代码之家  ›  专栏  ›  技术社区  ›  Jeremy Thompson

托管调试助手“PInvokeStackImbalance”在平凡方法上检测到

  •  0
  • Jeremy Thompson  · 技术社区  · 8 年前

    我从C#调用一个Delphi函数,得到错误:

    托管调试助手“PInvokeStackImbalance”在…中检测到问题。。。

    我已经尝试过改变.Net代码以适应Delphi签名,为什么它不能与基本的整数一起使用,这让我很困惑,有人知道我哪里出错了吗?

    即使是使用2个整数的最简单函数也会产生错误。

    我的目标是x86,并投入了几个小时的研究,但以下解决方案没有帮助 here , here here 还有这个 one .

    这是Delphi代码(编译的DLL版本可以从 here ):

    unit PasBallEntry;
    interface
    
    procedure EntryPoint( InInt: integer; InStr: PChar;
                          var OutInt: integer; var OutStr: PChar); stdcall;
    
    procedure ReleaseString( OutStr: PChar); stdcall;
    
    
    procedure TimTamC( InInt: integer; InStr: PChar;
                       var OutInt: integer; var OutStr: PChar); cdecl;
    
    procedure ReleaseStringC( OutStr: PChar); cdecl;
    
    procedure TimTamCS( InInt: integer; InStr: PChar;
                        var OutInt: integer; var OutStr: PChar); cdecl; stdcall;
    
    procedure ReleaseStringCS( OutStr: PChar); cdecl; stdcall;
    
    procedure OneTwoS( var A, B: integer); stdcall;
    procedure OneTwoC( var A, B: integer); cdecl;
    procedure OneTwoCS( var A, B: integer); cdecl; stdcall;
    
    exports
    EntryPoint    name 'TimTam',
    ReleaseString name 'ReleaseString';
    
    implementation
    
    uses Windows, SyncObjs, Classes, Generics.Collections;
    
    var
      Gate: TCriticalSection;
      Strs: TStrings;
      StrP: TList<PChar>;
    
    procedure EntryPoint( InInt: integer; InStr: PChar;
                          var OutInt: integer; var OutStr: PChar);
    var
      InStrL, OutStrL: string;
    begin
      OutInt  := 2 * InInt;
      InStrL  := InStr;
      OutStrL := InStrL + '_OUT!';
      UniqueString( OutStrL);
      if OutStrL = '' then
          OutStr := nil
        else
          begin
          OutStr := PChar( OutStrL);
          Gate.Enter;
          Strs.Add( OutStrL);
          StrP.Add( OutStr );
          Gate.Leave
          end
    end;
    
    procedure ReleaseString( OutStr: PChar);
    var
      I: integer;
    begin
      if not assigned( OutStr) then exit;
      Gate.Enter;
      StrP.Insert( I, OutStr);
      if I >= 0 then
        begin
        StrP.Delete( I);
        Strs.Delete( I)
        end;
      Gate.Leave
    end;
    
    procedure TimTamC( InInt: integer; InStr: PChar;
                       var OutInt: integer; var OutStr: PChar);
    begin
      EntryPoint( InInt, InStr, OutInt, OutStr)
    end;
    
    procedure ReleaseStringC( OutStr: PChar);
    begin
      ReleaseString( OutStr)
    end;
    
    procedure TimTamCS( InInt: integer; InStr: PChar;
                        var OutInt: integer; var OutStr: PChar);
    begin
      EntryPoint( InInt, InStr, OutInt, OutStr)
    end;
    
    procedure ReleaseStringCS( OutStr: PChar);
    begin
      ReleaseString( OutStr)
    end;
    
    procedure OneTwoS( var A, B: integer);
    begin
      A := 1;
      B := 2
    end;
    
    procedure OneTwoC( var A, B: integer);
    begin
      A := 1;
      B := 2
    end;
    
    procedure OneTwoCS( var A, B: integer);
    begin
      A := 1;
      B := 2
    end;
    
    initialization
      Gate := TCriticalSection.Create;
      Strs := TStringList.Create;
      StrP := TList<PChar>.Create
    
    finalization
      Strs.Free;
      Gate.Free;
      StrP.Free
    
    end.
    

    这是.Net代码:

    [DllImport("PascalBall.dll", EntryPoint = "TimTam", CallingConvention = CallingConvention.Cdecl)]
    public static extern void OneTwoC(ref int a, ref int b);
    
    [DllImport("PascalBall.dll", EntryPoint = "TimTam", CallingConvention = CallingConvention.StdCall)]
    public static extern void OneTwoS(ref int a, ref int b);
    
    [DllImport("PascalBall.dll", EntryPoint = "TimTam", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    public static extern void TimTamC(int inputInt, string inputString, ref int outputInt, ref string outputString);
    
    private void button1_Click(object sender, EventArgs e)
    {
        int a = 0;
        int b = 0;
    
        //Both these PInvoke calls fail (either StdCall or Cdecl)
        OneTwoS(ref a, ref b);
    
        OneTwoC(ref a, ref b);
    
        System.Diagnostics.Debug.WriteLine(a + b);
    }
    
    private void button2_Click(object sender, EventArgs e)
    {
        int outInt = 1;
        string outStr = "world";
        const int stringBufferSize = 1024;
        var outputStringBuffer = new String('\x00', stringBufferSize);
    
        try
        {
            TimTamC(1, outputStringBuffer, ref outInt, ref outputStringBuffer);
            ReleaseString(ref outStr);
        }
        catch (Exception ex)
        {
        }
    }
    

    编辑1: 认为 我使用TimTam正确输入了EntryPoint,因为我得到了 系统入口点未找到异常 如果我尝试其他方法,请看这里:

    enter image description here

    1 回复  |  直到 7 年前
        1
  •  1
  •   Community Egal    7 年前

    这里有很多错误。眼前的问题是:

    [DllImport("PascalBall.dll", EntryPoint = "TimTam", CallingConvention = CallingConvention.Cdecl)]
    public static extern void OneTwoC(ref int a, ref int b);
    
    [DllImport("PascalBall.dll", EntryPoint = "TimTam", CallingConvention = CallingConvention.StdCall)]
    public static extern void OneTwoS(ref int a, ref int b);
    

    为什么要指定 EntryPoint = "TimTam" ? 该函数不是您尝试导入的函数,并且具有不兼容的签名。因此,堆栈不平衡错误。

    您需要导出 OneTwoS OneTwoC 通过将它们添加到Delphi exports 条款您需要通过删除错误的 EntryPoint 规格

    使用字符串的函数也是错误的,如果不改变代码的两侧,就无法修复。简单的修复方法是使用 WideString Delphi中的参数, var 参数。将其映射到 ref string 在C#中,封送为 UnmanagedType.BStr 。您在评论中链接到的答案向您展示了如何: https://stackoverflow.com/a/26043567/495455