6

我有一个 C# 应用程序,它使用以下代码调用本机 Delphi dll:

C#

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern int GetString(string out str);

德尔福

function GetString(out a: PChar): Integer; stdcall;
begin
  a := PChar('abc');
  Result := 1;
end;

这在 32 位应用程序中运行良好。但是当我为 64 位编译 C# exe 和 Delphi dll 时,我遇到了一个奇怪的问题。在 Delphi 调试器中调用 GetString 后,我可以看到在 .NET 代码中的某处引发了异常,并且以下字符串出现在“调试器输出”窗口中:“检测到严重错误 c0000374”。谷歌表示此错误与堆损坏有关。我尝试使用 ref/var 参数修饰符而不是 out/out。仍然没有运气。为什么我会收到此错误?我应该为 64 位使用不同的调用约定吗?

顺便提一句。以下组合工作正常:

C#

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern string GetString(string a);

德尔福

function GetString(a: PChar): PChar; stdcall;
var
  inp: string;
begin
  inp := a;
  Result := PChar('test ' + inp);
end;

工作正常。但我确实需要返回一个字符串作为输出参数。

4

1 回答 1

17

您不能以这种方式从本机管理传递字符串。您的代码在 32 位中也是错误的,您只是碰巧侥幸逃脱。第二个版本的代码也是错误的。它似乎只工作。

您需要:

  1. 从共享堆中分配,以便托管代码可以从该堆中释放。p/invoke 的共享堆是 COM 堆。
  2. 在托管端分配内存,并将内容复制到本机端的缓冲区中。

选项 2 始终是可取的。它看起来像这样:

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode)]
public static extern int GetString(StringBuilder str, int len);

在本机方面,您将拥有

function GetString(str: PChar; len: Integer): Integer; stdcall;
begin
  StrLCopy(str, 'abc', len);
  Result := 1; // real code would have real error handling
end;

然后像这样调用它:

StringBuilder str = new StringBuilder(256);
int retval = GetString(str, str.Capacity);

如果您想尝试选项 1,它在托管端看起来像这样:

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode)]
public static extern int GetString(out string str);

和这个本地人一样:

function GetString(out str: PChar): Integer; stdcall;
begin
  str = CoTaskMemAlloc(SizeOf(Char)*(Length('abc')+1));
  StrCopy(str, 'abc');
  Result := 1; // real code would have real error handling
end;

当托管代码将 的内容复制str到字符串值时,它会调用CoTaskMemFree您返回的指针。

这很容易调用:

string str;
int retval = GetString(out str);
于 2013-04-23T13:22:16.590 回答