将字符串从delphi dll返回到64位的C#调用者

时间:2013-04-23 13:16:04

标签: c# delphi pinvoke

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

C#

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

的Delphi

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

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

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

C#

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

的Delphi

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

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

1 个答案:

答案 0 :(得分:17)

您不能以这种方式从本机托管传递字符串。你的代码在32位也是错误的,你恰好侥幸逃脱它。代码的第二个版本也是错误的。它似乎只能起作用。

您需要:

  1. 从共享堆分配,以便托管代码可以从该堆中解除分配。 p / invoke的共享堆是COM堆。
  2. 在托管端分配内存,并将内容复制到本机端的缓冲区中。
  3. 选项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);