SafeHandle クラス
定義
重要
一部の情報は、リリース前に大きく変更される可能性があるプレリリースされた製品に関するものです。 Microsoft は、ここに記載されている情報について、明示または黙示を問わず、一切保証しません。
オペレーティング システム ハンドルのラッパー クラスを表します。 このクラスは継承できません。
public ref class SafeHandle abstract : IDisposable
public ref class SafeHandle abstract : System::Runtime::ConstrainedExecution::CriticalFinalizerObject, IDisposable
[System.Security.SecurityCritical]
public abstract class SafeHandle : IDisposable
public abstract class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
[System.Security.SecurityCritical]
public abstract class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
[<System.Security.SecurityCritical>]
type SafeHandle = class
interface IDisposable
type SafeHandle = class
inherit CriticalFinalizerObject
interface IDisposable
[<System.Security.SecurityCritical>]
type SafeHandle = class
inherit CriticalFinalizerObject
interface IDisposable
Public MustInherit Class SafeHandle
Implements IDisposable
Public MustInherit Class SafeHandle
Inherits CriticalFinalizerObject
Implements IDisposable
- 継承
-
SafeHandle
- 継承
- 派生
- 属性
- 実装
例
次のコード例では、 から SafeHandleZeroOrMinusOneIsInvalid派生したオペレーティング システム ファイル ハンドルのカスタム セーフ ハンドルを作成します。 ファイルからバイトを読み取り、16 進数の値を表示します。 また、エラー テスト ハーネスも含まれています。これにより、スレッドは中止されますが、ハンドル値は解放されます。 を IntPtr 使用してハンドルを表すと、非同期スレッドの中止が原因でハンドルがリークすることがあります。
コンパイル済みアプリケーションと同じフォルダーにテキスト ファイルが必要です。 アプリケーションに "HexViewer" という名前を付ける場合、コマンド ラインの使用法は次のようになります。
HexViewer <filename> -Fault
必要に応じて、特定のウィンドウでスレッドを中止してハンドルのリークを意図的に試行するように指定 -Fault
します。 Windows Perfmon.exe ツールを使用して、障害の挿入中にハンドル数を監視します。
using System;
using System.Runtime.InteropServices;
using System.IO;
using System.ComponentModel;
using System.Security;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Runtime.ConstrainedExecution;
using System.Security.Permissions;
namespace SafeHandleDemo
{
internal class MySafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid
{
// Create a SafeHandle, informing the base class
// that this SafeHandle instance "owns" the handle,
// and therefore SafeHandle should call
// our ReleaseHandle method when the SafeHandle
// is no longer in use.
private MySafeFileHandle()
: base(true)
{
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
override protected bool ReleaseHandle()
{
// Here, we must obey all rules for constrained execution regions.
return NativeMethods.CloseHandle(handle);
// If ReleaseHandle failed, it can be reported via the
// "releaseHandleFailed" managed debugging assistant (MDA). This
// MDA is disabled by default, but can be enabled in a debugger
// or during testing to diagnose handle corruption problems.
// We do not throw an exception because most code could not recover
// from the problem.
}
}
[SuppressUnmanagedCodeSecurity()]
internal static class NativeMethods
{
// Win32 constants for accessing files.
internal const int GENERIC_READ = unchecked((int)0x80000000);
// Allocate a file object in the kernel, then return a handle to it.
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
internal extern static MySafeFileHandle CreateFile(String fileName,
int dwDesiredAccess, System.IO.FileShare dwShareMode,
IntPtr securityAttrs_MustBeZero, System.IO.FileMode dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile_MustBeZero);
// Use the file handle.
[DllImport("kernel32", SetLastError = true)]
internal extern static int ReadFile(MySafeFileHandle handle, byte[] bytes,
int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);
// Free the kernel's file object (close the file).
[DllImport("kernel32", SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal extern static bool CloseHandle(IntPtr handle);
}
// The MyFileReader class is a sample class that accesses an operating system
// resource and implements IDisposable. This is useful to show the types of
// transformation required to make your resource wrapping classes
// more resilient. Note the Dispose and Finalize implementations.
// Consider this a simulation of System.IO.FileStream.
public class MyFileReader : IDisposable
{
// _handle is set to null to indicate disposal of this instance.
private MySafeFileHandle _handle;
public MyFileReader(String fileName)
{
// Security permission check.
String fullPath = Path.GetFullPath(fileName);
new FileIOPermission(FileIOPermissionAccess.Read, fullPath).Demand();
// Open a file, and save its handle in _handle.
// Note that the most optimized code turns into two processor
// instructions: 1) a call, and 2) moving the return value into
// the _handle field. With SafeHandle, the CLR's platform invoke
// marshaling layer will store the handle into the SafeHandle
// object in an atomic fashion. There is still the problem
// that the SafeHandle object may not be stored in _handle, but
// the real operating system handle value has been safely stored
// in a critical finalizable object, ensuring against leaking
// the handle even if there is an asynchronous exception.
MySafeFileHandle tmpHandle;
tmpHandle = NativeMethods.CreateFile(fileName, NativeMethods.GENERIC_READ,
FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
// An async exception here will cause us to run our finalizer with
// a null _handle, but MySafeFileHandle's ReleaseHandle code will
// be invoked to free the handle.
// This call to Sleep, run from the fault injection code in Main,
// will help trigger a race. But it will not cause a handle leak
// because the handle is already stored in a SafeHandle instance.
// Critical finalization then guarantees that freeing the handle,
// even during an unexpected AppDomain unload.
Thread.Sleep(500);
_handle = tmpHandle; // Makes _handle point to a critical finalizable object.
// Determine if file is opened successfully.
if (_handle.IsInvalid)
throw new Win32Exception(Marshal.GetLastWin32Error(), fileName);
}
public void Dispose() // Follow the Dispose pattern - public nonvirtual.
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
// No finalizer is needed. The finalizer on SafeHandle
// will clean up the MySafeFileHandle instance,
// if it hasn't already been disposed.
// However, there may be a need for a subclass to
// introduce a finalizer, so Dispose is properly implemented here.
protected virtual void Dispose(bool disposing)
{
// Note there are three interesting states here:
// 1) CreateFile failed, _handle contains an invalid handle
// 2) We called Dispose already, _handle is closed.
// 3) _handle is null, due to an async exception before
// calling CreateFile. Note that the finalizer runs
// if the constructor fails.
if (_handle != null && !_handle.IsInvalid)
{
// Free the handle
_handle.Dispose();
}
// SafeHandle records the fact that we've called Dispose.
}
public byte[] ReadContents(int length)
{
if (_handle.IsInvalid) // Is the handle disposed?
throw new ObjectDisposedException("FileReader is closed");
// This sample code will not work for all files.
byte[] bytes = new byte[length];
int numRead = 0;
int r = NativeMethods.ReadFile(_handle, bytes, length, out numRead, IntPtr.Zero);
// Since we removed MyFileReader's finalizer, we no longer need to
// call GC.KeepAlive here. Platform invoke will keep the SafeHandle
// instance alive for the duration of the call.
if (r == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
if (numRead < length)
{
byte[] newBytes = new byte[numRead];
Array.Copy(bytes, newBytes, numRead);
bytes = newBytes;
}
return bytes;
}
}
static class Program
{
// Testing harness that injects faults.
private static bool _printToConsole = false;
private static bool _workerStarted = false;
private static void Usage()
{
Console.WriteLine("Usage:");
// Assumes that application is named HexViewer"
Console.WriteLine("HexViewer <fileName> [-fault]");
Console.WriteLine(" -fault Runs hex viewer repeatedly, injecting faults.");
}
private static void ViewInHex(Object fileName)
{
_workerStarted = true;
byte[] bytes;
using (MyFileReader reader = new MyFileReader((String)fileName))
{
bytes = reader.ReadContents(20);
} // Using block calls Dispose() for us here.
if (_printToConsole)
{
// Print up to 20 bytes.
int printNBytes = Math.Min(20, bytes.Length);
Console.WriteLine("First {0} bytes of {1} in hex", printNBytes, fileName);
for (int i = 0; i < printNBytes; i++)
Console.Write("{0:x} ", bytes[i]);
Console.WriteLine();
}
}
static void Main(string[] args)
{
if (args.Length == 0 || args.Length > 2 ||
args[0] == "-?" || args[0] == "/?")
{
Usage();
return;
}
String fileName = args[0];
bool injectFaultMode = args.Length > 1;
if (!injectFaultMode)
{
_printToConsole = true;
ViewInHex(fileName);
}
else
{
Console.WriteLine("Injecting faults - watch handle count in perfmon (press Ctrl-C when done)");
int numIterations = 0;
while (true)
{
_workerStarted = false;
Thread t = new Thread(new ParameterizedThreadStart(ViewInHex));
t.Start(fileName);
Thread.Sleep(1);
while (!_workerStarted)
{
Thread.Sleep(0);
}
t.Abort(); // Normal applications should not do this.
numIterations++;
if (numIterations % 10 == 0)
GC.Collect();
if (numIterations % 10000 == 0)
Console.WriteLine(numIterations);
}
}
}
}
}
注釈
クラスは SafeHandle 、ハンドル リソースのクリティカルなファイナライズを提供し、ガベージ コレクションによってハンドルが途中で再利用されるのを防ぎ、意図しないアンマネージ オブジェクトを参照するために Windows によってリサイクルされないようにします。
このトピックのセクションは次のとおりです。
SafeHandle の理由SafeHandle から派生したクラスの SafeHandle の機能
SafeHandle の理由
.NET Framework バージョン 2.0 より前では、すべてのオペレーティング システム ハンドルをマネージド ラッパー オブジェクトにのみカプセル化IntPtrできました。 これはネイティブ コードと相互運用する便利な方法でしたが、スレッドが予期せず中止されたりスタック オーバーフローが発生したりするなどの非同期例外によってハンドルがリークされる可能性があります。 これらの非同期例外はオペレーティング システム リソースをクリーンアップするための障害であり、アプリのほぼすべての場所で発生する可能性があります。
メソッドを Object.Finalize オーバーライドすると、オブジェクトがガベージ コレクションされるときにアンマネージド リソースをクリーンアップできますが、場合によっては、プラットフォーム呼び出し呼び出し内でメソッドを実行しているときに、ガベージ コレクションによってファイナライズ可能なオブジェクトを再利用できます。 ファイナライザーによって、そのプラットフォーム呼び出しに渡されたハンドルが解放されると、処理が破損する可能性があります。 また、ファイルの読み取り中など、プラットフォーム呼び出しの呼び出し中にメソッドがブロックされている間にハンドルを再利用することもできます。
さらに重要なことに、Windows は積極的にハンドルをリサイクルするため、ハンドルをリサイクルし、機密データを含む可能性のある別のリソースを指す可能性があります。 これはリサイクル攻撃と呼ばれ、データが破損し、セキュリティ上の脅威になる可能性があります。
SafeHandle の機能
クラスは SafeHandle 、これらのオブジェクトの有効期間に関するいくつかの問題を簡略化し、オペレーティング システム リソースがリークされないようにプラットフォーム呼び出しと統合されています。 クラスは SafeHandle 、中断することなくハンドルを割り当てて解放することで、オブジェクトの有効期間の問題を解決します。 これには重要なファイナライザーが含まれており、プラットフォーム呼び出しが破損した状態であると見なされた場合でも、ハンドルが閉じられ、予期しない AppDomain アンロード中に実行されることが保証されます。
は からCriticalFinalizerObject継承されるためSafeHandle、クリティカルでないファイナライザーはすべて、クリティカル ファイナライザーの前に呼び出されます。 ファイナライザーは、同じガベージ コレクション パス中に存在しなくなったオブジェクトに対して呼び出されます。 たとえば、オブジェクトは FileStream 通常のファイナライザーを実行して、ハンドルがリークまたはリサイクルされるリスクなしに、既存のバッファー内のデータをフラッシュできます。 クリティカルファイナライザーと非クリティカル ファイナライザーの間のこの非常に弱い順序付けは、一般的な使用を目的としたものではありません。 主に、これらのライブラリがセマンティクスを変更せずに使用 SafeHandle できるようにすることで、既存のライブラリの移行を支援するために存在します。 さらに、クリティカル ファイナライザーと、メソッドなどの SafeHandle.ReleaseHandle() 呼び出しは、制約付き実行領域に存在する必要があります。 これにより、ファイナライザーの呼び出しグラフ内で記述できるコードに制約が課されます。
プラットフォーム呼び出し操作では、 によって SafeHandle カプセル化されたハンドルの参照カウントが自動的にインクリメントされ、完了時にそれらを減らします。 これにより、ハンドルが予期せずリサイクルされたり閉じたりすることがなくなります。
オブジェクトを構築するときに、基になるハンドルの所有権を SafeHandle 指定するには、クラス コンストラクターの 引数に ownsHandle
値を SafeHandle 指定します。 これにより、オブジェクトが破棄された後に SafeHandle オブジェクトがハンドルを解放するかどうかを制御します。 これは、固有の有効期間要件を持つハンドル、または他のユーザーによって有効期間が制御されているハンドルを使用する場合に便利です。
SafeHandle から派生したクラス
SafeHandle は、オペレーティング システム ハンドルの抽象ラッパー クラスです。 このクラスからの派生は困難です。 代わりに、次のセーフ ハンドルを提供する Microsoft.Win32.SafeHandles 名前空間の派生クラスを使用してください。
ファイル ( SafeFileHandle クラス)。
メモリ マップファイル ( SafeMemoryMappedFileHandle クラス)。
パイプ ( SafePipeHandle クラス)。
メモリ ビュー ( SafeMemoryMappedViewHandle クラス)。
暗号化コンストラクト (、SafeNCryptKeyHandle、SafeNCryptHandleSafeNCryptProviderHandle、および SafeNCryptSecretHandle クラス)。
プロセス ( SafeProcessHandle クラス)。
レジストリ キー ( SafeRegistryHandle クラス)。
待機ハンドル ( SafeWaitHandle クラス)。
注意 (実装者)
から SafeHandle派生したクラスを作成するには、オペレーティング システム ハンドルを作成して解放する方法を知っている必要があります。 このプロセスは、 CloseHandle 関数を使用するものもあれば、 UnmapViewOfFile や FindClose などのより具体的な関数を使用するものもあります。 このため、安全なハンドルでラップするオペレーティング システム ハンドルの SafeHandle 種類ごとに、 の派生クラスを作成する必要があります。
SafeHandle から継承する場合は、IsInvalid メンバーと ReleaseHandle() メンバーをオーバーライドする必要があります。
また、無効なハンドル値を表す値を使用して基本コンストラクターを呼び出すパブリック パラメーターなしのコンストラクターと Boolean 、 によってネイティブ ハンドルが所有 SafeHandle されているかどうかを示す値を指定し、破棄されたときに SafeHandle ネイティブ ハンドルを解放する必要があります。
コンストラクター
SafeHandle() |
オペレーティング システム ハンドルのラッパー クラスを表します。 このクラスは継承できません。 |
SafeHandle(IntPtr, Boolean) |
指定した無効なハンドル値を使用して、SafeHandle クラスの新しいインスタンスを初期化します。 |
フィールド
handle |
ラップするハンドルを指定します。 |
プロパティ
IsClosed |
ハンドルが閉じているかどうかを示す値を取得します。 |
IsInvalid |
派生クラスでオーバーライドされると、ハンドル値が無効かどうかを示す値を取得します。 |
メソッド
Close() |
リソースを解放するためのハンドルをマークします。 |
DangerousAddRef(Boolean) |
SafeHandle インスタンスの参照カウンターを手動でインクリメントします。 |
DangerousGetHandle() |
handle フィールドの値を戻します。 |
DangerousRelease() |
SafeHandle インスタンスの参照カウンターを手動でデクリメントします。 |
Dispose() |
SafeHandle クラスによって使用されているすべてのリソースを解放します。 |
Dispose(Boolean) |
通常の破棄操作を実行するかどうかを指定して、SafeHandle クラスによって使用されているアンマネージ リソースを解放します。 |
Equals(Object) |
指定されたオブジェクトが現在のオブジェクトと等しいかどうかを判断します。 (継承元 Object) |
Finalize() |
ハンドルに関連付けられたすべてのリソースを解放します。 |
GetHashCode() |
既定のハッシュ関数として機能します。 (継承元 Object) |
GetType() |
現在のインスタンスの Type を取得します。 (継承元 Object) |
MemberwiseClone() |
現在の Object の簡易コピーを作成します。 (継承元 Object) |
ReleaseHandle() |
派生クラスでオーバーライドされると、ハンドルを解放するために必要なコードを実行します。 |
SetHandle(IntPtr) |
ハンドルを指定した既存のハンドルに設定します。 |
SetHandleAsInvalid() |
今後は使用しないものとしてハンドルをマークします。 |
ToString() |
現在のオブジェクトを表す文字列を返します。 (継承元 Object) |