从C#调用C ++函数 – 不平衡堆栈

我有一个带有以下签名的非托管C ++函数:

int function(char* param, int ret) 

我试图用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); } 

据我所知,从旧的C ++项目规范中, param为null, ret为0都是函数的有效输入。 当我尝试调用它似乎工作,但退出时我得到以下错误:

检测到PInvokeStackImbalance

对PInvoke函数’… :: Invoke’的调用使堆栈失衡。 这很可能是因为托管的PInvoke签名与非托管目标签名不匹配。 检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。

我已经尝试了几乎任何我能想到的东西(不安全是最后的手段),但是我找不到任何方法来运行该函数而不会得到不平衡的堆栈。 还有什么我可以尝试的吗?

IIRC,您需要使用调用约定来装饰委托签名。 不幸的是,这只能通过IL或使用Reflection.Emit生成存根来完成。

你可以试试这个:

 protected static Type MakeDelegateType(Type returntype, List 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; } 

我知道这个问题现在已经有一年了,但是比动态构建类型更简单的方法是使用委托上的UnmanagedFunctionPointer属性声明调用约定,如下所示:

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

来自MSDN :

控制作为非托管函数指针传递给非托管代码或从非托管代码传递的委托签名的编组行为。

有两件事需要注意:C#位和DLL之间的调用约定以及char *数据如何在此接口上进行编组。 如果您遇到其中任何一个错误,那么您将收到堆栈损坏投诉。 在定义界面时,如果可以将数据块的大小限制为固定的,则更容易。 设置最大字符串长度。

这是静态版本,其中DLL名称是固定的,您的字符串被处理为byte []并且大小限制为2Kbyte,您可以从中计算动态版本:

  private const string MYDLL = @"my.dll"; [DllImport(MYDLL, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)] public static extern int DataBlockDownload([MarshalAs(UnmanagedType.U4)] int A, [MarshalAs(UnmanagedType.LPArray, SizeConst = 2048)] byte[] B, [MarshalAs(UnmanagedType.U4)] int C); // NOTE: The data block byte array is fixed at 2Kbyte long!! public delegate int DDataBlockCallback([MarshalAs(UnmanagedType.U4)] int A, [MarshalAs(UnmanagedType.LPArray, SizeConst = 2048)] byte[] B, [MarshalAs(UnmanagedType.U4)] int C); 

如上所述,如果要保留char类型,可能还需要指定正在使用的字符集。

如果将c ++代码作为参数或者C ++代码将其传递回托管世界,则不会说明您对char *数据执行的操作。 阅读C#关键字ref和out作为避免char *类型和unsafe修饰符的方法。

通过一点谷歌搜索,你应该能够搞清楚。