我尝试构建自定义解决方案以与我们的IP Office电话系统进行交互。我能够找到并更新由我们的电话提供商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;
}
}
这有点......它返回结果而不抛出错误,但它返回的填充结构完全为空。它应该是返回值,表示有关呼叫的线路类型,媒体模式,呼叫者信息等,但我只是得到所有0。
有没有人有与TAPI进行托管互动的经验?我对Struct或Marshaling流程有什么问题吗?几乎所有我的其他函数调用都有效,包括我在下面执行相同编组操作的一些函数。这让我相信它可能与结构有关。
答案 0 :(得分:0)
对托管代码进行封送TAPI结构非常棘手。 TAPI在结构之后将可变长度数据放入内存中,您必须使用结构的xxxOffset和xxxSize成员来查找数据的位置。
我强烈建议使用适用于.NET的TAPI库,为您完成所有这些棘手的操作。您有两种选择:商业AddTapi.NET或开源Julmar TAPI。