Managed Debugging Assistant 'PInvokeStackImbalance' has detected a… on trivial method

走远了吗. 提交于 2020-01-06 15:54:08

问题


I am calling a Delphi function from C# and get the error:

Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in ...

I've exhausted attempts changing the .Net code to fit the Delphi signatures, why it doesn't work with basic Integers has me stumped, does anyone know where I am going wrong?

Even the simplest function using 2 integers produces the error.

I'm targeting x86 and have put in a couple of hours research but the following solutions haven't helped here, here and here and this one.

This is the Delphi Code (compiled DLL version can be downloaded from 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.

Here is the .Net code:

[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)
    {
    }
}

Edit 1: I think I have the EntryPoint correct using TimTam, because I get a System.EntryPointNotFoundException if I try anything else, see here:


回答1:


There are a very large number of mistakes here. The immediate problem is here:

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

Why are you specifying EntryPoint = "TimTam"? That function is not the one you are trying to import and has an incompatible signature. Hence the stack imbalance error.

You need to export OneTwoS and OneTwoC by adding them to the Delphi exports clause. And you need to import these functions in the C# by removing the erroneous EntryPoint specification.

You functions using strings are wrong too and can't be fixed without changing both sides of the code. The simple fix is to use WideString parameters in Delphi, var parameters. Map that to ref string in C#, marshaled as UnmanagedType.BStr. The answer you linked to in comments shows you how: https://stackoverflow.com/a/26043567/495455



来源:https://stackoverflow.com/questions/35262558/managed-debugging-assistant-pinvokestackimbalance-has-detected-a-on-trivial

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!