ファイル コピーの進行状況に関する通知を取得するために、LPPROGRESS_ROUTINE パラメーターに匿名デリゲートを渡して C# アプリケーションから CopyFileEx を呼び出しています。
私の質問は、匿名のデリゲートを固定する必要があるかどうか、およびその理由 (またはそうでない理由) です。
さらに、次の場合、答えは変わりますか?
- CopyFileEx はブロックされませんでした。
- 匿名ではないデリゲートを渡した場合。
ありがとう!
デリゲートを固定する必要はありません。管理対象オブジェクトは、ガベージ コレクターによって移動できない場合に固定されます。マーシャリング情報が正しい場合、マーシャリング レイヤーは、不動のものへのポインタが渡されることを保証します。
ただし、ローカル変数がデリゲートを存続させる可能性があることを示唆している上記のコメントは、変数の有効期間の誤解を示しています。仕様を参照してください。次のように記載されています。
ローカル変数の実際の有効期間は実装に依存します。たとえば、コンパイラは、ブロック内のローカル変数がそのブロックの小さな部分にのみ使用されていることを静的に判断する場合があります。この分析を使用して、コンパイラーは、変数のストレージがそれを含むブロックよりも短い寿命を持つコードを生成する可能性があります。ローカル参照変数によって参照されるストレージは、そのローカル参照変数の有効期間とは無関係に再利用されます
つまり、次のように言えます。
void M()
{
Foo foo = GetAFoo();
UnmanagedLibrary.DoSomethingToFoo(foo);
}
次に、ジッターは「アンマネージ呼び出しが呼び出された直後にマネージ コードが foo を再び使用することはありません。したがって、その時点で別のスレッドからそのオブジェクトのストレージを積極的に回収できます」と言うことができます。これは、別のスレッドで突然割り当てが解除されたときに、アンマネージ呼び出しがオブジェクトで機能している可能性があることを意味します。
Foo にデストラクタがある場合、これは特に厄介です。オブジェクトがアンマネージ ライブラリによって使用されている間、ファイナライズ コードが別のスレッドで実行される可能性があり、それがどのような災害を引き起こすかは天のみぞ知ることです。
この状況では、KeepAlive を使用して管理対象オブジェクトを存続させる必要があります。ローカル変数に依存しないでください。ローカル変数は、物事を存続させることが保証されていないため、具体的に文書化されています。
詳細については、 http://msdn.microsoft.com/en-us/library/system.gc.keepalive.aspxを参照してください。
固定する必要はありませんが、コピーが進行している限り、参照を有効にしておく必要があります。
アンマネージ コードによって呼び出されるサンクは固定されていますが、デリゲートがガベージ コレクションされていないことを確認する必要があります。
次のmsdnから、CopyFileEx は同期的であるため、この場合は固定と GC.KeepAlive の両方が不要のようです。具体的には次のように述べています。
「通常、デリゲートの有効期間について心配する必要はありません。デリゲートをアンマネージ コードに渡すときはいつでも、CLR は呼び出し中にデリゲートが有効であることを確認します。ただし、ネイティブ コードがポインターが呼び出しの範囲を超えており、後でそのポインターを介してコールバックする予定がある場合は、GCHandle を使用して、ガベージ コレクターがデリゲートを収集するのを明示的に防止する必要がある場合があります。」
CopyFileEx は呼び出しのスパンを超えて関数へのポインターを保持しないため、KeepAlive を呼び出す必要はありません。