【问题标题】:Managed Debugging Assistant 'PInvokeStackImbalance' has detected a... on trivial method托管调试助手“PInvokeStackImbalance”检测到...
【发布时间】:2016-02-08 04:55:46
【问题描述】:

我从 C# 调用 Delphi 函数并得到错误:

托管调试助手“PInvokeStackImbalance”在...中检测到问题

我已经尝试过更改 .Net 代码以适应 Delphi 签名,为什么它不能与基本整数一起使用让我很困惑,有人知道我哪里出错了吗?

即使是使用 2 个整数的最简单的函数也会产生错误。

我的目标是 x86,并进行了几个小时的研究,但以下解决方案没有帮助 hereherehere 和这个 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 示例是我削减了一个简单的骨骼复制。
  • 好吧,OneTwoCOneTwoS 没有与 EntryPoint 相同的签名,所以调用它很可能会导致您正在谈论的堆栈不平衡。 TimTamC 看起来很合理,但我知道 Delphi 中的字符串(过去)在与 C# 的互操作中需要特别小心,我怀疑我可以帮助你在没有 Delphi 进行测试的情况下正确完成它,所以我不得不将答案留给某人否则:)
  • 我编译了 Delphi DLL 以供下载 here,因为我知道这会阻止人们尝试。无论哪种方式,感谢您迄今为止的建议。你对 Delphi 字符串和互操作是正确的,我认为解决方案是使用 WideString,正如 David Hefferman 在这里指出的那样:stackoverflow.com/a/26043567/495455 但需要 Delphi 同事更改他的代码让我尝试这种方式。跨度>

标签: c# delphi pinvoke


【解决方案1】:

这里有很多错误。眼前的问题在这里:

[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"?该函数不是您要导入的函数,并且具有不兼容的签名。因此堆栈不平衡错误。

您需要通过将OneTwoSOneTwoC 添加到Delphi exports 子句中来导出它们。您需要通过删除错误的EntryPoint 规范在C# 中导入这些函数。

您使用字符串的函数也是错误的,如果不更改代码的两边就无法修复。简单的解决方法是在 Delphi 中使用WideString 参数,var 参数。将其映射到 C# 中的 ref string,编组为 UnmanagedType.BStr。您在 cmets 中链接的答案向您展示了如何:https://stackoverflow.com/a/26043567/495455

【讨论】:

  • 谢谢大卫,我刚结束一天令人沮丧的工作回到家,就像 Joachim-isaksson 的直觉一样,我知道你已经用 2 个整数方法确定了根本原因。虽然我有时间,但我认为 WideString 是解决 TimTamC 调用问题的方法是否正确?它会产生相同的不平衡错误,最终结果是使该功能正常工作。非常感谢您在这里的支持,明天将更新此线程,因为德尔福同事和我将坐在一起讨论它
  • 感谢您的更新正是我们需要知道的,一旦验证将奖励。
  • TimTamC 上的堆栈不平衡是调用约定不匹配。 Cdecl 与标准调用。当您修复该代码仍然无法正常工作时。因为字符串编组错误。
  • 坦率地说,Delphi 代码都是无用且错误的。对不起。扔了这一切。所有的。元帅的事情与我上面的答案中链接的答案完全一样。不需要清单。无需锁定。一次专注于一项功能。这里的代码太复杂了,无法理解。你只是在混淆自己。
  • 对不起!!但这确实是无可救药的。无论如何,其他链接的答案有你所需要的一切。
猜你喜欢
  • 2019-12-14
  • 1970-01-01
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 2011-09-03
相关资源
最近更新 更多