问题
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