对PInvoke函数的调用使堆栈失衡

时间:2013-09-29 20:27:57

标签: c# pinvoke

对.NET 4进行本机代码的函数调用会导致以下异常:

  

对PInvoke函数的调用使堆栈失衡。这很可能是因为托管PInvoke签名与非托管目标签名不匹配。检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。

以下是我在Native Code中的定义:

typedef void ( CALLBACK* CB_DOWNLOADING )
(
      ULONG,    // secs elapsed
      LPARAM,   // custom callback param
      LPBOOL    // abort?
);


FETCH_API HttpFetchW
(
      LPCWSTR         url,
      IStream**       retval,
      LPCWSTR         usrname  = NULL,
      LPCWSTR         pwd      = NULL,
      BOOL            unzip    = TRUE,
      CB_DOWNLOADING  cb       = NULL,
      LPARAM          cb_param = 0,
      LPWSTR          ctype    = NULL,
      ULONG           ctypelen = 0
);

以下是.NET版本:

[DllImport(dllPath, CharSet = CharSet.Unicode, SetLastError = true, CallingConvention =     CallingConvention.Cdecl)]
        internal static extern FETCH HttpFetchW
        (
            [MarshalAs(UnmanagedType.LPWStr)]
            string url,
            out System.Runtime.InteropServices.ComTypes.IStream retval,
            [MarshalAs(UnmanagedType.LPWStr)] 
            string usrname,
            [MarshalAs(UnmanagedType.LPWStr)] 
            string pwd,
            bool unzip,
            dlgDownloadingCB cb,
            IntPtr cb_param,
            [MarshalAs(UnmanagedType.LPWStr)] 
            string ctype,
            ulong ctypelen
        );

其中FETCH是枚举。

[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
    internal delegate void dlgDownloadingCB(ulong elapsedSec, IntPtr lParam, bool abort);


internal static void DownloadingCB(ulong elapsedSec, IntPtr lParam, bool abort)
        {
           // Console.WriteLine("elapsedSec = " + elapsedSec.ToString());
        }

有人可以在.NET 4中建议替代方案吗?

非常感谢你的帮助。

2 个答案:

答案 0 :(得分:4)

我可以看到HttpFetchW中唯一明确的错误是C#ulong是64位宽,C ++ ULONG是32位宽。最终参数的C#代码应为uint

要检查的其他事项包括调用约定。你确定它是cdecl吗?您确定SetLastError应该是true吗?

对于回调,您使用ulong犯了同样的错误。 abort参数为LPBOOL。这是指向BOOL的指针。因此,您应该将C#参数声明为ref参数。并且回调是使用CALLBACK stdcall声明的。

所以代表应该是:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate void dlgDownloadingCB(
    uint elapsedSec, 
    IntPtr lParam, 
    ref bool abort
);

或者只是删除UnmanagedFunctionPointer属性,因为默认值为stdcall

我认为极有可能因为回调是stdcall,所以函数也是如此。我希望FETCH_API宏扩展到返回枚举和调用约定。既然你没有提供这些细节,我只能猜测。你需要检查一下。

我们无法检查的另一件事是CB_DOWNLOADING

无论如何,根据这些假设,函数变为:

[DllImport(dllPath, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern FETCH HttpFetchW(
    string url,
    out System.Runtime.InteropServices.ComTypes.IStream retval,
    string usrname,
    string pwd,
    bool unzip,
    dlgDownloadingCB cb,
    IntPtr cb_param,
    string ctype,
    uint ctypelen
);

答案 1 :(得分:0)

来源:MSDN

  

在.NET Framework 3.5版中,默认情况下禁用pInvokeStackImbalance MDA。在Visual Studio 2005中使用.NET Framework 3.5版时,pInvokeStackImbalance MDA将显示在“例外”对话框的“托管调试助手”列表中(单击“调试”菜单上的“例外”时会显示该列表)。但是,选择或清除pInvokeStackImbalance的Thrown复选框不会启用或禁用MDA;它仅控制在激活MDA时Visual Studio是否抛出异常。

https://msdn.microsoft.com/en-us/library/0htdy0k3(v=vs.110).aspx