Pregunta

I know this question has been asked several times, as follows: (Unbalanced Stack!)

But I'm using an open source DLL, LZO 2.0.3, written in ANSI C. Information on the DLL is here: LZO download the source

My C# program is a downloader that establishes a TCP socket with a server that sends LZO compressed packets across the TCP connection.

There are several ports of LZO in .NET, such as:

http://lzohelper.codeplex.com/

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

http://lzo-net.sourceforge.net/ [outdated]

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

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

Unlike the multiple LZO and miniLZO ports in .NET, which have their own decompress functions that establish the length of the destination buffer based on the last 4 digits of the packet, my packets contain an uncompressed 8 byte header, as follows:

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 

Here's a snippet of my code:

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

the function call using PInvoke is as follows:

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

I'm using Visual Studio 2012 Express on a 64 bit Windows Server 2008 machine.

The error I receive, as the title of this post suggests, is:

    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.

The debug console yields the following output:

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

If you look at the source code of the LZO 2.0.3 library - specifically the file lzo1x.h, it says:

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

My question is simple - how do I resolve the error? As a newbie C# programmer with little knowledge of C, I am unfamiliar with PInvoke, and would really appreciate any specific recommendation(s) you might have. Apologies in advance for a possibly duplicate question/scenario.

¿Fue útil?

Solución

The default CallingConvention is StdCall, however it seems that LZO uses Cdecl, so you need to add that into the DllImport declaration. Previous version of the .NET runtime may have silently fixed the issue but in .NET 4.0 and above, this happens when the wrong calling convention is specified.

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

To improve performance in interoperability with unmanaged code, incorrect calling conventions in a platform invoke now cause the application to fail. In previous versions, the marshaling layer resolved these errors up the stack.

Also, the last parameter is a void* but since it's unused, you can specify the type as IntPtr and pass IntPtr.Zero which is the C/C++ equivalent of NULL.

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

Also, it seems that you marshalled uint -> int. This may be to avoid casting later on but this may result in possible negative values.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top