Compartilhar via


Classe SafeHandle

 

Dica

The .NET API Reference documentation has a new home. Visit the .NET API Browser on docs.microsoft.com to see the new experience.

Representa uma classe wrapper para identificadores do sistema operacional. Essa classe deve ser herdada.

Namespace:   System.Runtime.InteropServices
Assembly:  mscorlib (em mscorlib.dll)

Hierarquia de Herança

System.Object
  System.Runtime.ConstrainedExecution.CriticalFinalizerObject
    System.Runtime.InteropServices.SafeHandle
      Microsoft.Win32.SafeHandles.SafeAccessTokenHandle
      Microsoft.Win32.SafeHandles.SafeHandleMinusOneIsInvalid
      Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid

Sintaxe

[SecurityCriticalAttribute]
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, 
    UnmanagedCode = true)]
public abstract class SafeHandle : CriticalFinalizerObject, IDisposable
[SecurityCriticalAttribute]
[SecurityPermissionAttribute(SecurityAction::InheritanceDemand, 
    UnmanagedCode = true)]
public ref class SafeHandle abstract : CriticalFinalizerObject, 
    IDisposable
[<AbstractClass>]
[<SecurityCriticalAttribute>]
[<SecurityPermissionAttribute(SecurityAction.InheritanceDemand,
    UnmanagedCode = true)>]
type SafeHandle = 
    class
        inherit CriticalFinalizerObject
        interface IDisposable
    end
<SecurityCriticalAttribute>
<SecurityPermissionAttribute(SecurityAction.InheritanceDemand,
    UnmanagedCode := True)>
Public MustInherit Class SafeHandle
    Inherits CriticalFinalizerObject
    Implements IDisposable

Construtores

Nome Descrição
System_CAPS_protmethod SafeHandle(IntPtr, Boolean)

Inicializa uma nova instância da classe SafeHandle com o valor do identificador inválido especificado.

Propriedades

Nome Descrição
System_CAPS_pubproperty IsClosed

Obtém um valor que indica se o identificador está fechado.

System_CAPS_pubproperty IsInvalid

Quando substituído em uma classe derivada, obtém um valor que indica se o valor de indicador é válido.

Métodos

Nome Descrição
System_CAPS_pubmethod Close()

Marca o identificador de liberação de recursos.

System_CAPS_pubmethod DangerousAddRef(Boolean)

Incrementa manualmente o contador de referências em instâncias do SafeHandle.

System_CAPS_pubmethod DangerousGetHandle()

Retorna o valor do campo handle.

System_CAPS_pubmethod DangerousRelease()

Diminui manualmente o contador de referência em uma instância SafeHandle.

System_CAPS_pubmethod Dispose()

Libera todos os recursos usados pela classe SafeHandle.

System_CAPS_protmethod Dispose(Boolean)

Libera os recursos não gerenciados usados pela classe SafeHandle que especifica se deve executar uma operação de descarte normal.

System_CAPS_pubmethod Equals(Object)

Determina se o objeto especificado é igual ao objeto atual.(Herdado de Object.)

System_CAPS_protmethod Finalize()

Libera todos os recursos associados ao identificador.(Substitui o CriticalFinalizerObject.Finalize().)

System_CAPS_pubmethod GetHashCode()

Serve como a função de hash padrão.(Herdado de Object.)

System_CAPS_pubmethod GetType()

Obtém o Type da instância atual.(Herdado de Object.)

System_CAPS_protmethod MemberwiseClone()

Cria uma cópia superficial do Object atual.(Herdado de Object.)

System_CAPS_protmethod ReleaseHandle()

Quando substituído em uma classe derivada, executa o código necessário para liberar o identificador.

System_CAPS_protmethod SetHandle(IntPtr)

Define o identificador para o identificador pré-existente especificado.

System_CAPS_pubmethod SetHandleAsInvalid()

Marca um identificador como não mais usado.

System_CAPS_pubmethod ToString()

Retorna uma cadeia de caracteres que representa o objeto atual.(Herdado de Object.)

Campos

Nome Descrição
System_CAPS_protfield handle

Especifica o identificador a ser encapsulado.

Comentários

O SafeHandle classe fornece finalização crítica dos recursos do identificador, impedindo identificadores seja recuperada prematuramente pela coleta de lixo e de ser reciclado pelo Windows para fazer referência a objetos não gerenciados não intencionais.

Este tópico inclui as seções a seguir:

Por que SafeHandle?
O que faz SafeHandle
Classes derivadas de SafeHandle

Por que SafeHandle?

Antes do .NET Framework versão 2.0, o sistema operacional identificadores só podem ser encapsulados no IntPtr objeto wrapper gerenciado. Embora essa era uma maneira conveniente para a interoperação com código nativo, identificadores poderiam ser vazados por exceções assíncronas, como um thread anulando inesperadamente ou um estouro de pilha. Essas exceções assíncronas são um obstáculo para limpar os recursos do sistema operacional, e eles podem ocorrer praticamente qualquer lugar no seu aplicativo.

Embora as substituições feitas ao Object.Finalize método permite que a limpeza de recursos não gerenciados, quando um objeto está sendo limpo, em algumas circunstâncias, objetos finalizáveis podem ser recuperados pelo lixo coleção durante a execução de um método em uma plataforma de chamada de invocação. Se um finalizador libera o identificador passado para essa plataforma chamar invoke, isso pode levar a tratar corrupção. O identificador também poderia ser recuperado, enquanto o método é bloqueado durante uma plataforma de invocação de chamada, como ao ler um arquivo.

Mais crítico, porque o Windows agressivamente recicla identificadores, um identificador pode ser reciclado e aponte para outro recurso que pode conter dados confidenciais. Isso é conhecido como um ataque de reciclagem e pode potencialmente corromper os dados e ser uma ameaça à segurança.

O que faz SafeHandle

O SafeHandle classe simplifica vários desses problemas de tempo de vida do objeto e está integrada com a plataforma de invocação de forma que os recursos do sistema operacional não vazam. O SafeHandle classe resolve problemas de tempo de vida do objeto atribuindo e liberar identificadores sem interrupções. Ele contém um finalizador crítico que garante que o identificador é fechado e sempre é executado durante inesperado AppDomain descarrega, mesmo em casos em que a chamada de invocação de plataforma deve para estar em um estado corrompido.

Porque SafeHandle herda de CriticalFinalizerObject, todos os finalizadores não críticos são chamados antes de qualquer os finalizadores críticos. Os finalizadores são chamados em objetos que não são mais dinâmicos durante a fase de coleta de lixo mesmo. Por exemplo, um FileStream objeto pode executar um finalizador normal para liberar os dados armazenados em buffer existentes sem o risco de que o identificador seja perdido ou reciclados. Essa classificação muito fraca entre finalizadores críticos e não se destina para uso geral. Ele existe principalmente para ajudar na migração das bibliotecas existentes, permitindo que essas bibliotecas usar SafeHandle sem alterar sua semântica. Além disso, o finalizador crítico e tudo o que ele chama, como o SafeHandle.ReleaseHandle() método, deve estar em uma região de execução restrita. Isso impõe restrições na qual código pode ser gravado no gráfico de chamadas do finalizador.

Invocação de plataforma operações automaticamente incrementar a contagem de referência de identificadores encapsulado por um SafeHandle e de decremento-los após a conclusão. Isso garante que o identificador não será reciclado ou fechado inesperadamente.

Você pode especificar a propriedade do identificador subjacente ao construir SafeHandle objetos fornecendo um valor para o ownsHandle argumento o SafeHandle construtor de classe. Controla se o SafeHandle objeto liberará a alça depois que o objeto foi descartado. Isso é útil para identificadores com os requisitos de tempo de vida peculiares ou para o consumo de um identificador cujo tempo de vida é controlado por outra pessoa.

Classes derivadas de SafeHandle

SafeHandleé uma classe abstrata de wrapper para identificadores do sistema operacional. Essa classe é difícil. Em vez disso, use as classes derivadas no Microsoft.Win32.SafeHandles namespace que forneça identificadores de segurança para o seguinte:

Observações para os Herdeiros:

Para criar uma classe derivada de SafeHandle, você deve saber como criar e liberar um identificador de sistema operacional. Esse processo é diferente para tipos de identificador diferente porque alguns usam o CloseHandle funcionar, enquanto outras usam funções mais específicas, como UnmapViewOfFile ou FindClose. Por esse motivo, você deve criar uma classe derivada de SafeHandle para cada tipo de identificador de sistema operacional que você deseja incluir em um identificador seguro.

Importante

Escrever suas próprias classes derivadas de SafeHandle é um recurso de programação avançado. O .NET Framework fornece um conjunto de boa classes derivadas de SafeHandle no Microsoft.Win32.SafeHandles namespace. Essas classes são projetadas para fornecer funcionalidade comum com suporte a identificadores de arquivo e sistema operacional.

Ao herdar de SafeHandle, você deve substituir os seguintes membros: IsInvalid e ReleaseHandle.

Você também deve fornecer um construtor padrão que chama o construtor base com um valor que represente um valor identificador inválido, e um Boolean valor que indica se o identificador nativo é de propriedade de SafeHandle e, consequentemente, deve ser liberada quando que SafeHandle foi descartado.

Exemplos

O exemplo de código a seguir cria um identificador de seguro personalizado para um identificador de arquivo do sistema operacional, derivando de SafeHandleZeroOrMinusOneIsInvalid. Ele lê os bytes de um arquivo e exibe os valores hexadecimais. Ele também contém uma falha de equipamento de teste que faz com que o thread de anulação, mas o valor do identificador é liberado. Ao usar um IntPtr para representar os identificadores, o identificador ocasionalmente vazou devido à anulação de thread assíncrono.

Você precisará de um arquivo de texto na mesma pasta que o aplicativo compilado. Supondo que você nomear o aplicativo "HexViewer", o uso de linha de comando é:

HexViewer <filename> -Fault

Opcionalmente, especifique -Fault intencionalmente tente deixar vazar a alça, anulando o thread em uma janela de determinados. Use a ferramenta Windows Perform.exe para monitorar a contagem de identificadores ao injetar falhas.

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);
                }
            }

        }
    }
}

Segurança

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.

Informações de Versão

Plataforma Universal do Windows
Disponível desde 8
.NET Framework
Disponível desde 2.0
Biblioteca de Classes Portátil
Com suporte no: plataformas portáteis do .NET
Silverlight
Disponível desde 2.0
Windows Phone Silverlight
Disponível desde 7.0
Windows Phone
Disponível desde 8.1

Acesso thread-safe

Quaisquer membros estáticos públicos ( Compartilhado no Visual Basic) desse tipo são thread-safe. Não há garantia de que qualquer membro de instância seja thread-safe.

Confira Também

Microsoft.Win32.SafeHandles
CriticalHandle
CriticalFinalizerObject
Namespace System.Runtime.InteropServices

Retornar ao início