クリックして評価とフィードバックをお寄せください
MSDN
MSDN ライブラリ
.NET 開発
.NET Framework 3.5
.NET Framework 3.5
SafeHandle クラス

  低帯域幅での表示をオンにする
このページは次のバージョンについて記述しています。
Microsoft Visual Studio 2008/.NET Framework 3.5

その他のバージョンについては、以下の情報を参照してください。
.NET Framework クラス ライブラリ
SafeHandle クラス

更新 : 2007 年 11 月

オペレーティング システム ハンドルのラッパー クラスを表します。このクラスは継承元として使用する必要があります。

名前空間 :  System.Runtime.InteropServices
アセンブリ :  mscorlib (mscorlib.dll 内)

Visual Basic (宣言)
<SecurityPermissionAttribute(SecurityAction.LinkDemand, UnmanagedCode := True)> _
<SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode := True)> _
Public MustInherit Class SafeHandle _
    Inherits CriticalFinalizerObject _
    Implements IDisposable
Visual Basic (使用法)
Dim instance As SafeHandle
C#
[SecurityPermissionAttribute(SecurityAction.LinkDemand, UnmanagedCode = true)]
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
public abstract class SafeHandle : CriticalFinalizerObject, 
    IDisposable
Visual C++
[SecurityPermissionAttribute(SecurityAction::LinkDemand, UnmanagedCode = true)]
[SecurityPermissionAttribute(SecurityAction::InheritanceDemand, UnmanagedCode = true)]
public ref class SafeHandle abstract : public CriticalFinalizerObject, 
    IDisposable
J#
/** @attribute SecurityPermissionAttribute(SecurityAction.LinkDemand, UnmanagedCode = true) */
/** @attribute SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode = true) */
public abstract class SafeHandle extends CriticalFinalizerObject implements IDisposable
JScript
public abstract class SafeHandle extends CriticalFinalizerObject implements IDisposable

SafeHandle クラスはハンドル リソースのクリティカル ファイナライズを提供し、ガベージ コレクションでハンドルが早期に再要求されることや、Windows によるリサイクルで意図しないアンマネージ オブジェクトが参照されることを防ぎます。.NET Framework Version 2.0 よりも前は、すべてのオペレーティング システム ハンドルは、IntPtr マネージ ラッパー オブジェクトにカプセル化することしかできませんでした。

SafeHandle クラスには、ハンドルのクローズを保証するファイナライザが含まれており、実行が保証されています。これは、ホストが AppDomain の状態の一貫性を信頼できないときに、AppDomain が予想外にアンロードされる場合でも適用されます。

SafeHandle を使用する利点については、「セーフ ハンドルとクリティカル ファイナライズ」を参照してください。

汎用ハンドルを作成することはできないため、このクラスは抽象クラスです。SafeHandle を実装するには、派生クラスを作成する必要があります。SafeHandle の派生クラスを作成するには、オペレーティング システム ハンドルを作成し、解放する方法を把握している必要があります。CloseHandle を使用する場合もあれば、UnmapViewOfFileFindClose などの特定のメソッドを使用する場合もあるため、このプロセスはハンドルの型ごとに異なります。このため、オペレーティング システム ハンドルの型 ( MySafeRegistryHandleMySafeFileHandleMySpecialSafeFileHandle など) ごとに、SafeHandle の派生クラスを作成する必要があります。これらの派生クラスの一部は、Microsoft.Win32.SafeHandles 名前空間にあらかじめ作成され提供されています。

メモ :

SafeHandle から派生した独自のクラスの作成は高度なプログラミング技術です。SafeHandle から派生した作成済みの一連のクラスは、抽象派生クラスとして提供され、Microsoft.Win32.SafeHandles 名前空間にあります。これらのクラスは、ファイル ハンドルとオペレーティング システム ハンドルをサポートする共通機能を提供するように設計されています。

継承元へのメモ :

SafeHandle から継承する場合は、IsInvalid および ReleaseHandle の両メンバをオーバーライドする必要があります。

また、無効なハンドル値を表す値、およびネイティブ ハンドルが SafeHandle によって所有され、SafeHandle が破棄されたときに最終的に解放されるかどうかを示す Boolean 値を指定して基本コンストラクタを呼び出す、既定のコンストラクタを提供することも必要です。

次のコード例では、SafeHandleZeroOrMinusOneIsInvalid から派生して、オペレーティング システムのファイル ハンドルのカスタム セーフ ハンドルを作成します。この例では、ファイルからバイト データを読み取り、その 16 進値を表示します。また、スレッドを中止させる障害テスト ハーネスも含まれていますが、このハーネスを使用する場合はハンドル値は解放されます。IntPtr を使用してハンドルを表すと、非同期のスレッド中止のためにハンドルがリークすることがあります。

コンパイルしたアプリケーションと同じフォルダにテキスト ファイルが必要になります。アプリケーションの名前が "HexViewer" だとすると、コマンド ラインの使用方法は次のようになります。

HexViewer <filename> -Fault

オプションで -Fault を指定し、特定のウィンドウでスレッドを中止することで、意図的にハンドルのリークを発生させることができます。Windows の Perform.exe ツールを使用して、障害を強制的に発生させたときのハンドルのカウントを監視します。

C#
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);
                }
            }

        }
    }
}

この型のすべてのパブリック static (Visual Basic では Shared) メンバは、スレッド セーフです。インスタンス メンバの場合は、スレッド セーフであるとは限りません。

Windows Vista, Windows XP SP2, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP Starter Edition, Windows Server 2003, Windows Server 2000 SP4, Windows Millennium Edition, Windows 98

.NET Framework および .NET Compact Framework では、各プラットフォームのすべてのバージョンはサポートしていません。サポートされているバージョンについては、「.NET Framework システム要件」を参照してください。

.NET Framework

サポート対象 : 3.5、3.0、2.0
コミュニティ コンテンツ   コミュニティ コンテンツとは
新しいコンテンツの追加 RSS  注釈
Processing
© 2009 Microsoft Corporation. All rights reserved. 使用条件  |  商標  |  プライバシー
Page view tracker