你很明显在p/invoke代码和Delphi代码之间不匹配。您没有显示Delphi代码,但是C#代码足以知道Delphi代码应该是什么样子。
DllImport
属性使用默认值调用约定和字符集。这意味着调用约定是stdcall
,字符集是ANSI。您尚未指定任何编组属性,因此必须使用默认编组。
因此你的Delphi代码必须是这样的:
function MyMethod(someStringParam: PChar): PChar; stdcall;
begin
Result := ??;
end;
而现在这里的问题。 p/invoke编组人员以一种非常特殊的方式处理一个string
返回值。它假定p/invoke编组人员负责解除分配返回值的内存。它必须使用与本地代码相同的分配器。编组人员所做的假设是共享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
和德尔福端的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;
DLL中函数的调用约定是什么? – ain 2011-12-16 11:31:50
请显示Delphi方法的代码 – 2011-12-16 11:32:08
Delphi中函数的大机会声明不同于C#的对应方 – 2011-12-16 11:39:22