web-dev-qa-db-ja.com

char *を返すC関数のPInvoke

アンマネージDLLからメソッドを呼び出すC#コードを書き込もうとしています。 dll内の関数のプロトタイプは次のとおりです。

extern "C" __declspec(dllexport) char *foo(void);

C#では、最初に次のものを使用しました。

[DllImport(_dllLocation)]
public static extern string foo();

表面的には機能しているようですが、実行時にメモリ破損エラーが発生します。たまたま正しいが、すでに解放されているメモリを指していると思います。

「P/InvokeInteropAssistant」というPInvokeコード生成ユーティリティを使用してみました。それは私に出力を与えました:

[System.Runtime.InteropServices.DLLImportAttribute(_dllLocation, EntryPoint = "foo")]
public static extern System.IntPtr foo();

これは正しいです?もしそうなら、どうすればこのIntPtrをC#の文字列に変換できますか?

45
Brandon

これをIntPtrとして返す必要があります。 PInvoke関数からSystem.String型を返すには、細心の注意が必要です。 CLRは、メモリをネイティブ表現からマネージ表現に転送する必要があります。これは簡単で予測可能な操作です。

ただし、問題はfoo()から返されたネイティブメモリをどうするかということにあります。 CLRは、文字列型を直接返すPInvoke関数について次の2つの項目を想定しています。

  1. ネイティブメモリを解放する必要があります
  2. ネイティブメモリはCoTaskMemAllocで割り当てられました

したがって、文字列をマーシャリングしてから、ネイティブメモリBLOBでCoTaskMemFreeを呼び出します。このメモリをCoTaskMemAllocで実際に割り当てない限り、これはせいぜいアプリケーションでクラッシュを引き起こすでしょう。

ここで正しいセマンティクスを取得するには、IntPtrを直接返す必要があります。次に、Marshal.PtrToString *を使用して、管理対象の文字列値を取得します。ネイティブメモリを解放する必要があるかもしれませんが、それはfooの実装に依存します。

75
JaredPar

Marshal.PtrToStringAutoメソッドを使用できます。

IntPtr ptr = foo();
string str = Marshal.PtrToStringAuto(ptr);
23
Strelok