Using 3CX Call Control API in a .NET application
UPDATE (May 2018): See my latest article for more information on how to manage calls programmatically on 3CX v15. The article below was written for 3CX 11 and some information may not be applicable for the latest version of 3CX.
In one of the projects at work I attempted to use the 3CX Call Control API (see this) from my .NET application and encountered unique challenges because the Call Control API is only available from a .NET application running on the same server as the 3CX machine. This means, even if the sample .NET application to demonstrate the API provided by 3CX is working well, it is of little usefulness if you want to expose the API to your custom application which undoubtedly must be running from the client machine and not on the same server.
Issues with 3cxpscomcpp2.dll when called from ASP.NET SOAP web service
My design is to write an API wrapper that runs on the 3CX server, receives client requests via HTTP, interacts with the 3CX Call Control API to perform the necessary actions and returns the API response back to the client also via HTTP.
With this design, the first attempt is to use an ASP.NET SOAP web service, which unfortunately does not work. First, the web service fails to start due to a BadImageFormatException once the API DLL 3cxpscomcpp2.dll, is added as a reference to the project
System.BadImageFormatException: Could not load file or assembly ‘3cxpscomcpp2.dll’ or one of its dependencies. An attempt was made to load a program with an incorrect format
Knowing that this is because of the format of the DLL (32-bit vs. 64-bit) and the architecture of the project (x86 or x64), I tried to changed the project platform but neither x86 or x64 works. I also changed the application pool settings inside IIS following this article, which also does not help.
Next I noticed that the API DLL is a 64-bit DLL and attempted to install 3CX on a 32-bit machine to retrieve the 32-bit version of the DLL. This time, the error message when loading the web service changed:
The error message is not very useful and several Google searches did not return any working solutions. This has to do with the fact that the 3cxpscomcpp2.dll uses a native C++ dll named sl.dll. For the API to initialize properly, both DLLs are required. Despite substantial research, I could not find any reasons why sl.dll fails to be loaded and thus giving up integrating the DLL with ASP.NET web service.
- The browser must allow cross domain calls. For Chrome, you can launch chrome with the parameters –disable-web-security and cross domain calls will be allowed.
- The server response must contain the following header to allow calls from any origin:
Access-Control-Allow-Origin: *
- The server sends the response in JSONP
Because (1) is out of the question since the calling web site must be able to support different browsers while (3) is not possible yet from WCF, I have chosen (2). Luckily I found the following MSDN blog which proposes a solution that does not require any code changes:
- Import the WebHttpCors DLL provided by the author
- Modified app.config to add the tag to allow cross-domain calls.
It works well and the API can be called from jQuery with no issues. However, during the project, I also noticed several limitations with the 3CX Call Control API:
- There is no API to put a call on hold. The closest you can get is to transfer the call to a parked extension. To unhold the call, make a call to the parked number and you will be able to continue with the parked call.
- There is no API to retrieve the call history and the application needs to manually parse the 3CX Call History log files located at C:ProgramData3CXDataLogsCallHistory or read from the 3CX internal PostgreSQL database. Refer to this article for more details. In this aspect, the approach of using a WCF application instead of ASP.NET poses a major advantage because as a Windows application, WCF has no difficulties accessing files located on different paths on the server. This is also needed to read the call recording files located at C:ProgramData3CXDataRecordings
How do I contact you… This code is very interesting as part of a project i'm working on.
Hi Biztactix, you can just submit your query here. I will try to answer where I can.
How did you run WCF service? When I add 3cxpscomcpp2.dll and start debuging in visual studio I see message System.BadImageFormatException: Could not load file or assembly '3cxpscomcpp2.dll'… regardless x32, x86, 32-bit application pool?
Hi,
You will need to host your WCF service in a console app, or as a Windows service. Hosting it under IIS and you'll have the BadImageFormatException issue – same as when running as ASP.NET Web Service.
Secondly, once you've hosted as a console app or Windows service, check your build target to make sure your app is targeting x86/x64 accordingly, and not Any CPU. The 3cxpscomcpp2.dll file would need to be taken from a 3CX version that is installed for the specified architecture (x86 or x64 version), otherwise you will also have the exception.
Thirdly, remember to keep sl.dll in the same directly as the application executable, as it will try to load this DLL at runtime.
Hope this helps
Excellent, it works. Thank you.
I have same problem.
Please help me to solve this issue.
I need 3cxpscomcpp2.dll file for 64 bit version.
Could you say me how can I get this dll?
Hi, you will need to install 3CX server on a 64-bit version of Windows, then take the 3cxpscomcpp2.dll from the 3CX installation directory – it will be the 64-bit version. The installer copies either the 32-bit or 64-bit version of this DLL depending on your Windows version.
thanks!
Hi ,
I have a problem related to dropcall sample , so
Could you say me that where I can get parameters(callerId and participiant) for calling dropcall sample ?
Please help me .
Hi,
For DropCall method, callID is the ID of the call that you want to terminate. You can retrieve this ID from the list of active calls returned by the GetActiveConnections() method. participant is the number of the party which you want to be removed from the call – just pass the extension number that is currently involved in the call. The call will be disconnected after the DropCall method if there are fewer than two active parties.
Let me know if you have any other questions.
Thanks for quick response!
We can proceed to a call with MakeCall, but we are not able to find out the CallID from the database while the call is active.
After few minutes when the call is finished we can find this information it db table : callhistory3 and call details.
Maybe there is another table where can get this while the call is active. Thanks in advance!
Hi there,
Active calls will not be in the database- you need to get its ID from the Call Control API by using GetActiveConnections() as mentioned. The following code will help you to retrieve the callID for all active calls:
foreach (Tenant t in PhoneSystem.Root.GetTenants())
{
DN[] dnArr = t.GetDN();
foreach (DN dn in dnArr)
{
foreach (ActiveConnection con in dn.GetActiveConnections())
{
Debug.WriteLine(con.CallID);
}
}
}
With the above ID, pass it to DropCall and it will work.
Additionally the ID of the call that you saw in the DB tables is the history ID of the call – a long alphanumeric string that looks like 00000BD538E62E50_2539. The callID to be passed into DropCall should be a small integer that gets incremented each time a new call is placed on the PBX.
Thanks man , you saved my time
Hi
I have problem. How to get pressed number (DTMF tones) from mobile. Please save my life . I can’t find anything.
Hi all
I am working with version 14 of 3CX server so I might also be able to assist with comments and suggestions. I have a weird issue that I hope anybody can help me with.
In version 14, all incoming and outgoing calls are stored in the table called “cl_participants” in the database along with the call id. However, the call id in the database and the call id for an active call (using the call control API) does not match up. Each time the server is restarted, the call id counter restarts counting at 1 again. For example the last call id on the server is 1600. The next call id for a new call will be 1601? Now the server gets restarted. As soon as a new call is made, that new call is recorded in the database as call id 1601, but when I do a GetActiveConnection() using the API, it returns me a call id of 1. I wanted to use the call id to get the URL of any call recording from the database.
Does anyone know about this issue or have any suggestions on how to get past this issue?
Many many thanks!
Jaco
HI MD,
PLEASE UPDATE SAMPLE SOURCE CODE FOR 3CX API & WCF.