SafeHandle SafeHandle SafeHandle SafeHandle Class

Definição

Representa uma classe wrapper para identificadores do sistema operacional.Represents a wrapper class for operating system handles. Essa classe deve ser herdada.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
Herança
Derivado
Atributos
Implementações

Exemplos

O exemplo de código a seguir cria um identificador de segurança personalizado para um identificador de arquivo do sistema SafeHandleZeroOrMinusOneIsInvalidoperacional, derivado de.The following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. Ele lê bytes de um arquivo e exibe seus valores hexadecimais.It reads bytes from a file and displays their hexadecimal values. Ele também contém um equipamento de teste de falha que faz com que o thread seja anulado, mas o valor do identificador é liberado.It also contains a fault testing harness that causes the thread to abort, but the handle value is freed. Ao usar um IntPtr para representar identificadores, o identificador é ocasionalmente vazado devido à anulação de thread assíncrona.When using an IntPtr to represent handles, the handle is occasionally leaked due to the asynchronous thread abort.

Você precisará de um arquivo de texto na mesma pasta que o aplicativo compilado.You will need a text file in the same folder as the compiled application. Supondo que você nomeie o aplicativo como "HexViewer", o uso da linha de comando é:Assuming that you name the application "HexViewer", the command line usage is:

HexViewer <filename> -Fault

Opcionalmente, -Fault especifique para tentar vazar intencionalmente o identificador anulando o thread em uma determinada janela.Optionally specify -Fault to intentionally attempt to leak the handle by aborting the thread in a certain window. Use a ferramenta Windows PerfMon. exe para monitorar contagens de identificadores ao injetar falhas.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);
                }
            }

        }
    }
}

Comentários

A SafeHandle classe fornece a finalização crítica dos recursos de identificador, impedindo que os identificadores sejam recuperados prematuramente pela coleta de lixo e sejam reciclados pelo Windows para fazer referência a objetos não gerenciados indesejados.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.

Este tópico inclui as seções a seguir:This topic includes the following sections:

Por que o SafeHandle? Why SafeHandle?
O que o SafeHandle faz What SafeHandle does
Classes derivadas de SafeHandleClasses derived from SafeHandle

Por que o SafeHandle?Why SafeHandle?

Antes da versão .NET Framework 2,0, todos os identificadores do sistema operacional podiam ser encapsulados IntPtr apenas no objeto wrapper gerenciado.Before the .NET Framework version 2.0, all operating system handles could only be encapsulated in the IntPtr managed wrapper object. Embora essa seja uma maneira conveniente de interoperar com código nativo, os identificadores poderiam ser vazados por exceções assíncronas, como um thread anulando inesperadamente ou um estouro de pilha.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. Essas exceções assíncronas são um obstáculo para a limpeza de recursos do sistema operacional e podem ocorrer em praticamente qualquer lugar em seu aplicativo.These asynchronous exceptions are an obstacle to cleaning up operating system resources, and they can occur almost anywhere in your app.

Embora as substituições Object.Finalize para o método permitam a limpeza de recursos não gerenciados quando um objeto está sendo coletado pelo lixo, em algumas circunstâncias, os objetos finalizáveis podem ser recuperados pela coleta de lixo ao executar um método dentro de uma invocação de plataforma ligação.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. Se um finalizador liberar o identificador passado para a chamada de invocação de plataforma, ele poderá levar ao tratamento de corrupção.If a finalizer frees the handle passed to that platform invoke call, it could lead to handle corruption. O identificador também pode ser recuperado enquanto o método é bloqueado durante uma chamada de invocação de plataforma, como durante a leitura de um arquivo.The handle could also be reclaimed while your method is blocked during a platform invoke call, such as while reading a file.

De forma mais crítica, como o Windows recicla manipuladores agressivamente, um identificador pode ser reciclado e apontar para outro recurso que pode conter dados confidenciais.More critically, because Windows aggressively recycles handles, a handle could be recycled and point to another resource that might contain sensitive data. Isso é conhecido como um ataque de reciclagem e pode potencialmente corromper dados e ser uma ameaça à segurança.This is known as a recycle attack and can potentially corrupt data and be a security threat.

O que o SafeHandle fazWhat SafeHandle does

A SafeHandle classe simplifica vários desses problemas de tempo de vida de objeto e é integrada à invocação de plataforma para que os recursos do sistema operacional não sejam vazados.The SafeHandle class simplifies several of these object lifetime issues, and is integrated with platform invoke so that operating system resources are not leaked. A SafeHandle classe resolve problemas de tempo de vida do objeto atribuindo e liberando identificadores sem interrupção.The SafeHandle class resolves object lifetime issues by assigning and releasing handles without interruption. Ele contém um finalizador crítico que garante que o identificador seja fechado e seja garantido que seja executado durante AppDomain descargas inesperadas, mesmo em casos em que a chamada de invocação de plataforma seja considerada em um estado corrompido.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.

Como SafeHandle o herda CriticalFinalizerObjectde, todos os finalizadores não críticos são chamados antes de qualquer um dos finalizadores críticos.Because SafeHandle inherits from CriticalFinalizerObject, all the noncritical finalizers are called before any of the critical finalizers. Os finalizadores são chamados em objetos que não estão mais ativos durante a mesma passagem de coleta de lixo.The finalizers are called on objects that are no longer live during the same garbage collection pass. Por exemplo, um FileStream objeto pode executar um finalizador normal para liberar dados existentes em buffer sem o risco de o identificador ser vazado ou reciclado.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. Essa ordenação muito fraca entre os finalizadores críticos e não críticos não se destina ao uso geral.This very weak ordering between critical and noncritical finalizers is not intended for general use. Ele existe principalmente para auxiliar na migração de bibliotecas existentes, permitindo que essas bibliotecas sejam usadas SafeHandle sem alterar sua semântica.It exists primarily to assist in the migration of existing libraries by allowing those libraries to use SafeHandle without altering their semantics. Além disso, o finalizador crítico e qualquer coisa que ele chama, SafeHandle.ReleaseHandle() como o método, deve estar em uma região de execução restrita.Additionally, the critical finalizer and anything it calls, such as the SafeHandle.ReleaseHandle() method, must be in a constrained execution region. Isso impõe restrições sobre o código que pode ser gravado no grafo de chamada do finalizador.This imposes constraints on what code can be written within the finalizer's call graph.

As operações de invocação de plataforma incrementam automaticamente a contagem de referência SafeHandle de identificadores encapsulados por um e os decrementam após a conclusão.Platform invoke operations automatically increment the reference count of handles encapsulated by a SafeHandle and decrement them upon completion. Isso garante que o identificador não será reciclado ou fechado inesperadamente.This ensures that the handle will not be recycled or closed unexpectedly.

Você pode especificar a propriedade do identificador subjacente ao construir SafeHandle objetos fornecendo um valor para o ownsHandle argumento no construtor de SafeHandle classe.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. Isso controla se o SafeHandle objeto liberará o identificador depois que o objeto tiver sido descartado.This controls whether the SafeHandle object will release the handle after the object has been disposed. Isso é útil para identificadores com requisitos de tempo de vida peculiar ou para consumir um identificador cujo tempo de vida é controlado por outra pessoa.This is useful for handles with peculiar lifetime requirements or for consuming a handle whose lifetime is controlled by someone else.

Classes derivadas de SafeHandleClasses derived from SafeHandle

SafeHandleé uma classe de wrapper abstrata para identificadores do sistema operacional.SafeHandle is an abstract wrapper class for operating system handles. A derivação dessa classe é difícil.Deriving from this class is difficult. Em vez disso, use as classes derivadas no espaço Microsoft.Win32.SafeHandles que fornecem manipuladores de segurança para o seguinte:Instead, use the derived classes in the Microsoft.Win32.SafeHandles namespace that provide safe handles for the following:

Notas aos Herdeiros

Para criar uma classe derivada do SafeHandle, você deve saber como criar e liberar um identificador do sistema operacional.To create a class derived from SafeHandle, you must know how to create and free an operating system handle. Esse processo é diferente para diferentes tipos de identificadores porque alguns usam a função CloseHandle , enquanto outros usam funções mais específicas, como UnmapViewOfFile ou FindClose.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. Por esse motivo, você deve criar uma classe derivada de SafeHandle para cada tipo de identificador do sistema operacional que você deseja encapsular em um identificador seguro.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.

Ao herdar do SafeHandle, você deve substituir os seguintes membros: IsInvalid e ReleaseHandle().When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle().

Você também deve fornecer um construtor sem parâmetros que chama o construtor base com um valor que representa um valor de identificador inválido e um Boolean valor que indica se o identificador nativo pertence ao SafeHandle e, consequentemente, deve ser liberado Quando isso SafeHandle foi Descartado.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.

Construtores

SafeHandle() SafeHandle() SafeHandle() SafeHandle()
SafeHandle(IntPtr, Boolean) SafeHandle(IntPtr, Boolean) SafeHandle(IntPtr, Boolean) SafeHandle(IntPtr, Boolean)

Inicializa uma nova instância da classe SafeHandle com o valor do identificador inválido especificado.Initializes a new instance of the SafeHandle class with the specified invalid handle value.

Campos

handle handle handle handle

Especifica o identificador a ser encapsulado.Specifies the handle to be wrapped.

Propriedades

IsClosed IsClosed IsClosed IsClosed

Obtém um valor que indica se o identificador está fechado.Gets a value indicating whether the handle is closed.

IsInvalid IsInvalid IsInvalid IsInvalid

Quando substituído em uma classe derivada, obtém um valor que indica se o valor de indicador é válido.When overridden in a derived class, gets a value indicating whether the handle value is invalid.

Métodos

Close() Close() Close() Close()

Marca o identificador de liberação de recursos.Marks the handle for releasing and freeing resources.

DangerousAddRef(Boolean) DangerousAddRef(Boolean) DangerousAddRef(Boolean) DangerousAddRef(Boolean)

Incrementa manualmente o contador de referências em instâncias do SafeHandle.Manually increments the reference counter on SafeHandle instances.

DangerousGetHandle() DangerousGetHandle() DangerousGetHandle() DangerousGetHandle()

Retorna o valor do campo handle.Returns the value of the handle field.

DangerousRelease() DangerousRelease() DangerousRelease() DangerousRelease()

Diminui manualmente o contador de referência em uma instância SafeHandle.Manually decrements the reference counter on a SafeHandle instance.

Dispose() Dispose() Dispose() Dispose()

Libera todos os recursos usados pela classe SafeHandle.Releases all resources used by the SafeHandle class.

Dispose(Boolean) Dispose(Boolean) Dispose(Boolean) Dispose(Boolean)

Libera os recursos não gerenciados usados pela classe SafeHandle que especifica se deve executar uma operação de descarte normal.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)

Determina se o objeto especificado é igual ao objeto atual.Determines whether the specified object is equal to the current object.

(Inherited from Object)
Finalize() Finalize() Finalize() Finalize()

Libera todos os recursos associados ao identificador.Frees all resources associated with the handle.

GetHashCode() GetHashCode() GetHashCode() GetHashCode()

Serve como a função de hash padrão.Serves as the default hash function.

(Inherited from Object)
GetType() GetType() GetType() GetType()

Obtém o Type da instância atual.Gets the Type of the current instance.

(Inherited from Object)
MemberwiseClone() MemberwiseClone() MemberwiseClone() MemberwiseClone()

Cria uma cópia superficial do Object atual.Creates a shallow copy of the current Object.

(Inherited from Object)
ReleaseHandle() ReleaseHandle() ReleaseHandle() ReleaseHandle()

Quando substituído em uma classe derivada, executa o código necessário para liberar o identificador.When overridden in a derived class, executes the code required to free the handle.

SetHandle(IntPtr) SetHandle(IntPtr) SetHandle(IntPtr) SetHandle(IntPtr)

Define o identificador para o identificador pré-existente especificado.Sets the handle to the specified pre-existing handle.

SetHandleAsInvalid() SetHandleAsInvalid() SetHandleAsInvalid() SetHandleAsInvalid()

Marca um identificador como não mais usado.Marks a handle as no longer used.

ToString() ToString() ToString() ToString()

Retorna uma cadeia de caracteres que representa o objeto atual.Returns a string that represents the current object.

(Inherited from Object)

Segurança

InheritanceDemand
para confiança total de herdeiros.for full trust for inheritors. Este membro não pode ser herdado por código parcialmente confiável.This member cannot be inherited by partially trusted code.

SecurityCriticalAttribute
requer confiança total para o chamador imediato.requires full trust for the immediate caller. Este membro não pode ser usado pelo código transparente ou parcialmente confiável.This member cannot be used by partially trusted or transparent code.

Aplica-se a

Veja também