SafeHandle Класс

Определение

Представляет класс обертки для дескрипторов операционной системы.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. Если вы назначите имя приложения «Хексвиевер», использование командной строки будет следующим: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
Классы, производные от SafeHandleClasses 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.

Что делает SafeHandleWhat 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.

Классы, производные от SafeHandleClasses 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 или финдклосе.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().

Кроме того, необходимо предоставить конструктор без параметров, который вызывает базовый конструктор со значением, представляющим недопустимое значение Handle, и 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(IntPtr, Boolean)

Инициализирует новый экземпляр класса SafeHandle с заданным значением недопустимого дескриптора.Initializes a new instance of the SafeHandle class with the specified invalid handle value.

Поля

handle

Определяет инкапсулируемый дескриптор.Specifies the handle to be wrapped.

Свойства

IsClosed

Получает значение, показывающее, является ли дескриптор закрытым.Gets a value indicating whether the handle is closed.

IsInvalid

При переопределении в производном классе получает значение, показывающее, допустимо ли значение дескриптора.When overridden in a derived class, gets a value indicating whether the handle value is invalid.

Методы

Close()

Помечает дескриптор для освобождения самого дескриптора и соответствующих ресурсов.Marks the handle for releasing and freeing resources.

DangerousAddRef(Boolean)

Вручную увеличивает счетчик ссылок для экземпляров SafeHandle.Manually increments the reference counter on SafeHandle instances.

DangerousGetHandle()

Возвращает значение поля handle.Returns the value of the handle field.

DangerousRelease()

Вручную уменьшает счетчик ссылок для экземпляра SafeHandle.Manually decrements the reference counter on a SafeHandle instance.

Dispose()

Освобождает все ресурсы, используемые классом SafeHandle.Releases all resources used by the SafeHandle class.

Dispose(Boolean)

Освобождает неуправляемые ресурсы, используемые классом SafeHandle, определяя, нужно ли выполнять обычную операцию удаления.Releases the unmanaged resources used by the SafeHandle class specifying whether to perform a normal dispose operation.

Equals(Object)

Определяет, равен ли заданный объект текущему объекту.Determines whether the specified object is equal to the current object.

(Унаследовано от Object)
Finalize()

Освобождает все ресурсы, связанные с дескриптором.Frees all resources associated with the handle.

GetHashCode()

Служит хэш-функцией по умолчанию.Serves as the default hash function.

(Унаследовано от Object)
GetType()

Возвращает объект Type для текущего экземпляра.Gets the Type of the current instance.

(Унаследовано от Object)
MemberwiseClone()

Создает неполную копию текущего объекта Object.Creates a shallow copy of the current Object.

(Унаследовано от Object)
ReleaseHandle()

При переопределении в производном классе выполняет код, необходимый для освобождения дескриптора.When overridden in a derived class, executes the code required to free the handle.

SetHandle(IntPtr)

Определяет дескриптор для заданного ранее существующего дескриптора.Sets the handle to the specified pre-existing handle.

SetHandleAsInvalid()

Помечает дескриптор, как больше не используемый.Marks a handle as no longer used.

ToString()

Возвращает строку, представляющую текущий объект.Returns a string that represents the current object.

(Унаследовано от 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.

Применяется к

Дополнительно