【发布时间】:2016-02-08 04:55:46
【问题描述】:
我从 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 获得了正确的入口点,因为如果我尝试其他任何方法,我会得到 System.EntryPointNotFoundException,看这里:
【问题讨论】:
-
为什么
OneTwoC有入口点TimTam? -
我编辑了我的问题以显示
exports EntryPoint name 'TimTam',但没有任何 Delphi 经验这可能是问题所在(因为我已经尝试了几乎所有其他数据类型映射排列以满足签名)。最终,TimTamC(1, outputStringBuffer, ref outInt, ref outputStringBuffer);是我真正需要调用的函数,OneTwo 示例是我削减了一个简单的骨骼复制。 -
好吧,
OneTwoC和OneTwoS没有与EntryPoint相同的签名,所以调用它很可能会导致您正在谈论的堆栈不平衡。TimTamC看起来很合理,但我知道 Delphi 中的字符串(过去)在与 C# 的互操作中需要特别小心,我怀疑我可以帮助你在没有 Delphi 进行测试的情况下正确完成它,所以我不得不将答案留给某人否则:) -
我编译了 Delphi DLL 以供下载 here,因为我知道这会阻止人们尝试。无论哪种方式,感谢您迄今为止的建议。你对 Delphi 字符串和互操作是正确的,我认为解决方案是使用
WideString,正如 David Hefferman 在这里指出的那样:stackoverflow.com/a/26043567/495455 但需要 Delphi 同事更改他的代码让我尝试这种方式。跨度>