您显然在 p/invoke 代码和 Delphi 代码之间存在不匹配。您还没有展示 Delphi 代码,但 C# 代码足以知道 Delphi 代码应该是什么样子。
您的DllImport 属性使用调用约定和字符集的默认值。这意味着调用约定是stdcall,字符集是ANSI。您尚未指定任何编组属性,因此必须使用默认编组。
因此,您的 Delphi 代码必须如下所示:
function MyMethod(someStringParam: PChar): PChar; stdcall;
begin
Result := ??;
end;
现在问题来了。 p/invoke marshaller 以一种非常特殊的方式处理string 返回值。它假定 p/invoke marshaller 负责释放返回值的内存。它必须使用与本机代码相同的分配器。编组器的假设是共享 COM 分配器将被使用。
所以规则是本机代码必须通过调用CoTaskMemAlloc 来使用COM 分配器分配内存。我敢打赌,您的代码不会这样做,这肯定会导致错误。
这是一个示例,说明如何在代码中创建与 C# 签名一起使用的本机 Delphi 函数。
function MyMethod(someStringParam: PChar): PChar; stdcall;
var
Size: Integer;
begin
Size := SizeOf(Char)*(StrLen(someStringParam)+1);//+1 for zero-terminator
Result := CoTaskMemAlloc(Size);
Move(someStringParam^, Result^, Size);
end;
虽然您可以采用这种方法,但我推荐另一种方法。将所有字符串编组为 C# 端的 BSTR 和 Delphi 端的 WideString。这些是由 COM 分配器分配的匹配类型。双方都知道如何处理这些类型,这将使您的生活更轻松。
不幸的是,您不能从 Delphi 函数跨互操作边界返回 WideString,因为 Delphi 对函数返回值使用不同的 ABI。这个问题的更多细节可以在我的问题Why can a WideString not be used as a function return value for interop?
中找到
为了解决这个问题,我们可以将 Delphi 代码的返回类型声明为 TBStr。您的代码将如下所示:
C#
[DllImport(@"MyDll.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string MyMethod(
[MarshalAs(UnmanagedType.BStr)]
string someStringParam
);
德尔福
function MyMethod(someStringParam: WideString): TBStr; stdcall;
begin
Result := SysAllocString(POleStr(someStringParam));
end;