職場では、専用のフラット ファイル データベースの読み取りと書き込みを担当するネイティブ C コードがあります。P/Invoke 呼び出しを OO モデルにカプセル化する C# で記述されたラッパーがあります。P/Invoke 呼び出しのマネージ ラッパーは、プロジェクトが開始されてからかなり複雑になりました。逸話的には、現在のラッパーは問題なく動作していますが、正しい動作を確保するには、実際にはさらに多くのことを行う必要があると考えています。
回答によってもたらされたいくつかのメモ:
- おそらくキープアライブは必要ありません
- おそらくGCHandleのピン留めは必要ありません
- GCHandle を使用する場合は、試してみてください...最終的にそのビジネス (ただし、CER の質問は解決されていません)
修正されたコードの例を次に示します。
[DllImport(@"somedll", EntryPoint="ADD", CharSet=CharSet.Ansi,
ThrowOnUnmappableChar=true, BestFitMapping=false,
SetLastError=false)]
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
internal static extern void ADD(
[In] ref Int32 id,
[In] [MarshalAs(UnmanagedType.LPStr)] string key,
[In] byte[] data, // formerly IntPtr
[In] [MarshalAs(UnmanagedType.LPArray, SizeConst=10)] Int32[] details,
[In] [MarshalAs(UnmanagedType.LPArray, SizeConst=2)] Int32[] status);
public void Add(FileId file, string key, TypedBuffer buffer)
{
// ...Arguments get checked
int[] status = new int[2] { 0, 0 };
int[] details = new int[10];
// ...Make the details array
lock (OPERATION_LOCK)
{
ADD(file.Id, key, buffer.GetBytes(), details, status);
// the byte[], details, and status should be auto
// pinned/keepalive'd
if ((status[0] != 0) || (status[1] != 0))
throw new OurDatabaseException(file, key, status);
// we no longer KeepAlive the data because it should be auto
// pinned we DO however KeepAlive our 'file' object since
// we're passing it the Id property which will not preserve
// a reference to 'file' the exception getting thrown
// kinda preserves it, but being explicit won't hurt us
GC.KeepAlive(file);
}
}
私の(修正された)質問は次のとおりです。
- データ、詳細、ステータスは自動固定/キープアライブされますか?
- これが正しく動作するために必要なものが他にありませんか?
編集: 最近、私の好奇心をかき立てた図を見つけました。基本的には、P/Invoke メソッドを呼び出すと、 GC がネイティブ コードをプリエンプトできると述べています。そのため、ネイティブ呼び出しは同期的に行われる可能性がありますが、GCはメモリを実行して移動/削除することを選択できます。私は今、自動ピン留めで十分かどうか(またはそれが実行されるかどうか)疑問に思っていると思います。