调用PInvoke函数导致堆栈不平衡。

3
调用.NET 4中的本机代码函数导致以下异常:
调用PInvoke函数不平衡堆栈。这很可能是因为托管的PInvoke签名与非托管目标签名不匹配。请检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。
以下是我的本机代码定义:
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内提供替代方案?

非常感谢您的帮助。


1
我认为 LPBOOLref bool 匹配。 - H H
2个回答

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

0

来源:MSDN

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接