セーフ ハンドルとクリティカル ファイナライズ

.NET Framework Version 2.0 より前は、オペレーティング システム ハンドルは IntPtr マネージ ラッパー オブジェクト内のみでカプセル化できました。 ネイティブ コードと相互運用する場合はこの方法が便利でしたが、予期せず中止されたスレッド、スタック オーバーフローなどの非同期例外によってハンドルがリークされるおそれがありました。 このような非同期例外は、オペレーティング システム リソースのクリーンアップの妨げとなり、プログラムのあらゆる場所で発生する可能性があります。 発生しやすい場所としては、Microsoft SQL Server などのマネージ コードを実行するホストを使用するアプリケーションがあります。

状況によっては、プラットフォーム呼び出しでのメソッドの実行中に、ガベージ コレクションによってファイナライズ可能なオブジェクトが再生される可能性があります。 そのプラットフォーム呼び出しに渡されたハンドルをファイナライザーが解放すると、ハンドルの破損につながるおそれがあります。 また、プラットフォーム呼び出し中 (ファイルの読み取り中など) にメソッドがブロックされている間に、ハンドルが再生される可能性もあります。

さらに重大な点は、Windows が積極的にハンドルをリサイクルするために、ハンドルがリサイクルされて、重要情報を含む別のリソースをポイントする可能性があることです。 これはリサイクル攻撃と呼ばれ、データを破壊し、セキュリティ上の脅威となるおそれがあります。

.NET Framework 2.0 からは、SafeHandle クラスによってオブジェクトの有効期間に関するこのような問題の一部が簡略化されています。このクラスは、オペレーティング システム リソースがリークされないように、プラットフォーム呼び出しに統合されています。 SafeHandle クラスは、処理を中断することなくハンドルの割り当てと解放を行うことで、オブジェクトの有効期間に関する問題を解決します。 このクラスは、ハンドルが確実に閉じられるようにするクリティカル ファイナライザーが含まれています。また、プラットフォーム呼び出しが破損状態にあると見られる場合でも、AppDomain のアンロード中に実行されることが保証されています。

SafeHandleCriticalFinalizerObject から継承されるため、すべての非クリティカル ファイナライザーは、クリティカル ファイナライザーの前に呼び出されます。 ファイナライザーは、同じガベージ コレクションの実行中に、有効でなくなったオブジェクトに対して呼び出されます。 たとえば FileStream オブジェクトは、標準のファイナライザーを実行して、ハンドルがリークされたりリサイクルされたりするリスクなしに既存のバッファー データを消去できます。 クリティカル ファイナライザーと非クリティカル ファイナライザー間のこの非常に弱い順序付けは、一般に使用されることを想定していません。 その主な存在目的は、既存のライブラリでそのセマンティクスを変えずに SafeHandle を使用できるようにして、それらのライブラリの移行を支援することです。 また、クリティカル ファイナライザーとそれが呼び出す SafeHandle.ReleaseHandle() などのすべてのメソッドは、制約された実行領域内にあることが必要です。 これにより、ファイナライザーの呼び出し先で記述できるコードの内容が制限されます。

.NET Framework Version 2.0 からは、プラットフォーム呼び出し操作で、SafeHandle によってカプセル化されたハンドルの参照カウントのインクリメント、および完了時のカウントのデクリメントが自動的に行われるようになりました。 これにより、ハンドルが予期せずリサイクルされたり閉じられたりしなくなります。

SafeHandle オブジェクトを構築する際に、基になるハンドルの所有権を指定できます。 これにより、SafeHandle オブジェクトが破棄された後にそのオブジェクトがハンドルを解放するかどうかを制御します。 これは、有効期間の要件が特殊であるハンドル、または第三者によって有効期間が制御されるハンドルを使用する場合に役立ちます。

セーフ ハンドル クラス

System.Runtime.InteropServices 名前空間の SafeHandle クラスは、オペレーティング システム ハンドルの抽象ラッパー クラスです。 このクラスからの派生は困難です。 代わりに、次のセーフ ハンドルを提供する Microsoft.Win32.SafeHandles 名前空間の派生クラスを使用してください。

  • ファイルとパイプ

  • メモリ ビュー

  • 暗号の構成要素

  • レジストリ キー

  • 待機ハンドル

参照

関連項目

SafeHandle

CriticalHandle

CriticalFinalizerObject