Managed Debugging Assistant 'PInvokeStackImbalance' 检测到一个...关于简单的方法
Managed Debugging Assistant 'PInvokeStackImbalance' has detected a... on trivial method
我正在从 C# 调用 Delphi 函数并得到错误:
Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in ...
我已经尝试过更改 .Net 代码以适应 Delphi 签名,为什么它不能与基本整数一起使用让我感到困惑,有人知道我哪里出错了吗?
即使是使用 2 个整数的最简单的函数也会产生错误。
我的目标是 x86 并投入了几个小时的研究,但以下解决方案没有帮助 here,
here and here and this 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 的入口点是正确的,因为我得到 System.EntryPointNotFoundException 如果我尝试其他方法,请参阅此处:
这里有很多错误。眼前的问题在这里:
[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
规范在 C# 中导入这些函数。
您使用字符串的函数也是错误的,如果不更改两边的代码就无法修复。简单的解决方法是在 Delphi、var
参数中使用 WideString
参数。在 C# 中将其映射到 ref string
,封送为 UnmanagedType.BStr
。您在评论中链接到的答案向您展示了如何:
我正在从 C# 调用 Delphi 函数并得到错误:
Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in ...
我已经尝试过更改 .Net 代码以适应 Delphi 签名,为什么它不能与基本整数一起使用让我感到困惑,有人知道我哪里出错了吗?
即使是使用 2 个整数的最简单的函数也会产生错误。
我的目标是 x86 并投入了几个小时的研究,但以下解决方案没有帮助 here, here and here and this 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 的入口点是正确的,因为我得到 System.EntryPointNotFoundException 如果我尝试其他方法,请参阅此处:
这里有很多错误。眼前的问题在这里:
[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
规范在 C# 中导入这些函数。
您使用字符串的函数也是错误的,如果不更改两边的代码就无法修复。简单的解决方法是在 Delphi、var
参数中使用 WideString
参数。在 C# 中将其映射到 ref string
,封送为 UnmanagedType.BStr
。您在评论中链接到的答案向您展示了如何: