【问题标题】:Pinvoke error stack unbalanced - lzo decompression C function [duplicate]Pinvoke 错误堆栈不平衡 - lzo 解压 C 函数 [重复]
【发布时间】:2013-06-19 12:06:12
【问题描述】:

我知道这个问题已经被问过好几次了,如下: (Unbalanced Stack!)

但我使用的是开源 DLL,LZO 2.0.3,用 ANSI C 编写。有关 DLL 的信息在这里: LZO download the source

我的 C# 程序是一个下载器,它与服务器建立 TCP 套接字,通过 TCP 连接发送 LZO 压缩数据包。

.NET中有几个LZO的端口,比如:

http://lzohelper.codeplex.com/

http://wallaceturner.com/lzo-for-c

http://lzo-net.sourceforge.net/[过时]

http://www.codeproject.com/Articles/16239/Pure-C-MiniLZO-port

http://powerawarebt2.googlecode.com/svn/trunk/PowerAwareBT/CompactFramework/ADOHelper/SRC_Helper/MiniLZO.cs

与 .NET 中的多个 LZO 和 miniLZO 端口不同,它们具有自己的解压缩函数,根据数据包的最后 4 位建立目标缓冲区的长度,我的数据包包含未压缩的 8 字节标头,如下所示:

Header:
  4 bytes - number of packets [bytes] in the incoming packet
  4 bytes - length of decompressed data packet

Data:
  X bytes - what needs to be decompressed 

这是我的代码的 sn-p:

Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
client.Connect(remoteEP);
while (true)
//infinite loop since the client keeps receiving data 
{
    m = client.Receive(inStream);
    //instream is previously defined as a byte of 100000
    //this is followed by code copying the m bytes received from inStream
    //to a new byte original
    short iNoOfPackets = 0;
    short myPacketLength = 0;
    //this is followed by code to extract the two variables above
    //from the first 8 bytes of original (or inStream)
    if (m > 8)
    {
        compressedData = new byte[m - 8];
        Array.Copy(original, 8, compressedData, 0, compressedData.Length);
    }
    byte[] destination = new byte[myPacketLength];
    try
    {
        int destlen = (int)myPacketLength;
        r = lzo1x_decompress_safe(compressedData, (int)compressedData.Length, destination, ref destlen, null);
    }
    catch (Exception ex)
    ....

使用PInvoke的函数调用如下:

[DllImport(LzoDLL64bit)]
private static extern int lzo1x_decompress_safe(byte[] src, int src_len, byte[] dst, ref int dst_len, byte[] wrkmem);

我在 64 位 Windows Server 2008 机器上使用 Visual Studio 2012 Express。

正如这篇文章的标题所暗示的,我收到的错误是:

    Additional Information: A call to PInvoke function 'My.Program::lzo1x_decompress_safe' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

调试控制台产生以下输出:

    4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
    'MyProgra.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
    A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
    The program '[6852] Myprogram.vshost.exe: Managed (v4.0.30319)' has exited with code -1073741510 (0xc000013a).

如果您查看 LZO 2.0.3 库的源代码 - 特别是文件 lzo1x.h,它会说:

/* safe decompression with overrun testing */
LZO_EXTERN(int)
lzo1x_decompress_safe   ( const lzo_bytep src, lzo_uint  src_len,
                            lzo_bytep dst, lzo_uintp dst_len,
                            lzo_voidp wrkmem /* NOT USED */ );

我的问题很简单——我该如何解决这个错误?作为一个对 C 知之甚少的新手 C# 程序员,我不熟悉 PInvoke,并且非常感谢您可能提出的任何具体建议。对于可能重复的问题/场景,请提前道歉。

【问题讨论】:

  • 下面的签名不同 - src_len 是 uint 不是 int,dst_len 是 ref uint 不是 int,这并不重要,但最后一个参数 wrkmem 有注释 /* NOT USED / this是 void 指针,这可以是不安全的 c# 指针中的对象或 void,或 IntPtr

标签: c# .net pinvoke dllimport lzo


【解决方案1】:

默认的 CallingConvention 是 StdCall,但是 LZO 似乎使用 Cdecl,因此您需要将其添加到 DllImport 声明中。以前版本的 .NET 运行时可能已经静默修复了该问题,但在 .NET 4.0 及更高版本中,当指定了错误的调用约定时会发生这种情况。

http://msdn.microsoft.com/en-us/library/ee941656%28v=VS.100%29.aspx#core

为了提高与非托管代码的互操作性性能,平台调用中不正确的调用约定现在会导致应用程序失败。在以前的版本中,封送层在堆栈中解决了这些错误。

此外,最后一个参数是 void*,但由于未使用,您可以将类型指定为 IntPtr 并传递 IntPtr.Zero,它是 NULL 的 C/C++ 等效项。

[DllImport(LzoDLL64bit, CallingConvention = CallingConvention.Cdecl)] 
private static extern int lzo1x_decompress_safe(byte[] src, uint src_len, byte[] dst, ref uint dst_len, IntPtr wrkmem);

此外,您似乎编组了 uint -> int。这可能是为了避免稍后进行强制转换,但这可能会导致可能的负值。

【讨论】:

  • 谢谢,这太完美了!不仅错误得到了解决,而且您还省去了我发布另一个关于不断导致程序崩溃的负值(src_len / dest_len)的问题的麻烦。一旦服务器在几个小时内恢复在线,我将重新测试。
猜你喜欢
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多