C# – A call to PInvoke function has unbalanced the stack

cpinvoke

Making a function call to .NET 4 to native code is resulting in the following exception:

A call to PInvoke function 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.

Here is my definition in 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
);

Here is the .NET counterpart:

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

Where FETCH is an enum.

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

Can anyone suggest an alternative within the .NET 4?

Thank you very much for your help.

Best Answer

The only clear error in HttpFetchW that I can see is that C# ulong is 64 bits wide, by C++ ULONG is 32 bits wide. The C# code for the final parameter should be uint.

Other things to check include the calling convention. Are you sure it is cdecl? Are you sure that SetLastError should be true?

As for the callback, you make the same error with ulong. And the abort parameter is LPBOOL. That is a pointer to BOOL. So you should declare the C# parameter as a ref parameter. And the callback is declared with CALLBACK which is stdcall.

So the delegate should be:

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

Or simply delete the UnmanagedFunctionPointer attribute since the default is stdcall.

I think it exceptionally likely that since the callback is stdcall, so will the function. I expect that the FETCH_API macro expands to both the return enum and the calling convention. Since you did not supply those details, I can only guess. You'll need to check.

The other thing that we cannot check is CB_DOWNLOADING.

Anyway, with those assumptions, the function becomes:

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