代码之家  ›  专栏  ›  技术社区  ›  David Božjak

从C++非平衡栈调用C++函数

  •  4
  • David Božjak  · 技术社区  · 14 年前

    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++项目规范都是NULL。 0代表 是函数的有效输入。当我尝试调用它时,它似乎可以工作,但是在退出时,我得到以下错误:

    检测到PinvokeStack不平衡

    “…::调用” 很可能是因为管理品沃克 签名与非托管签名不匹配 调用约定和参数 非托管签名。

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

    3 回复  |  直到 4 年前
        1
  •  2
  •   leppie    14 年前

    IIRC,你需要用一个调用约定来修饰代理签名。不幸的是,这只能通过IL或使用Reflection.Emit生成存根来完成。

    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;
    }
    
        2
  •  8
  •   lfalin    13 年前

    我知道这个问题已经存在一年了,但是比动态构建类型更简单的方法是使用 UnmanagedFunctionPointer 属性,如下所示:

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

    MSDN

        3
  •  -1
  •   0xDEADBEEF    14 年前

    有两件事需要注意: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++代码通过它返回托管的世界,那么你就不用说你在做什么。阅读C#关键字ref和out,作为避免char*类型和不安全修饰符的方法。

    用谷歌搜索一下,你应该能找到答案。