Search code examples
c#structvb6marshallingtapi

TAPI lineGetCallInfo returning empty results


I'm attempting to build a custom solution to interact with our IP Office phone system. I was able to find and update an old VB6 TAPI project provided by our phone provider Avaya which utilizes the TAPI32.dll driver. However the change from VB6 to .net is giving me a few problems. The biggest of which is that the old code is passing a Struct to the pinvoke functions on the dll. The function, struct, and calling code look like this (sorry for the length):

Dim l_Call As Long                          
Dim struct_MyCallInfo As lineCallInfo  
Dim l_lineGetCallInfo_Result As Long    

' Init Parameters..
l_Call = RMSTAPIRoutines.glhCall

' Set Memory needed...
struct_MyCallInfo.l_dwTotalSize = LINECALLINFO_FIXEDSIZE + LINECALLINFO_MEMSIZE

' Run lineGetCallInfo..
l_lineGetCallInfo_Result = RMSTAPIDeclarations.lineGetCallInfo(l_Call, struct_MyCallInfo)



Global Const LINECALLINFO_MEMSIZE = DEFAULT_SIZE

Type lineCallInfo
l_dwTotalSize As Long
l_dwNeededSize As Long
l_dwUsedSize As Long

l_hLine As Long
l_dwLineDeviceID As Long
l_dwAddressID As Long

l_dwBearerMode As Long
l_dwRate As Long
l_dwMediaMode As Long

l_dwAppSpecific As Long
l_dwCallID As Long
l_dwRelatedCallID As Long
l_dwCallParamFlags As Long
l_dwCallStates As Long

l_dwMonitorDigitModes As Long
l_dwMonitorMediaModes As Long
struct_DialParams As lineDialParams

l_dwOrigin As Long
l_dwReason As Long
l_dwCompletionID As Long
l_dwNumOwners As Long
l_dwNumMonitors As Long

l_dwCountryCode As Long
l_dwTrunk As Long

l_dwCallerIDFlags As Long
l_dwCallerIDSize As Long
l_dwCallerIDOffset As Long
l_dwCallerIDNameSize As Long
l_dwCallerIDNameOffset As Long

l_dwCalledIDFlags As Long
l_dwCalledIDSize As Long
l_dwCalledIDOffset As Long
l_dwCalledIDNameSize As Long
l_dwCalledIDNameOffset As Long

l_dwConnectedIDFlags As Long
l_dwConnectedIDSize As Long
l_dwConnectedIDOffset As Long
l_dwConnectedIDNameSize As Long
l_dwConnectedIDNameOffset As Long

l_dwRedirectionIDFlags As Long
l_dwRedirectionIDSize As Long
l_dwRedirectionIDOffset As Long
l_dwRedirectionIDNameSize As Long
l_dwRedirectionIDNameOffset As Long

l_dwRedirectingIDFlags As Long
l_dwRedirectingIDSize As Long
l_dwRedirectingIDOffset As Long
l_dwRedirectingIDNameSize As Long
l_dwRedirectingIDNameOffset As Long

l_dwAppNameSize As Long
l_dwAppNameOffset As Long

l_dwDisplayableAddressSize As Long
l_dwDisplayableAddressOffset As Long

l_dwCalledPartySize As Long
l_dwCalledPartyOffset As Long

l_dwCommentSize As Long
l_dwCommentOffset As Long

l_dwDisplaySize As Long
l_dwDisplayOffset As Long

l_dwUserUserInfoSize As Long
l_dwUserUserInfoOffset As Long

l_dwHighLevelCompSize As Long
l_dwHighLevelCompOffset As Long

l_dwLowLevelCompSize As Long
l_dwLowLevelCompOffset As Long

l_dwChargingInfoSize As Long
l_dwChargingInfoOffset As Long

l_dwTerminalModesSize As Long
l_dwTerminalModesOffset As Long

l_dwDevSpecificSize As Long
l_dwDevSpecificOffset As Long

l_dwCallTreatment As Long

l_dwCallDataSize As Long
l_dwCallDataOffset As Long

l_dwSendingFlowspecSize As Long
l_dwSendingFlowspecOffset As Long

l_dwReceivingFlowspecSize As Long
l_dwReceivingFlowspecOffset As Long

' >= TAPI 3.0...  BEGIN
l_dwCallerIDAddressType As Long
l_dwCalledIDAddressType As Long
l_dwConnectedIDAddressType As Long
l_dwRedirectionIDAddressType As Long
l_dwRedirectingIDAddressType As Long
' >= TAPI 3.0...  END

mem As String * LINECALLINFO_MEMSIZE
End Type

Global Const LINECALLINFO_FIXEDSIZE = 344



Declare Function lineGetCallInfo Lib "TAPI32.DLL" _
(ByVal l_hCall As Long, ptr_lpCallInfo As Any) As Long

C# won't let me do this (various claims on accessing protected memory). After some research it seems like everyone agrees you need to pass an IntPtr to the unmanaged function in place of the Struct itself. From the examples I have come up with this code

struct_MyCallInfo.l_dwTotalSize = RMSTAPIDeclarations.LINECALLINFO_FIXEDSIZE + RMSTAPIDeclarations.LINECALLINFO_MEMSIZE;

System.IntPtr sPTR = Marshal.AllocHGlobal(Marshal.SizeOf(struct_MyCallInfo));
Marshal.StructureToPtr(struct_MyCallInfo, sPTR, true);

int l_lineGetCallInfo_Result = RMSTAPIEvents.lineGetCallInfo(l_Call, sPTR);

to call this function

[DllImport("TAPI32.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    extern public static int lineGetCallInfo(int l_hCall, System.IntPtr ptr_lpCallInfo);

With this struct

  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct lineCallInfo
    {
        public int l_dwTotalSize;
        public int l_dwNeededSize;
        public int l_dwUsedSize;

        public int l_hLine;
        public int l_dwLineDeviceID;
        public int l_dwAddressID;

        public int l_dwBearerMode;
        public int l_dwRate;
        public int l_dwMediaMode;

        public int l_dwAppSpecific;
        public int l_dwCallID;
        public int l_dwRelatedCallID;
        public int l_dwCallParamFlags;
        public int l_dwCallStates;

        public int l_dwMonitorDigitModes;
        public int l_dwMonitorMediaModes;
        public lineDialParams struct_DialParams;

        public int l_dwOrigin;
        public int l_dwReason;
        public int l_dwCompletionID;
        public int l_dwNumOwners;
        public int l_dwNumMonitors;

        public int l_dwCountryCode;
        public int l_dwTrunk;

        public int l_dwCallerIDFlags;
        public int l_dwCallerIDSize;
        public int l_dwCallerIDOffset;
        public int l_dwCallerIDNameSize;
        public int l_dwCallerIDNameOffset;

        public int l_dwCalledIDFlags;
        public int l_dwCalledIDSize;
        public int l_dwCalledIDOffset;
        public int l_dwCalledIDNameSize;
        public int l_dwCalledIDNameOffset;

        public int l_dwConnectedIDFlags;
        public int l_dwConnectedIDSize;
        public int l_dwConnectedIDOffset;
        public int l_dwConnectedIDNameSize;
        public int l_dwConnectedIDNameOffset;

        public int l_dwRedirectionIDFlags;
        public int l_dwRedirectionIDSize;
        public int l_dwRedirectionIDOffset;
        public int l_dwRedirectionIDNameSize;
        public int l_dwRedirectionIDNameOffset;

        public int l_dwRedirectingIDFlags;
        public int l_dwRedirectingIDSize;
        public int l_dwRedirectingIDOffset;
        public int l_dwRedirectingIDNameSize;
        public int l_dwRedirectingIDNameOffset;

        public int l_dwAppNameSize;
        public int l_dwAppNameOffset;

        public int l_dwDisplayableAddressSize;
        public int l_dwDisplayableAddressOffset;

        public int l_dwCalledPartySize;
        public int l_dwCalledPartyOffset;

        public int l_dwCommentSize;
        public int l_dwCommentOffset;

        public int l_dwDisplaySize;
        public int l_dwDisplayOffset;

        public int l_dwUserUserInfoSize;
        public int l_dwUserUserInfoOffset;

        public int l_dwHighLevelCompSize;
        public int l_dwHighLevelCompOffset;

        public int l_dwLowLevelCompSize;
        public int l_dwLowLevelCompOffset;

        public int l_dwChargingInfoSize;
        public int l_dwChargingInfoOffset;

        public int l_dwTerminalModesSize;
        public int l_dwTerminalModesOffset;

        public int l_dwDevSpecificSize;
        public int l_dwDevSpecificOffset;

        public int l_dwCallTreatment;

        public int l_dwCallDataSize;
        public int l_dwCallDataOffset;

        public int l_dwSendingFlowspecSize;
        public int l_dwSendingFlowspecOffset;

        public int l_dwReceivingFlowspecSize;
        public int l_dwReceivingFlowspecOffset;

        // >= TAPI 3.0...  BEGIN
        public int l_dwCallerIDAddressType;
        public int l_dwCalledIDAddressType;
        public int l_dwConnectedIDAddressType;
        public int l_dwRedirectionIDAddressType;
        public int l_dwRedirectingIDAddressType;
        // >= TAPI 3.0...  END

        [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 2048)]//LINEDEVSTATUS_MEMSIZE)]
        public char[] _mem;
        public string mem
        {
            get
            {
                return new string(_mem);
            }
            set
            {
                CopyValueToArray(_mem, value);
            }
        }

        public static lineCallInfo CreateInstance()
        {
            lineCallInfo result = new lineCallInfo();
            result._mem = new char[2048];
            return result;
        }
    }

This works... sort of. It returns a result without throwing an error but the populated structure it returns is entirely empty. It should be returning values indicating the line type, media modes, caller info, etc. about the call but instead I just get all 0's.

Does anyone have any experience with managed interaction with TAPI? Is there something I am doing wrong with the Struct or the Marshaling process? Almost all of my other function calls work, including some where I do the same marshaling as I do below. That leads me to believe it may be something to do with the struct.


Solution

  • Marshaling TAPI structures to/from managed code is very tricky. TAPI puts variable length data in memory after the structure and you have to use xxxOffset and xxxSize members of the structure to find out where the data is.

    I would strongly recommend using a TAPI library for .NET that does all those tricky bits for you. You have couple of options: commercial AddTapi.NET or open-source Julmar TAPI.