托管调试助手"PInvokeStackImbalance"已检测到一个...无关紧要的方法 [英] Managed Debugging Assistant 'PInvokeStackImbalance' has detected a... on trivial method
问题描述
我正在从C#调用Delphi函数并收到错误消息:
托管调试助手"PInvokeStackImbalance"在...中检测到问题.
我已经竭尽全力尝试更改.Net代码以适合Delphi签名,为什么它不能与基本的Integers一起使用,让我感到困惑,有人知道我要去哪里了吗?
即使使用2个整数的最简单函数也会产生错误.
我的目标是x86,并进行了几个小时的研究,但以下解决方案没有帮助此处和一个.
这是Delphi代码(可从此处下载DLL版本)
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)
{
}
}
我认为我使用TimTam可以正确设置EntryPoint,因为如果尝试其他操作,我会收到 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中使用WideString
参数,在var
参数中使用.将其映射到C#中的ref string
,封送为UnmanagedType.BStr
.您在评论中链接的答案向您显示了如何: https://stackoverflow.com/a/26043567/495455 >
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:
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
这篇关于托管调试助手"PInvokeStackImbalance"已检测到一个...无关紧要的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!