从 C# 应用程序调用 Delphi 函数时的 PInvokeStackImbalance

本文关键字:PInvokeStackImbalance 函数 Delphi 应用程序 调用 | 更新日期: 2023-09-27 18:36:20

我被迫使用非托管的delphi dll。我无法访问源代码。只有模糊的文档:

type
  TServiceData = packed record 
    DBAlias: PChar; 
    LicKey: PChar; 
    Pass: PChar; 
  end; 
  PServiceData = ^TServiceData; 
function CreateRole(SrvData: PServiceData; var UserName: PChar): byte; stdcall;

UserName应该是一个out参数。

我的 C# 代码:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SERVICE_DATA
{
    public string DBALias;
    public string LicKey;
    public string Pass;
}
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
public static extern byte CreateRole(SERVICE_DATA data, out string str);    

我不知道什么会导致堆栈不平衡(除了调用似乎是正确的约定)。我不知道我的结构中的字符串是否正确编组,但根据其他线程,这不会导致 PStackImbalanceException。任何帮助将不胜感激:)

编辑。我已经实施了大卫的建议,现在我得到了访问冲突异常:

"未处理的异常:系统。访问违规异常:试图读取或写入受保护的内存。这通常表示其他内存已损坏"

我的结构和方法声明只是从答案中复制粘贴的,我称呼它的方式没有什么花哨的:

        string str;
        var data = new SERVICE_DATA();
        data.DBALias = "test";
        data.LicKey = "test";
        data.Pass = "test";
        var result = CreateRole(ref data, out str);

从 C# 应用程序调用 Delphi 函数时的 PInvokeStackImbalance

翻译有几个问题:

  1. 德尔菲代码接收指向记录的指针。C# 代码按值传递它。这就是堆栈不平衡警告的原因。
  2. 用户名参数可能在 Delphi 端处理不正确。它需要是指向动态分配内存的指针,通过调用 COM 堆CoTaskMemAlloc 分配。我猜你没有这样做,所以当编组器试图通过调用CoTaskMemFree来释放指针时,你会遇到问题。

我可能会对字符串使用 COM 字符串类型。我也会避免打包记录,因为一般来说,这是不好的做法。

我会这样写:

德 尔 福

type
  TServiceData = record
    DBAlias: WideString;
    LicKey: WideString;
    Pass: WideString;
  end;
function CreateRole(const SrvData: TServiceData; out UserName: WideString): Byte; 
  stdcall;

C#

[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_DATA
{
    [MarshalAs(UnmanagedType.BStr)]
    public string DBALias;
    [MarshalAs(UnmanagedType.BStr)]
    public string LicKey;
    [MarshalAs(UnmanagedType.BStr)]
    public string Pass;
}
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
public static extern byte CreateRole(
    [In] ref SERVICE_DATA data, 
    [MarshalAs(UnmanagedType.BStr)] out string str
);    

这是一个完整的测试项目,表明它按预期工作:

德 尔 福

library Project1;
type
  TServiceData = record
    DBAlias: WideString;
    LicKey: WideString;
    Pass: WideString;
  end;
function CreateRole(const SrvData: TServiceData; out UserName: WideString): Byte;
  stdcall;
begin
  UserName := SrvData.DBAlias + SrvData.LicKey + SrvData.Pass;
  Result := Length(UserName);
end;
exports
  CreateRole;
begin
end.

C#

using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
    class Program
    {
        const string dllname = @"...";
        [StructLayout(LayoutKind.Sequential)]
        public struct SERVICE_DATA
        {
            [MarshalAs(UnmanagedType.BStr)]
            public string DBALias;
            [MarshalAs(UnmanagedType.BStr)]
            public string LicKey;
            [MarshalAs(UnmanagedType.BStr)]
            public string Pass;
        }
        [DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
        public static extern byte CreateRole(
            [In] ref SERVICE_DATA data,
            [MarshalAs(UnmanagedType.BStr)] out string str
        ); 
        static void Main(string[] args)
        {
            SERVICE_DATA data;
            data.DBALias = "DBALias";
            data.LicKey = "LicKey";
            data.Pass = "Pass";
            string str;
            var result = CreateRole(ref data, out str);
            Console.WriteLine(result);
            Console.WriteLine(str);
        }
    }
}

输出

17DBALiasLicKeyPass
相关文章:
  • 没有找到相关文章