托管调试助手"PInvokeStackImbalance"已检测到一个...无关紧要的方法 [英] Managed Debugging Assistant 'PInvokeStackImbalance' has detected a... on trivial method

查看:1297
本文介绍了托管调试助手"PInvokeStackImbalance"已检测到一个...无关紧要的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

您需要通过将OneTwoSOneTwoC添加到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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆