1

私は非常に奇妙な問題を抱えています:

NUnit を使用してアンマネージ C dll への関数呼び出しをいくつかテストしています。奇妙なことに、正常に実行するとテストは失敗しますが、デバッガーで実行すると (ブレークポイントがなくても) 正常に合格します。

では、デバッガーはプレーンな NUnit アプリケーションよりも広いメモリ アクセスを持っていますか?

失敗した呼び出しを分離しました。マーシャラーが C# 文字列に変換する必要がある文字列への char ポインターを返します。C 側は次のようになります。

#define get_symbol(a) ((a).a_w.w_symbol->s_name)
EXTERN char *atom_get_symbol(t_atom *a);

...

char *atom_get_symbol(t_atom *a) {
  return get_symbol(*a);
}

および C# コード:

[DllImport("csharp.dll", EntryPoint="atom_get_symbol")]
[return:MarshalAs(UnmanagedType.LPStr)]
private static extern string atom_get_symbol(IntPtr a);

c から返されるポインターは、コード内の非常に深い部分とリストの一部です。だから私はいくつかのセキュリティ設定を見逃していますか?

編集:ここに私が得る例外があります:

System.AccessViolationException : (英語に翻訳:) 保護されたメモリを読み書きしようとしました。これは、他のメモリが破損していることを示している可能性があります。

at Microsoft.Win32.Win32Native.CoTaskMemFree(IntPtr ptr)
at ....atom_get_symbol(IntPtr a)

解決:

問題は、マーシャラーが C 構造体の一部であるメモリを解放したかったことです。ただし、文字列のコピーを作成して、メモリをそのままにしておく必要があります。

[DllImport("csharp.dll", EntryPoint="atom_get_symbol")]
private static extern IntPtr atom_get_symbol(IntPtr a);

次に、コードで次の文字列のコピーを取得します。

var string = Marshal.PtrToStringAnsi(atom_get_symbol(ptrToStruct));

すごい!

4

1 に答える 1

4

これにより、Vista 以降では常にクラッシュが発生します。どのように回避したかは明確ではありません。スタック トレースは、pinvoke マーシャラーが文字列に割り当てられた文字列バッファーを解放しようとしていることを物語っています。そのために常に CoTaskMemFree() を使用します。これは、文字列にメモリを割り当てるために使用された可能性のあるアロケーターで唯一の合理的な推測です。しかし、それがうまくいくことはめったにありません。C または C++ コードはほとんどの場合、CRT のプライベート ヒープを使用します。これは XP ではクラッシュしません。より寛容なメモリ マネージャーを備えています。これにより、診断不能なメモリ リークが発生します。

注目に値するのは、C の宣言では、関数をピンボークできるという保証があまりなく、. を返さないことconst char*です。唯一の希望は、戻り値の型を文字列ではなく IntPtr として宣言して、pinvoke マーシャラーがポイント先のメモリを解放しようとしないようにすることです。返された IntPtr を文字列に変換するには、Marshal.PtrToStringAnsi() を使用する必要があります。

メモリをリークしないようにするには、関数を 10 億回呼び出す必要があります。そのテストが OutOfMemoryException でクラッシュする場合は、大きな問題があります。唯一の代替手段は、C++/CLI 言語でラッパーを作成し、ネイティブ コードとまったく同じバージョンの CRT を使用して、両方が同じヒープを使用するようにすることです。ソースコードがなければ、これはトリッキーで不可能です。この関数は、C を含む任意の言語から呼び出すのは単純に困難int atom_get_symbol(t_atom* a, char* buf, size_t buflen)です。クライアント コードによって割り当てられたバッファーを使用して呼び出すことができるように宣言する必要があります。

于 2012-05-13T16:04:38.313 に答える