从 C# 应用程序调用 Delphi 函数时的 PInvokeStackImbalance
PInvokeStackImbalance when calling Delphi function from C# application
我被迫使用非托管 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。任何帮助将不胜感激:)
编辑。
我已经实施了 David 的建议,现在我遇到了访问冲突异常:
“未处理的异常:System.AccessViolationException:试图读取或写入受保护的内存。这通常表明其他内存已损坏”
我的结构和方法声明只是从答案中复制粘贴而来,我的调用方式没有什么特别的:
string str;
var data = new SERVICE_DATA();
data.DBALias = "test";
data.LicKey = "test";
data.Pass = "test";
var result = CreateRole(ref data, out str);
翻译有几处错误:
- Delphi 代码接收到记录的指针。 C# 代码按值传递它。这就是堆栈不平衡警告的原因。
- 用户名参数可能在 Delphi 端处理不正确。它需要是指向动态分配内存的指针,通过调用
CoTaskMemAlloc
在 COM 堆上分配。我猜你没有这样做,所以当编组器试图通过调用 CoTaskMemFree
. 来释放指针时,你会遇到问题
我可能会使用 COM 字符串类型作为字符串。我也会避免打包记录,因为这通常是不好的做法。
我会这样写:
Delphi
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
);
这是一个完整的测试项目,表明它按预期工作:
Delphi
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);
}
}
}
输出
17
DBALiasLicKeyPass
我被迫使用非托管 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。任何帮助将不胜感激:)
编辑。 我已经实施了 David 的建议,现在我遇到了访问冲突异常:
“未处理的异常:System.AccessViolationException:试图读取或写入受保护的内存。这通常表明其他内存已损坏”
我的结构和方法声明只是从答案中复制粘贴而来,我的调用方式没有什么特别的:
string str;
var data = new SERVICE_DATA();
data.DBALias = "test";
data.LicKey = "test";
data.Pass = "test";
var result = CreateRole(ref data, out str);
翻译有几处错误:
- Delphi 代码接收到记录的指针。 C# 代码按值传递它。这就是堆栈不平衡警告的原因。
- 用户名参数可能在 Delphi 端处理不正确。它需要是指向动态分配内存的指针,通过调用
CoTaskMemAlloc
在 COM 堆上分配。我猜你没有这样做,所以当编组器试图通过调用CoTaskMemFree
. 来释放指针时,你会遇到问题
我可能会使用 COM 字符串类型作为字符串。我也会避免打包记录,因为这通常是不好的做法。
我会这样写:
Delphi
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
);
这是一个完整的测试项目,表明它按预期工作:
Delphi
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);
}
}
}
输出
17 DBALiasLicKeyPass