2011-07-15 11 views
3

複数の関数を実装するDLLとのインターフェイスを試行していますが、そのうちの1つはnullで終了する文字列とintをとり、NULLで終了する文字列を返します。私はこれをしようとしたとき、AccessViolationExceptionは、私は私に言って投げている、しかしDllImport nullで終了した文字列を返すAccessViolationException

StringBuilder message = new StringBuilder(1000); 
StringBuilder out2 = new StringBuilder(1000); 
out2 = GetErrorMessage(message, res0); 

[DllImport(dll_loc)] 
[return : MarshalAs(UnmanagedType.LPStr)] 
public static extern StringBuilder GetErrorMessage([MarshalAs(UnmanagedType.LPStr)] 
                 StringBuilder message, 
                 int error_code); 

は、私は、このようなメソッドを呼び出そう:私はこのような方法とのインタフェースしようとしてきました保護されたメモリにアクセスしようとしています。

は、私は、次のようなさまざまな方法を宣言することに成功した:

[DllImport(dll_loc)] 
public static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)] 
             StringBuilder version); 

と同じようにそれを呼び出すことが、この方法は、現在の関数呼び出しのために動作しません。

また、技術的には、メソッドが文字列バッファの最初の文字へのポインタを返しますが、役に立たないと書かれているので、IntPtrを返そうとしました。

ここで間違っている可能性のある人はいますか? dllがメモリにアクセスしようとしている原因となっているこれらの2つの方法の違いは何か。あるいは、この問題をデバッグすることをどうお勧めしますか?

+0

いくつかのWindows DLLをピンボケするときに同様の問題が発生しました。私は、パラメータのいくつかの小さな微調整によって大きな改善が見出されたことを知りました。やや間違った整列/データ型/ etc。大きな違いがあります。アンマネージDLLインターフェイスのドキュメントを実際に詳しく見ていく必要があります。 –

答えて

10

文字列を返すC関数は、メモリ管理の問題です。 C文字列は配列を必要とし、その配列のメモリは、文字列が消費された後に解放する必要があります。そのような機能は、Cプログラムでは非常に使いにくく、次にピンボケでは使用できません。スタック上の文字列へのポインタを返す古典的なCのバグです。

pinvoke marshallerは、メモリリークを回避するために必要に応じて、返された文字列を解放しようとします。 CoTaskMemFree()を使用します。それはしばしば良い結果に終わることはないが、Cコードが実際にCoTaskMemAllocを使って配列のメモリを割り当てることは稀である。 XPでは、メモリリークが発生します。 VistaとWin7のヒープマネージャは非常に厳しく、アンマネージデバッガが接続されているとデバッグブレークが呼び出されます。次にアクセス違反でプログラムを爆破する。

戻り値の型をIntPtrとして宣言し、文字列をマーシャリングすることで、自動マーシャリングの動作を回避できます。通常、Marshal.PtrToStringAnsi()を使用します。しかし、あなたはまだメモリを解放するという課題に直面しています。あなたはCRTヒープのハンドルを持っておらず、free()を呼び出すことはできません。

文字列を返すC関数は、文字列バッファを渡す引数で宣言する必要があります。バッファーの大きさを表す引数。このように:

int GetErrorMessage(int errorCode, char* buffer, size_t bufferSize); 

これで、呼び出し元はバッファにメモリを割り当てて解放できるようになりました。そして、C関数は文字列を単にバッファにコピーします。戻り値を使用すると、コピーされた文字の数を知ることができます。または、より大きなバッファが必要であることが示されます。 bufferSize引数では気にしないでください。バッファオーバーフローが致命的で、恐ろしいFatalExecutionEngineError例外が呼び出されます。これは、GCヒープの破損が後で検出されないため、AVとしてundebuggableの例外です。 StringBuilderはC#側で使用し、ゼロ以外の容量で適切に初期化します。 bufferSizeの値。

+0

残念ながら、私は関数呼び出しを定義するCコードにアクセスすることはできません。これは、dllを介してインタフェースします。関数をマネージコードから呼び出すときに戻り値を無視し、文字列バッファを渡すだけでメモリリークの問題は解決しますか?または、これにより問題が解決されます。メモリリークがある場合、返されたIntPtrで 'Marshal.FreeHGlobal'を呼び出してそのメモリを解放できますか? – Patrick

+0

宣言をもう一度見てみると、この関数は単に呼び出されたポインタを返すことがあります。この場合、問題はありません。[DllImport]宣言でvoidを返すと宣言して、pinvoke marshallerはもう正しいことをやろうとしません。返された文字列を復元するには、message.ToString()を使用します。違う文字列ポインタが実際に返ってきたら、あなたはそれをピンボケできません。 –

1

常に私のためにこの方法を働いSystem.Runtime.InteropServices.Marshal.FreeHGlobal

を呼び出し、その後PtrToStringAuto

で文字列にmessageまたはout2を変換し、その後System.Runtime.InteropServices.Marshal.AllocHGlobal

のみmessage VARを割り当て、System.IntPtrですべてStringBuilder年代を交換してくださいchar*またはwchar_t*を扱う場合。

+0

私はこれを行う方法を正確にはわかりません。私は 'public static extern IntPtr GetErrorMessage(IntPtr message、int error_code);という関数を定義してみました。それから' IntPtr message = new IntPtr(); Marshal.AllocHGlobal(1000); IntPtr out2 = GetErrorMessage(message、res0); '、しかし同じ例外がスローされました。私は何か間違っているのですか? – Patrick

+0

IntPtr message = Marshal.AllocHGlobal(1000); – Djole

関連する問題