SafeHandle SafeHandle SafeHandle SafeHandle Class

定義

オペレーティング システム ハンドルのラッパー クラスを表します。Represents a wrapper class for operating system handles. このクラスは継承する必要があります。This class must be inherited.

public ref class SafeHandle abstract : System::Runtime::ConstrainedExecution::CriticalFinalizerObject, IDisposable
[System.Security.SecurityCritical]
public abstract class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
type SafeHandle = class
    inherit CriticalFinalizerObject
    interface IDisposable
Public MustInherit Class SafeHandle
Inherits CriticalFinalizerObject
Implements IDisposable
継承
派生
属性
実装

次のコード例では、からSafeHandleZeroOrMinusOneIsInvalid派生するオペレーティングシステムファイルハンドルのカスタムセーフハンドルを作成します。The following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. ファイルからバイトを読み取り、16進値を表示します。It reads bytes from a file and displays their hexadecimal values. また、スレッドを中止する障害テストハーネスも含まれていますが、ハンドル値は解放されます。It also contains a fault testing harness that causes the thread to abort, but the handle value is freed. を使用しIntPtrてハンドルを表す場合、非同期スレッドの中止によってハンドルがリークすることがあります。When using an IntPtr to represent handles, the handle is occasionally leaked due to the asynchronous thread abort.

コンパイル済みアプリケーションと同じフォルダーにテキストファイルが必要になります。You will need a text file in the same folder as the compiled application. アプリケーションに "HexViewer" という名前を指定した場合、コマンドラインの使用法は次のようになります。Assuming that you name the application "HexViewer", the command line usage is:

HexViewer <filename> -Fault

必要に-Fault応じて、特定のウィンドウでスレッドを中止してハンドルのリークを意図的に試みるように指定します。Optionally specify -Fault to intentionally attempt to leak the handle by aborting the thread in a certain window. Windows Perfmon.exe ツールを使用して、フォールトを挿入するときのハンドル数を監視します。Use the Windows Perfmon.exe tool to monitor handle counts while injecting faults.

using System;
using System.Runtime.InteropServices;
using System.IO;
using System.ComponentModel;
using System.Security.Permissions;
using System.Security;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Runtime.ConstrainedExecution;

namespace SafeHandleDemo
{
    [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
    [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
    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(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.
        // Howerver, there may be a need for a subclass to
        // introduce a finalizer, so Dispose is properly implemented here.
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        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.
        }


        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        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 HexViwer"
            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 によって意図しないアンマネージオブジェクトを参照することを防止できます。The SafeHandle class provides critical finalization of handle resources, preventing handles from being reclaimed prematurely by garbage collection and from being recycled by Windows to reference unintended unmanaged objects.

このトピックには、次のセクションがあります。This topic includes the following sections:

SafeHandle の理由 Why SafeHandle?
SafeHandle の機能 What SafeHandle does
SafeHandle から派生したクラスClasses derived from SafeHandle

SafeHandle の理由Why SafeHandle?

.NET Framework バージョン2.0 より前では、すべてのオペレーティングシステムハンドルをIntPtrマネージラッパーオブジェクトにカプセル化することしかできませんでした。Before the .NET Framework version 2.0, all operating system handles could only be encapsulated in the IntPtr managed wrapper object. これはネイティブコードとの相互運用に便利な方法ですが、予期しないスレッドの中止やスタックオーバーフローなどの非同期例外によってハンドルがリークする可能性があります。While this was a convenient way to interoperate with native code, handles could be leaked by asynchronous exceptions, such as a thread aborting unexpectedly or a stack overflow. これらの非同期例外は、オペレーティングシステムのリソースをクリーンアップするのには問題があり、アプリ内のほぼすべての場所で発生する可能性があります。These asynchronous exceptions are an obstacle to cleaning up operating system resources, and they can occur almost anywhere in your app.

Object.Finalizeメソッドをオーバーライドすると、オブジェクトがガベージコレクションされるときにアンマネージリソースのクリーンアップを許可しますが、場合によっては、プラットフォーム呼び出し内でメソッドを実行している間に、ファイナライズ可能なオブジェクトをガベージコレクションによって再利用できます。発信.Although overrides to the Object.Finalize method allow cleanup of unmanaged resources when an object is being garbage collected, in some circumstances, finalizable objects can be reclaimed by garbage collection while executing a method within a platform invoke call. ファイナライザーがそのプラットフォーム呼び出しに渡されたハンドルを解放すると、処理が破損する可能性があります。If a finalizer frees the handle passed to that platform invoke call, it could lead to handle corruption. また、ファイルの読み取り中など、プラットフォーム呼び出しの間にメソッドがブロックされている間に、ハンドルを再利用することもできます。The handle could also be reclaimed while your method is blocked during a platform invoke call, such as while reading a file.

Windows が積極的にハンドルをリサイクルするため、ハンドルがリサイクルされ、機密データが含まれている可能性のある別のリソースを指している可能性があります。More critically, because Windows aggressively recycles handles, a handle could be recycled and point to another resource that might contain sensitive data. これはリサイクル攻撃と呼ばれ、データが破壊され、セキュリティ上の脅威となる可能性があります。This is known as a recycle attack and can potentially corrupt data and be a security threat.

SafeHandle の機能What SafeHandle does

クラスSafeHandleは、これらのオブジェクトの有効期間の問題のいくつかを簡略化し、オペレーティングシステムのリソースがリークしないように、プラットフォーム呼び出しに統合されています。The SafeHandle class simplifies several of these object lifetime issues, and is integrated with platform invoke so that operating system resources are not leaked. クラスSafeHandleは、中断せずにハンドルの割り当てと解放を行うことによって、オブジェクトの有効期間の問題を解決します。The SafeHandle class resolves object lifetime issues by assigning and releasing handles without interruption. これには、プラットフォーム呼び出しが破損したと見なされる場合でも、ハンドルがAppDomain閉じられ、予期しないアンロード中に確実に実行されることを保証するクリティカルファイナライザーが含まれます。It contains a critical finalizer that ensures that the handle is closed and is guaranteed to run during unexpected AppDomain unloads, even in cases when the platform invoke call is assumed to be in a corrupted state.

SafeHandle からCriticalFinalizerObject継承されるため、すべての重要でないファイナライザーは、重要なファイナライザーの前に呼び出されます。Because SafeHandle inherits from CriticalFinalizerObject, all the noncritical finalizers are called before any of the critical finalizers. ファイナライザーは、同じガベージコレクションパスの間に存在しなくなったオブジェクトに対して呼び出されます。The finalizers are called on objects that are no longer live during the same garbage collection pass. たとえば、オブジェクトはFileStream通常のファイナライザーを実行して、ハンドルがリークしたりリサイクルされたりすることなく、既存のバッファーデータをフラッシュすることができます。For example, a FileStream object can run a normal finalizer to flush out existing buffered data without the risk of the handle being leaked or recycled. クリティカルファイナライザーとクリティカルでないファイナライザーとの間の非常に弱い順序は、一般的な使用を目的としたものではありません。This very weak ordering between critical and noncritical finalizers is not intended for general use. これは主に、既存のライブラリを移行する際に、セマンティクスを変更SafeHandleせずに使用できるようにするために存在します。It exists primarily to assist in the migration of existing libraries by allowing those libraries to use SafeHandle without altering their semantics. さらに、クリティカルなファイナライザーと、このSafeHandle.ReleaseHandle()メソッドが呼び出すすべてのものは、制約された実行領域に存在する必要があります。Additionally, the critical finalizer and anything it calls, such as the SafeHandle.ReleaseHandle() method, must be in a constrained execution region. これにより、ファイナライザーの呼び出しグラフ内に記述できるコードに制約が課されます。This imposes constraints on what code can be written within the finalizer's call graph.

プラットフォーム呼び出し操作は、 SafeHandleによってカプセル化されたハンドルの参照カウントを自動的にインクリメントし、完了時にデクリメントします。Platform invoke operations automatically increment the reference count of handles encapsulated by a SafeHandle and decrement them upon completion. これにより、ハンドルが予期せずリサイクルまたは閉じられることがなくなります。This ensures that the handle will not be recycled or closed unexpectedly.

クラスコンストラクターのSafeHandle ownsHandle引数に値を指定することによって、オブジェクトを構築するときに、基になるハンドルの所有権を指定できます。 SafeHandleYou can specify ownership of the underlying handle when constructing SafeHandle objects by supplying a value to the ownsHandle argument in the SafeHandle class constructor. オブジェクトが破棄さSafeHandleれた後に、オブジェクトがハンドルを解放するかどうかを制御します。This controls whether the SafeHandle object will release the handle after the object has been disposed. これは、特別な有効期間要件を持つハンドルや、有効期間が他のユーザーによって制御されるハンドルを使用する場合に便利です。This is useful for handles with peculiar lifetime requirements or for consuming a handle whose lifetime is controlled by someone else.

SafeHandle から派生したクラスClasses derived from SafeHandle

SafeHandleは、オペレーティングシステムハンドルの抽象ラッパークラスです。SafeHandle is an abstract wrapper class for operating system handles. このクラスからの派生は困難です。Deriving from this class is difficult. 代わりに、次のセーフ ハンドルを提供する Microsoft.Win32.SafeHandles 名前空間の派生クラスを使用してください。Instead, use the derived classes in the Microsoft.Win32.SafeHandles namespace that provide safe handles for the following:

注意 (継承者)

からSafeHandle派生したクラスを作成するには、オペレーティングシステムハンドルの作成方法と解放方法を把握しておく必要があります。To create a class derived from SafeHandle, you must know how to create and free an operating system handle. このプロセスは、 CloseHandle関数を使用する場合や、 Unmapviewoffile にfindcloseなどのより具体的な関数を使用する場合があるので、ハンドルの種類によって異なります。This process is different for different handle types because some use the CloseHandle function, while others use more specific functions such as UnmapViewOfFile or FindClose. このため、セーフハンドルでラップするオペレーティングシステムハンドルSafeHandleの種類ごとに、の派生クラスを作成する必要があります。For this reason, you must create a derived class of SafeHandle for each operating system handle type that you want to wrap in a safe handle.

SafeHandle から継承する場合は、IsInvalid メンバーと ReleaseHandle() メンバーをオーバーライドする必要があります。When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle().

また、無効なハンドル値を表す値を使用して基底コンストラクターを呼び出すパラメーターなしのコンストラクターとBoolean 、ネイティブハンドルがにSafeHandleよって所有され、その結果解放される必要があるかどうかを示す値を指定する必要があります。SafeHandleこのが破棄された場合。You should also provide a parameterless constructor that calls the base constructor with a value that represent an invalid handle value, and a Boolean value indicating whether the native handle is owned by the SafeHandle and consequently should be freed when that SafeHandle has been disposed.

コンストラクター

SafeHandle() SafeHandle() SafeHandle() SafeHandle()
SafeHandle(IntPtr, Boolean) SafeHandle(IntPtr, Boolean) SafeHandle(IntPtr, Boolean) SafeHandle(IntPtr, Boolean)

指定した無効なハンドル値を使用して、SafeHandle クラスの新しいインスタンスを初期化します。Initializes a new instance of the SafeHandle class with the specified invalid handle value.

フィールド

handle handle handle handle

ラップするハンドルを指定します。Specifies the handle to be wrapped.

プロパティ

IsClosed IsClosed IsClosed IsClosed

ハンドルが閉じているかどうかを示す値を取得します。Gets a value indicating whether the handle is closed.

IsInvalid IsInvalid IsInvalid IsInvalid

派生クラスでオーバーライドされると、ハンドル値が無効かどうかを示す値を取得します。When overridden in a derived class, gets a value indicating whether the handle value is invalid.

メソッド

Close() Close() Close() Close()

リソースを解放するためのハンドルをマークします。Marks the handle for releasing and freeing resources.

DangerousAddRef(Boolean) DangerousAddRef(Boolean) DangerousAddRef(Boolean) DangerousAddRef(Boolean)

SafeHandle インスタンスの参照カウンターを手動でインクリメントします。Manually increments the reference counter on SafeHandle instances.

DangerousGetHandle() DangerousGetHandle() DangerousGetHandle() DangerousGetHandle()

handle フィールドの値を返します。Returns the value of the handle field.

DangerousRelease() DangerousRelease() DangerousRelease() DangerousRelease()

SafeHandle インスタンスの参照カウンターを手動でデクリメントします。Manually decrements the reference counter on a SafeHandle instance.

Dispose() Dispose() Dispose() Dispose()

SafeHandle クラスによって使用されているすべてのリソースを解放します。Releases all resources used by the SafeHandle class.

Dispose(Boolean) Dispose(Boolean) Dispose(Boolean) Dispose(Boolean)

通常の破棄操作を実行するかどうかを指定して、SafeHandle クラスによって使用されているアンマネージ リソースを解放します。Releases the unmanaged resources used by the SafeHandle class specifying whether to perform a normal dispose operation.

Equals(Object) Equals(Object) Equals(Object) Equals(Object)

指定したオブジェクトが、現在のオブジェクトと等しいかどうかを判断します。Determines whether the specified object is equal to the current object.

(Inherited from Object)
Finalize() Finalize() Finalize() Finalize()

ハンドルに関連付けられたすべてのリソースを解放します。Frees all resources associated with the handle.

GetHashCode() GetHashCode() GetHashCode() GetHashCode()

既定のハッシュ関数として機能します。Serves as the default hash function.

(Inherited from Object)
GetType() GetType() GetType() GetType()

現在のインスタンスの Type を取得します。Gets the Type of the current instance.

(Inherited from Object)
MemberwiseClone() MemberwiseClone() MemberwiseClone() MemberwiseClone()

現在の Object の簡易コピーを作成します。Creates a shallow copy of the current Object.

(Inherited from Object)
ReleaseHandle() ReleaseHandle() ReleaseHandle() ReleaseHandle()

派生クラスでオーバーライドされると、ハンドルを解放するために必要なコードを実行します。When overridden in a derived class, executes the code required to free the handle.

SetHandle(IntPtr) SetHandle(IntPtr) SetHandle(IntPtr) SetHandle(IntPtr)

ハンドルを指定した既存のハンドルに設定します。Sets the handle to the specified pre-existing handle.

SetHandleAsInvalid() SetHandleAsInvalid() SetHandleAsInvalid() SetHandleAsInvalid()

今後は使用しないものとしてハンドルをマークします。Marks a handle as no longer used.

ToString() ToString() ToString() ToString()

現在のオブジェクトを表す文字列を返します。Returns a string that represents the current object.

(Inherited from Object)

セキュリティ

InheritanceDemand
継承者に対する完全な信頼の場合。for full trust for inheritors. このメンバーは、部分的に信頼されたコードによって継承することはできません。This member cannot be inherited by partially trusted code.

SecurityCriticalAttribute
直前の呼び出し元に完全信頼が必要です。requires full trust for the immediate caller. このメンバーは、部分的に信頼されているコードまたは透過的なコードでは使用できません。This member cannot be used by partially trusted or transparent code.

適用対象

こちらもご覧ください