A call to PInvoke function has unbalanced the stack - c#

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.

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

Source : MSDN
In the .NET Framework version 3.5, the pInvokeStackImbalance MDA is disabled by default. When you use the .NET Framework version 3.5 with Visual Studio 2005, the pInvokeStackImbalance MDA will appear in the Managed Debugging Assistants list in the Exceptions dialog box (which is displayed when you click Exceptions on the Debug menu). However, selecting or clearing the Thrown check box for pInvokeStackImbalance does not enable or disable the MDA; it only controls whether Visual Studio throws an exception when the MDA is activated.
https://msdn.microsoft.com/en-us/library/0htdy0k3(v=vs.110).aspx

Related

Calling C++ dll Method from C#

I am trying to call method available in C++ dll
HRESULT WINAPI TestMethod(
_Out_ BOOL *isSuccess,
_In_opt_ DWORD UsernmaeLength,
_Out_opt_ LPWSTR userName );
Wrapper method Which I have written in C# looks like this
[DllImport("Test.dll", CharSet = CharSet.Unicode, SetLastError = true ,CallingConvention = CallingConvention.StdCall)]
public static extern int TestMethod (
IntPtr isSuccess,
[In, Optional] int UsernmaeLength,
out string userName
);
I am calling this method in program
Wrapper. TestMethod (isSuccess, 200, out userName);
I am getting System.AccessViolationException
tried changing the C# wrapper method with
[DllImport("Test.dll", CharSet = CharSet.Unicode, SetLastError = true ,CallingConvention = CallingConvention.StdCall)]
public static extern int TestMethod (
bool isSuccess,
[In, Optional] int UsernmaeLength,
out string userName
);
//Caller
bool isSuccess = false;
Wrapper. TestMethod (isSuccess, 200, out userName);
Could you please help me to understand what I am doing wrong here?
_In_opt_ DWORD UsernmaeLength
The SAL annotation is not very useful. What it probably is trying to tell you is that you can pass NULL for the string buffer argument. In which case what you pass for the buffer length doesn't matter. It is not actually [Optional], you'd consider simply passing 0 if you really don't want a string back.
The 3rd argument cannot be String or out since that is an immutable type and the function wants to write into the buffer you pass. It must be StringBuilder. The 2nd argument must be its Capacity. Be sure to make the StringBuilder big enough to fit a user name. If it is not then it isn't very obvious what will happen, hopefully the function then just returns an error code instead of silently truncating string. Test that.
The 1st argument is bool passed by reference, [Out] out bool. Not very likely that it SetLastError, that is only done by winapi functions. It already returns an error code embedded in the HResult. A value less than 0 is an error. Stdcall is the default. Summarizing:
[DllImport("Test.dll", CharSet = CharSet.Unicode)]
public static extern int TestMethod (
[Out] out bool isSuccess,
int userNameLength,
StringBuilder userName
);
Called as:
bool success;
var name = new StringBuilder(666);
int hr = TestMethod(out success, name.Capacity, name);
if (hr < 0) Marshal.ThrowExceptionForHR(hr);
If you still have trouble then you need the help of the author of this code if you cannot debug it yourself. Have a small repro available so he can easily repro the issue.

How to PInvoke CMark's cmark_markdown_to_html

I'm trying to PInvoke the following function from GitHub's fork of CMark
char *cmark_markdown_to_html(const char *text, size_t len, int options)
and here's my PInvoke signature:
[DllImport("cmark.dll")]
public static extern IntPtr cmark_markdown_to_html(
[In()] [MarshalAs(UnmanagedType.LPStr)] string text,
[MarshalAs(UnmanagedType.SysUInt)] uint len,
[MarshalAs(UnmanagedType.SysInt)] int options);
I call it as follows:
var retValue = cmark_markdown_to_html(markdown, 0, 0);
However, it throws a Marshaling exception the with the message:
Cannot marshal 'parameter #2':
Invalid managed/unmanaged type combination
(Int32/UInt32 must be paired with I4, U4, or Error).
OK, so I change the signature to:
[DllImport("cmark.dll")]
public static extern IntPtr cmark_markdown_to_html(
[In, MarshalAs(UnmanagedType.LPStr)] string text,
[MarshalAs(UnmanagedType.U4)] uint len,
[MarshalAs(UnmanagedType.I4)] int options);
Now it throws a PInvokeStackImbalance error
The name '$exception' does not exist in the current context
Native stuff is a mystery to me. Can someone help?
Stack imbalance
The reason for stack imbalance is described in Why are Cdecl calls often mismatched in the "standard" P/Invoke Convention?.
Basically you need to specify proper calling convention in the DllImport that will probably be cdecl:
[DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]
Marshalling size_t
That one is discussed in what is equal to the c++ size_t in c# .
Assuming that CMark will always be native (32 bit on x86, 64 bit on x64), you should use (U)IntPtr.
If it is always 32 bit, then Int32 should be used.
PInvokeStackImbalance
Presumably CMark is using the default calling convention. The default calling convention for C code is "Cdecl", while the default used for P/invoke calls is "StdCall". The calling convention specifies how the arguments and return value are placed on the call stack. A mismatch between the used calling convention causes the stack to become imbalanced.
You can specify the calling convention to use from C# code in the DllImport attribute.
[DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cmark_markdown_to_html(
[In, MarshalAs(UnmanagedType.LPStr)] string text,
[MarshalAs(UnmanagedType.U4)] uint len,
[MarshalAs(UnmanagedType.I4)] int options);
Marshalling
In case you want your code to be compatible with architectures other than 32-bit, you would want to marshall size_t with UIntPtr. (Read #Eugene Podskal answer)
[DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cmark_markdown_to_html(
[In, MarshalAs(UnmanagedType.LPStr)] string text,
[MarshalAs(UnmanagedType.U4)] UIntPtr len,
[MarshalAs(UnmanagedType.I4)] int options);
Then you can call the method like so
var retValue = cmark_markdown_to_html(markdown, UIntPtr.Zero, 0);

c# Calling method using P/Invoke causing AccessViolation Exception

I interfacing with a device using P/Invoke. But I've come stuck at calling the following method:
BOOL __stdcall voGetFirmwareVersion(IN OUT LPTSTR lpVersion, IN OUT DWORD *dwSize);
In my wrapper I have called the method like so:
[DllImport(DLL_LOCATION, CharSet = CharSet.Ansi)]
private static extern Boolean voGetFirmwareVersion(string s, uint d);
I have attempted to change the inputs to Out/Ref and tried adding the [In,Out] attributes but I am constantly getting a AccessViolation Exception. Can anyone point me in the right direction? I've been at this for a couple of hours now and google hasn't been able to put me straight
dwSize is passed by address, so it should be a ref parameter.
BOOL return type must be marshaled if any nonzero value may come as a true value
I am not sure if you really want to use Ansi charset. Try Auto or Unicode.
LPStr and StdCall are not a must, they are the default settings.
So try this:
[DllImport(DLL_LOCATION, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool voGetFirmwareVersion([MarshalAs(UnmanagedType.LPTStr)] string s, ref uint d);

Marshal void** c#

I try to use C dll in C#
typedef enum M_STATUS
{
// Unknown error. Should not be returned.
M_UNKNOWN = -1,
// Successful.
M_OK = 0
} M_STATUS;
M_STATUS WINAPI M_Create(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);
In C# I have
[DllImport("Y:\\libs\\Min.x86.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern M_STATUS M_Create(IntPtr pTarget, IntPtr pDetour, ref IntPtr ppOriginal);
But I always get PinvokeStackImbalance Exception
Where is mistake?
Adding as an actual answer so it won't get lost.
The C function is declared as WINAPI, but the calling convention specified in the DllImport attribute is Cdecl. The conventions must match, so change it to either StdCall or Winapi.

Memory access violation while passing void* param to DLL function

I'm adding new disk device to system from my C# code, so I want to call
[System.Runtime.InteropServices.DllImport("Shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public extern static void SHChangeNotify(long wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
like below
MyWin32Functions.SHChangeNotify(0x00000100/*ADDRIVE*/, 0x0005/*PATHW*/, driveLetter, IntPtr.Zero);
dwItem1 is void* and we should pass a wchar_t* (pointing to null terminated string) containing drive root in this case; so driveLetter above is
string letter = "Z:\\";
byte[] data = Encoding.Default.GetBytes(letter);
byte[] zdata = new byte[data.Length + 1];
data.CopyTo(zdata, 0);
IntPtr p = System.Runtime.InteropServices.Marshal.AllocHGlobal(zdata.Length);
System.Runtime.InteropServices.Marshal.Copy(zdata, 0, p, zdata.Length);
(my code almost same as code in similiar case: How to call SHChangeNotify from C# without error 14007
but I get System.AccessViolationException)
Any suggestions what am I doing wrong?
The first parameter in your interop signature should be an int, not a long. Though the Win32 function is declared as LONG, a LONG in Win32 is 32-bit.
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
public extern static void SHChangeNotify(int wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
This MSDN article shows the common mapping between Win32 types an .NET types for Platform Invoke.

Categories