2013-04-23 11 views
6

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

BTW。以下の組み合わせが正常に動作します:

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パラメータとして文字列を返す必要があります。

答えて

17

ネイティブ管理の方法で文字列を渡すことはできません。あなたのコードは32ビットでも間違っています、あなたはただそれで逃げ出します。コードの2番目のバージョンも間違っています。それはうまくいくように見えます。

次のいずれかを行う必要があります。マネージコードは、そのヒープから割り当て解除できるというように、

  1. が共有ヒープから割り当てます。 p/invokeの共有ヒープはCOMヒープです。
  2. 管理対象側にメモリを割り当て、その内容をネイティブ側のそのバッファにコピーします。

オプション2は常に好ましいです。それは次のようになります。あなたはオプション1を試してみたい場合は、それがどのように見える

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

:ネイティブ側で

[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; 

は、そのようにようにこれを呼び出す必要がありますこれは管理対象側:

[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); 
+0

はどうもありがとうございました!あなたは私にこの問題で数時間の戦いをさせてくれました。 – Max

+0

'PChar'?チャンスの試合は 'PAnsiChar'か' PWideChar'でしたか? (もちろん、それは64ビットXE2であると推測できますが、このコードを64ビットFPCでコンパイルしたり、32ビットプロジェクトにコピーしたりすることはできません。 –

+0

@ Arioch'The次に、 'PWideChar'もしあなたが好きなら。 –

関連する問題