SafeHandle SafeHandle SafeHandle SafeHandle Class

定義

表示作業系統控制代碼的包裝函式類別 (Wrapper 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
繼承
衍生
屬性
實作

範例

下列程式碼範例會建立自訂的安全控制代碼,用於作業系統檔案控制代碼,衍生自SafeHandleZeroOrMinusOneIsInvalidThe following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. 它會從檔案讀取位元組,並顯示其十六進位值。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 參考非預期的 unmanaged 的物件被回收。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 版中,所有的作業系統控制代碼無法僅封裝在之前IntPtrmanaged 包裝函式物件。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方法允許清除 unmanaged 資源,當物件已進行記憶體回收,在某些情況下,透過記憶體回收執行平台內的方法時,可以回收最終處理物件叫用呼叫。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中的引數SafeHandle類別建構函式。You 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函式,而其他人使用更特定的函式,例如UnmapViewOfFileFindClose.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 時,您必須覆寫下列成員:IsInvalidReleaseHandle()When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle().

您也應該提供值,表示無效的控制代碼的值,這個值與基底建構函式會呼叫預設建構函式和Boolean值,指出是否已擁有原生控制代碼SafeHandle,因此應該被釋放時SafeHandle已經過處置。You should also provide a default 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 類別所使用的 Unmanaged 資源,指定是否要執行一般處置 (Dispose) 作業。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()

取得目前執行個體的 TypeGets 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.

適用於

另請參閱