TAPI lineGetCallInfo 返回空结果

TAPI lineGetCallInfo returning empty results

我正在尝试构建自定义解决方案以与我们的 IP Office phone 系统进行交互。我能够找到并更新我们的 phone 提供商 Avaya 提供的旧 VB6 TAPI 项目,该项目使用 TAPI32.dll 驱动程序。然而,从 VB6 到 .net 的变化给我带来了一些问题。其中最大的是旧代码将 Struct 传递给 dll 上的 pinvoke 函数。函数、结构和调用代码如下所示(抱歉长度):

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# 不允许我这样做(关于访问受保护内存的各种声明)。经过一些研究,似乎每个人都同意您需要将 IntPtr 传递给非托管函数,而不是 Struct 本身。从我想出这个代码的例子

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);

调用这个函数

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

有了这个结构

  [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;
        }
    }

这行得通……有点。它 returns 结果没有抛出错误,但它 returns 填充的结构完全是空的。它应该返回指示线路类型、媒体模式、呼叫者信息等关于呼叫的值,但我只是得到全 0。

有人有过与 TAPI 进行托管交互的经验吗?我在结构或封送处理过程中做错了什么吗?几乎所有我的其他函数调用都有效,包括一些我在下面进行相同封送处理的地方。这让我相信它可能与结构有关。

编组 TAPI 结构 to/from 托管代码非常棘手。 TAPI在结构之后将可变长度数据放入内存中,您必须使用结构的xxxOffset和xxxSize成员才能找出数据所在的位置。

我强烈建议使用适用于 .NET 的 TAPI 库,它可以为您完成所有这些棘手的工作。您有两个选择:商业 AddTapi.NET or open-source Julmar TAPI.