你很明顯在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