SafeHandle 类

定义

表示操作系统句柄的包装类。 必须继承此类。

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操作系统文件句柄创建自定义安全句柄。 它从文件读取字节并显示其十六进制值。 它还包含导致线程中止但释放句柄值的错误测试工具。 使用 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);
                }
            }
        }
    }
}

注解

有关此 API 的详细信息,请参阅 SafeHandle 的补充 API 说明

实施者说明

若要创建派生自 SafeHandle的类,必须了解如何创建和释放操作系统句柄。 此过程对于不同的句柄类型是不同的,因为有些人使用 [CloseHandle] (/windows/win32/api/handleapi/nf-handleapi-closehandle) 函数, 而其他函数则使用 [UnmapViewOfFile] (/windows/win32/api/memoryapi/nf-memoryapi-unmapviewoffile) 或 [FindClose] (/windows/win32/api/fileapi/nf-fileapi-findclose) 。 出于此原因,必须为每个要包装在安全句柄中的操作系统句柄类型创建派生类 SafeHandle

当从 SafeHandle 继承时,必须重写下面的成员:IsInvalidReleaseHandle()

还应提供一个公共无参数构造函数,该构造函数使用表示无效句柄值的值调用基构造函数,以及一个Boolean值,该值指示本机句柄是否由 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)

适用于

另请参阅