6

次のコードを使用してネイティブ Delphi dll を呼び出す C# アプリケーションがあります。

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 ビット アプリケーションで正常に動作します。しかし、C# exe と Delphi dll の両方を 64 ビット用にコンパイルすると、奇妙な問題が発生します。Delphi デバッガーで GetString を呼び出した後、.NET コードのどこかで例外が発生し、デバッガーの出力ウィンドウに次の文字列が表示されることがわかります: "Critical error detected c0000374" . Google は、このエラーはヒープの破損に関連していると述べています。out/out の代わりに ref/var パラメータ修飾子を使用してみました。まだ運がありません。なぜこのエラーが発生するのですか? 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;

正常に動作します。しかし、out パラメーターとして文字列を返す必要があります。

4

1 に答える 1

17

そのように管理されているネイティブから文字列を渡すことはできません。あなたのコードは 32 ビットでも間違っています。コードの 2 番目のバージョンも間違っています。動作するように見えるだけです。

次のいずれかを行う必要があります。

  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 に答える