Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a C++ function from C# - unbalanced stack

I have a unmanaged C++ function with the following signature:

int function(char* param, int ret)

I am trying to call it from C#:

unsafe delegate int MyFunc(char* param, int ret);

...

int Module = LoadLibrary("fullpathToUnamanagedDll");
IntPtr pProc = GetProcAddress(Module, "functionName");
MyFunc func = (MyFunc)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(pProc, typeof(MyFunc));

unsafe
{
    char* param = null;
    int ret = 0;
    int result = func(param, ret);
}

As far as I can tell from the old C++ project specification both null for param and 0 for ret are valid inputs to the function. When I try to call it it seems to work, however upon exiting I get the following error:

PInvokeStackImbalance was detected

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

I have tried pretty much anything I could think off (unsafe was last resort), however I can't find any way to run the function without getting unbalanced stack. Is there something else I could try?

like image 323
David Božjak Avatar asked Jan 22 '23 17:01

David Božjak


2 Answers

I know this question is a year old now, but an easier method than building the type dynamically is to declare the calling convention using an UnmanagedFunctionPointer attribute on your delegate, like this:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
unsafe delegate int MyFunc(char* param, int ret);

From MSDN:

Controls the marshaling behavior of a delegate signature passed as an unmanaged function pointer to or from unmanaged code.

like image 115
lfalin Avatar answered Jan 24 '23 07:01

lfalin


IIRC, you need to decorate the delegate signature with a calling convention. Unfortunately, this can only be done via IL or generating the stub with Reflection.Emit.

You can try this:

protected static Type MakeDelegateType(Type returntype, List<Type> paramtypes)
{
  ModuleBuilder dynamicMod = ... ; // supply this

  TypeBuilder tb = dynamicMod.DefineType("delegate-maker" + Guid.NewGuid(), 
      TypeAttributes.Public | TypeAttributes.Sealed, typeof(MulticastDelegate));

  tb.DefineConstructor(MethodAttributes.RTSpecialName | 
       MethodAttributes.SpecialName | MethodAttributes.Public |
       MethodAttributes.HideBySig, CallingConventions.Standard,
       new Type[] { typeof(object), typeof(IntPtr) }). 
       SetImplementationFlags(MethodImplAttributes.Runtime);

  var inv = tb.DefineMethod("Invoke", MethodAttributes.Public | 
       MethodAttributes.Virtual | MethodAttributes.NewSlot | 
       MethodAttributes.HideBySig, 
       CallingConventions.Standard ,returntype,null, 
       new Type[] 
       { 
          // this is the important bit
          typeof(System.Runtime.CompilerServices.CallConvCdecl)
       }, 
       paramtypes.ToArray(), null, null);

  inv.SetImplementationFlags(MethodImplAttributes.Runtime);

  var t = tb.CreateType();
  return t;
}
like image 40
leppie Avatar answered Jan 24 '23 06:01

leppie