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. 它从文件中读取字节并显示其十六进制值。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 工具监视句柄计数,同时注入错误。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. 如果终结器释放传递到该平台 invoke 调用的句柄,则可能会导致句柄损坏。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函数,而另一些则使用更具体的函数,例如UnmapViewOfFileFindCloseThis 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 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 类所使用的非托管资源,并指定是否执行常规释放 (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.

适用于

另请参阅