【问题标题】:.Net 4 causes DLLImport function to return failure code, .Net 2, 3 or 3.5 does not.Net 4 导致 DLLImport 函数返回失败代码,.Net 2、3 或 3.5 不会
【发布时间】:2012-04-20 21:47:54
【问题描述】:

我正在查看来自 sourceforge 的示例项目,用于链接到 TSAPI(不是 TAPI)电话系统 - http://tsapi.sourceforge.net/

我的开发环境是32位Windows XP,项目设置为x86目标。

这可以正常运行,因为它可以针对 .Net 2 运行,但我需要针对 .Net 4 运行。当我更改框架并运行第一个函数时,返回 -1 表示失败。

函数定义为:

[DllImport("csta32.dll")]
public static extern int acsOpenStream(ref UInt32 acsHandle, int invokeIDType, UInt32 invokeID, int streamType, char[] serverID, char[] loginID, char[] passwd, char[] applicationName, int acsLevelReq, char[] apiVer, ushort sendQSize, ushort sendExtraBufs, ushort recvQSize, ushort recvExtraBufs, ref PrivateData_t priv);

调用该函数的c#代码(摘自示例项目)为:

    // The public method to open the ACS stream
    public bool open(string strLoginId, string strPasswd, string strServerId)
    {
        // Convert the parameters to character arrays
        char[] serverId = strServerId.ToCharArray();
        char[] loginId = strLoginId.ToCharArray();
        char[] passwd = strPasswd.ToCharArray();

        // Define the initial set of variables used for opening the ACS Stream
        int invokeIdType = 1;
        UInt32 invokeId = 0;
        int streamType = 1;
        char[] appName = "Mojo".ToCharArray();
        int acsLevelReq = 1;
        char[] apiVer = "TS1-2".ToCharArray();
        ushort sendQSize = 0;
        ushort sendExtraBufs = 0;
        ushort recvQSize = 0;
        ushort recvExtraBufs = 0;

        // Define the mandatory (but unused) private data structure
        Csta.PrivateData_t privData = new Csta.PrivateData_t();
        privData.vendor = "MERLIN                          ".ToCharArray();
        privData.length = 4;
        privData.data = "N".ToCharArray();

        // Define the event buffer pointer that gets data back from the TServer
        ushort numEvents = 0;
        Csta.EventBuf_t eventBuf = new Csta.EventBuf_t();
        ushort eventBufSize = (ushort)Csta.CSTA_MAX_HEAP;

        // Open the ACS stream
        try
        {
            int openStream = Csta.acsOpenStream(ref acsHandle, invokeIdType, invokeId, streamType, serverId, loginId, passwd, appName, acsLevelReq, apiVer, sendQSize, sendExtraBufs, recvQSize, recvExtraBufs, ref privData);

还提供了一个 C++ 示例应用程序,其中函数调用为:

m_nRetCode = acsOpenStream(&m_lAcsHandle            // Handle for ACS Stream 
    , APP_GEN_ID            // APP_GEN_ID indicates Application   generated invokeID's
    , (InvokeID_t)m_ulInvokeID  // By default 1
    , ST_CSTA
    , (ServerID_t *)(serverID)  // AE Server Name
    , (LoginID_t *)(loginID)    // CTI LoginID
    , (Passwd_t *)(password)  // CTI LoginID password
    , (AppName_t *)"TSAPI_AgentView"
    , ACS_LEVEL1
    , (Version_t *) "TS1-2" // private Data version in use 8 in our case
    , 10
    , 5 
    , 50
    , 5
    , (PrivateData_t *)&m_stPrivateData); // private Data.

如果我将 DLLImport 更改为

[DllImport("csta32.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int acsOpenStream(ref UInt32 acsHandle, int invokeIDType, UInt32 invokeID, int streamType, char[] serverID, char[] loginID, char[] passwd, char[] applicationName, int acsLevelReq, char[] apiVer, ushort sendQSize, ushort sendExtraBufs, ushort recvQSize, ushort recvExtraBufs, ref PrivateData_t priv);

我收到运行时错误

检测到 PInvokeStackImbalance 消息:对 PInvoke 函数 'Mojo!Csta::acsOpenStream' 的调用使堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。

Avaya 不提供头文件 - 文档表明函数定义为:

RetCode_t acsOpenStream(
ACSHandle_t *acsHandle, /* RETURN */
InvokeIDType_t invokeIDType, /* INPUT */
InvokeID_t invokeID, /* INPUT */
StreamType_t streamType, /* INPUT */
ServerID_t *serverID, /* INPUT */
LoginID_t *loginID, /* INPUT */
Passwd_t *passwd, /* INPUT */
AppName_t *applicationName, /* INPUT */
Level_t acsLevelReq /* INPUT */
Version_t *apiVer, /* INPUT */
unsigned short sendQSize, /* INPUT */
unsigned short sendExtraBufs, /* INPUT */
unsigned short recvQSize, /* INPUT */
unsigned short recvExtraBufs /* INPUT */
PrivateData_t *privateData); /* INPUT */

【问题讨论】:

  • char[] 看起来不对。肯定应该是字符串。无论如何,您需要显示 C++ 声明和 C# 调用代码。
  • 第 23 个参数的值错误。
  • 您仍然没有显示函数的 C++ 声明。无论如何, ToCharArray 不会产生以空字符结尾的字符串。您需要使用字符串而不是 char[]。并正确设置 CharSet。你的代码总是错的,只是在我猜到之前你很幸运。
  • 为什么不能显示 C++ 声明?没有那个我们就无能为力了,我正在失去兴趣。如果没有可靠的信息,您将继续得到虚假的答案,导致您大打出手。
  • 有一个头文件。你还没有找到。 C++ 示例使用它。我只能猜测字符串参数真的是char*。在这种情况下,您需要在 C# 中使用字符串而不是 char[]。你明白其中的区别吗?这个问题现在真是一团糟。你可能需要重新开始。试着很好地掌握函数在 C++ 中的定义,并去掉问题的虚假部分。

标签: c# .net dllimport


【解决方案1】:

您声明为char[] 的字符串参数总是错误的。我猜你在早期的 .net 版本中碰巧侥幸逃脱了。

C++ 代码将这些参数声明为char*,即指向空终止字符数组的指针。在 C# 代码中声明这些参数的正确方法是 string。 p/invoke marshaller 将负责将您的 C# 字符串转换为 C 字符串,您可以删除所有虚假的 ToCharArray() 代码。

【讨论】:

    【解决方案2】:

    考虑到您说的是 4.0,我假设,也很难从提供的问题中推断出,您在 4.0 下运行的应用程序也在 64 位操作系统下运行。

    如果,那么,失败是绝对合理的,因为你的目标是csta32.dll DLL 和 使用类似参数UInt32

    提出解决此问题的解决方案的第一步是

    • 选择正确的库来导入
    • Uint32 替换为uint,因此该类型的正确维度将由运行时选择。

    但这些自然是尝试,应该尊重必须声明导入的正确方法的正确签名。

    如果这不是您要求的,或者不是您的问题,请澄清。

    【讨论】:

    • uint 和 UInt32 在所有平台上都是 32 位宽。
    • 我在 32 位 XP 上运行,平台目标是 x86。如前所述,不寻常的部分(对我而言)是它在您更改目标框架之前一直有效,这表明您应该在 3.5 和 4.0 之间使用 DLLImport 的方式发生了一些我不知道的变化。
    • 如果您突然切换到 x64,那么 DLL 将无法加载。
    • 但是我没有改成x64?
    • @Rob 评论不好!您需要编辑问题以添加这些内容。
    【解决方案3】:

    答案是将char[]修改为字符串。然后这适用于 .Net 4.0 - 如果保留为 char [] 它返回 -2 ( ACSERR_BADPARAMETER )。

    因此,2.0 必须处理此问题,因为 DLLImport 不需要此更改即可在该版本的框架上工作。

    【讨论】:

      猜你喜欢
      • 2012-11-17
      • 2012-03-22
      • 2022-01-24
      • 1970-01-01
      • 2011-11-26
      • 2010-10-07
      • 1970-01-01
      • 1970-01-01
      • 2013-05-27
      相关资源
      最近更新 更多