Search code examples
c#.nettapiobjectdisposedexception

LineMakeCall in JulMar's ATAPI throws ObjectDisposedException


I'm currently using JulMar's ATAPI to interact with Microsoft's Telephony API (TAPI) 2.x.

Whenever I try to place a call onto a specified line or address, I get an ObjectDispoedException. I think this exception is thrown, becasue the LineMakeCall-Function never sets the HCALL handle.

DLLImport:

[DllImport("Tapi32.dll", EntryPoint = "lineMakeCallW", CharSet = CharSet.Auto)]
internal static extern int lineMakeCall(HTLINE hLine, out uint hCall, string DestAddress, int CountryCode, IntPtr lpCallParams);

Function call:

uint hCall = 0;
int rc = NativeMethods.lineMakeCall(Line.Handle, out hCall, address, countryCode, lpCp);

Now, the problem is when this method terminates, hCall is not set and I don't understand why. However, when I change the target framework to .NET 4 or higher (I'm running my application on .NET 3.5 by default), hCall will be set.

From what I understand, the out parameter has to be set before the function terminates.

I read up on the differences between .NET 3.5 and .NET 4 but I didn't find anything usefull for my case.

Does anbody have any idea why the out parameter isn't being set?

Edit:

I finally managed to get it working and want to share this solution. I basically did what was recommended by Kris Vanherck

Try changing your signature of out uint hCall into ref IntPtr lphCall

DLLImport:

[DllImport("Tapi32.dll", EntryPoint = "lineMakeCallW", CharSet = CharSet.Auto)]
internal static extern int lineMakeCall(HTLINE hLine, IntPtr hCall, string DestAddress, int CountryCode, IntPtr lpCallParams);

Function call:

    public TapiCall MakeCall(string address, int countryCode, MakeCallParams param)
    {
        if (!Line.IsOpen)
            throw new TapiException("Line is not open", NativeMethods.LINEERR_OPERATIONUNAVAIL);

        IntPtr lpCp = IntPtr.Zero;
        //jf 2016-10-07
        IntPtr lpHcall = IntPtr.Zero;
        //jf
        try
        {
            lpCp = MakeCallParams.ProcessCallParams(_addressId, param, 0);

            //jf 2016-10-16
            CallHandle callHandle = new CallHandle();
            lpHcall = Marshal.AllocHGlobal(Marshal.SizeOf(callHandle));
            Marshal.StructureToPtr(callHandle, lpHcall, true);
            //jf

            int rc = NativeMethods.lineMakeCall(Line.Handle, lpHcall, address, countryCode, lpCp);
            if (rc < 0)
                throw new TapiException("lineMakeCall failed", rc);
            else
            {
                // Wait for the LINE_REPLY so we don't need to deal with the value type 
                // issues of IntPtr being filled in async.
                var req = new PendingTapiRequest(rc, null, null);
                Line.TapiManager.AddAsyncRequest(req);
                req.AsyncWaitHandle.WaitOne();
                if (req.Result < 0)
                    throw new TapiException("lineMakeCall failed", req.Result);

                //jf 2016-10-07
                Marshal.PtrToStructure(lpHcall, callHandle);
                //jf
                var call = new TapiCall(this, callHandle.hCall);
                AddCall(call);
                return call;
            }
        }
        finally
        {
            //jf 2016-10-07
            if(lpHcall != IntPtr.Zero)
                Marshal.FreeHGlobal(lpHcall);
            //jf

            if (lpCp != IntPtr.Zero)
                Marshal.FreeHGlobal(lpCp);
        }
    }

And finally the CallHandle class:

[StructLayout(LayoutKind.Sequential)]
internal class CallHandle
{
    internal uint hCall;
}

Solution

  • If you look at the MSDN page for the function you can note this section:

    The handle is only valid after the LINE_REPLY message is received by the application indicating that the lineMakeCall function successfully completed.

    So it is not guaranteed to be set before the end of the function call. Also it is actually not a handle. You can see it in the naming: "lp" stands for long pointer and "h" for handle. 'lphCall' is a pointer to a memory location where the handle will be set when the LINE_REPLY comes back (which may be before, during or after the function returns).

    This is a timing issue, changing framework may simply have made some things slower/faster, giving you the different result.

    Try changing your signature of out uint hCall into ref IntPtr lphCall