SafeHandle SafeHandle SafeHandle SafeHandle Class

Definición

Representa una clase contenedora para los identificadores del sistema operativo.Represents a wrapper class for operating system handles. Se debe heredar esta clase.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
Herencia
Derivado
Atributos
Implementaciones

Ejemplos

En el ejemplo de código siguiente se crea un controlador seguro personalizado para un identificador de archivo del sistema SafeHandleZeroOrMinusOneIsInvalidoperativo, que deriva de.The following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. Lee los bytes de un archivo y muestra sus valores hexadecimales.It reads bytes from a file and displays their hexadecimal values. También contiene un instrumento de prueba de errores que hace que se anule el subproceso, pero se libera el valor de identificador.It also contains a fault testing harness that causes the thread to abort, but the handle value is freed. Cuando se usa IntPtr un para representar controladores, el identificador se filtra ocasionalmente debido a la anulación asincrónica del subproceso.When using an IntPtr to represent handles, the handle is occasionally leaked due to the asynchronous thread abort.

Necesitará un archivo de texto en la misma carpeta que la aplicación compilada.You will need a text file in the same folder as the compiled application. Suponiendo que el nombre de la aplicación es "HexViewer", el uso de la línea de comandos es:Assuming that you name the application "HexViewer", the command line usage is:

HexViewer <filename> -Fault

Opcionalmente, -Fault puede especificar para intentar de forma intencionada perder el identificador anulando el subproceso en una determinada ventana.Optionally specify -Fault to intentionally attempt to leak the handle by aborting the thread in a certain window. Use la herramienta Perfmon. exe de Windows para supervisar los recuentos de identificadores mientras se insertan errores.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);
                }
            }

        }
    }
}

Comentarios

La SafeHandle clase proporciona una finalización crítica de los recursos de identificador, evitando que los identificadores sean reclamados prematuramente por la recolección de elementos no utilizados y que Windows los reciclen para hacer referencia a objetos no administrados no deseados.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.

En este tema, se incluyen las siguientes secciones:This topic includes the following sections:

¿Por qué SafeHandle? Why SafeHandle?
¿Qué hace SafeHandle? What SafeHandle does
Clases derivadas de SafeHandleClasses derived from SafeHandle

¿Por qué SafeHandle?Why SafeHandle?

Antes de la versión 2,0 de .NET Framework, todos los identificadores del sistema operativo solo se IntPtr podían encapsular en el objeto contenedor administrado.Before the .NET Framework version 2.0, all operating system handles could only be encapsulated in the IntPtr managed wrapper object. Aunque esta era una manera cómoda de interoperar con código nativo, las excepciones asincrónicas podrían filtrar los identificadores, como una anulación de subproceso inesperada o un desbordamiento de pila.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. Estas excepciones asincrónicas son un obstáculo para limpiar los recursos del sistema operativo y pueden producirse prácticamente en cualquier parte de la aplicación.These asynchronous exceptions are an obstacle to cleaning up operating system resources, and they can occur almost anywhere in your app.

Aunque las invalidaciones del Object.Finalize método permiten la limpieza de recursos no administrados cuando un objeto se recolecta como elemento no utilizado, en algunas circunstancias, la recolección de elementos no utilizados puede reclamar objetos finalizables mientras se ejecuta un método dentro de una invocación de plataforma. llama.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. Si un finalizador libera el identificador pasado a esa llamada de invocación de plataforma, podría provocar daños en el control.If a finalizer frees the handle passed to that platform invoke call, it could lead to handle corruption. También se puede reclamar el identificador mientras el método está bloqueado durante una llamada de invocación de plataforma, por ejemplo, al leer un archivo.The handle could also be reclaimed while your method is blocked during a platform invoke call, such as while reading a file.

Más importante, ya que Windows recicla de forma agresiva los controladores, se podría reciclar un identificador y apuntar a otro recurso que podría contener datos confidenciales.More critically, because Windows aggressively recycles handles, a handle could be recycled and point to another resource that might contain sensitive data. Esto se conoce como ataque de reciclaje y puede dañar los datos y ser una amenaza para la seguridad.This is known as a recycle attack and can potentially corrupt data and be a security threat.

¿Qué hace SafeHandle?What SafeHandle does

La SafeHandle clase simplifica varios de estos problemas de duración de objetos y se integra con la invocación de plataforma para que no se pierdan los recursos del sistema operativo.The SafeHandle class simplifies several of these object lifetime issues, and is integrated with platform invoke so that operating system resources are not leaked. La SafeHandle clase resuelve los problemas de duración de los objetos mediante la asignación y liberación de identificadores sin interrupción.The SafeHandle class resolves object lifetime issues by assigning and releasing handles without interruption. Contiene un finalizador crítico que garantiza que el identificador está cerrado y se garantiza que se ejecuta durante las AppDomain descargaciones inesperadas, incluso en los casos en los que se supone que la llamada de invocación de plataforma está en un estado dañado.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.

Dado SafeHandle que hereda de CriticalFinalizerObject, se llama a todos los finalizadores no críticos antes de cualquiera de los finalizadores críticos.Because SafeHandle inherits from CriticalFinalizerObject, all the noncritical finalizers are called before any of the critical finalizers. Se llama a los finalizadores en objetos que ya no están activos durante el mismo paso de recolección de elementos no utilizados.The finalizers are called on objects that are no longer live during the same garbage collection pass. Por ejemplo, un FileStream objeto puede ejecutar un finalizador normal para vaciar los datos almacenados en búfer existentes sin el riesgo de que el controlador se filtre o se recicle.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. Esta ordenación débil entre finalizadores críticos y no críticos no está pensada para su uso general.This very weak ordering between critical and noncritical finalizers is not intended for general use. Existe principalmente para ayudar en la migración de bibliotecas existentes, ya que permite que esas bibliotecas SafeHandle usen sin alterar su semántica.It exists primarily to assist in the migration of existing libraries by allowing those libraries to use SafeHandle without altering their semantics. Además, el finalizador crítico y todo lo que llama, como el SafeHandle.ReleaseHandle() método, deben estar en una región de ejecución restringida.Additionally, the critical finalizer and anything it calls, such as the SafeHandle.ReleaseHandle() method, must be in a constrained execution region. Esto impone restricciones en el código que se puede escribir en el gráfico de llamadas del finalizador.This imposes constraints on what code can be written within the finalizer's call graph.

Las operaciones de invocación de plataforma incrementan automáticamente el recuento de SafeHandle referencias de los identificadores encapsulados por y los disminuyen al finalizar.Platform invoke operations automatically increment the reference count of handles encapsulated by a SafeHandle and decrement them upon completion. Esto garantiza que el identificador no se recicle ni se cierre de forma inesperada.This ensures that the handle will not be recycled or closed unexpectedly.

Puede especificar la propiedad del identificador subyacente al construir SafeHandle objetos proporcionando un valor ownsHandle al argumento en el SafeHandle constructor de clase.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. Controla si el SafeHandle objeto liberará el identificador una vez eliminado el objeto.This controls whether the SafeHandle object will release the handle after the object has been disposed. Esto resulta útil para los identificadores con requisitos de duración peculiares o para consumir un identificador cuya duración está controlada por otra persona.This is useful for handles with peculiar lifetime requirements or for consuming a handle whose lifetime is controlled by someone else.

Clases derivadas de SafeHandleClasses derived from SafeHandle

SafeHandlees una clase contenedora abstracta para los identificadores del sistema operativo.SafeHandle is an abstract wrapper class for operating system handles. Es difícil derivar de esta clase.Deriving from this class is difficult. En su lugar, use las clases derivadas del espacio de nombres Microsoft.Win32.SafeHandles que proporcionan controladores seguros para lo siguiente:Instead, use the derived classes in the Microsoft.Win32.SafeHandles namespace that provide safe handles for the following:

Notas a los desarrolladores de herederos

Para crear una clase derivada de SafeHandle, debe saber cómo crear y liberar un identificador del sistema operativo.To create a class derived from SafeHandle, you must know how to create and free an operating system handle. Este proceso es diferente para los distintos tipos de identificadores porque algunos usan la función CloseHandle , mientras que otros usan funciones más específicas, como UnmapViewOfFile o 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 esta razón, debe crear una clase derivada de SafeHandle para cada tipo de identificador del sistema operativo que desee ajustar en un controlador 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.

Al heredar de SafeHandle, es necesario reemplazar los miembros siguientes: IsInvalid y ReleaseHandle().When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle().

También debe proporcionar un constructor sin parámetros que llame al constructor base con un valor que represente un valor de identificador no válido y Boolean un valor que indique si el identificador nativo es propiedad SafeHandle de y, por consiguiente, se debe liberar. SafeHandle cuando se ha eliminado.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.

Constructores

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

Inicializa una nueva instancia de la clase SafeHandle con un valor de identificador no válido especificado.Initializes a new instance of the SafeHandle class with the specified invalid handle value.

Campos

handle handle handle handle

Especifica el identificador que se va a ajustar.Specifies the handle to be wrapped.

Propiedades

IsClosed IsClosed IsClosed IsClosed

Obtiene un valor que indica si el identificador está cerrado.Gets a value indicating whether the handle is closed.

IsInvalid IsInvalid IsInvalid IsInvalid

Cuando se invalida en una clase derivada, obtiene un valor que indica si este identificador es no 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 el identificador para soltar y liberar recursos.Marks the handle for releasing and freeing resources.

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

Aumenta manualmente el recuento de referencias en instancias de SafeHandle.Manually increments the reference counter on SafeHandle instances.

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

Devuelve el valor del campo handle.Returns the value of the handle field.

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

Disminuye manualmente el recuento de referencias en una instancia de SafeHandle.Manually decrements the reference counter on a SafeHandle instance.

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

Libera todos los recursos que utiliza la clase SafeHandle.Releases all resources used by the SafeHandle class.

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

Libera los recursos no administrados utilizados por la clase SafeHandle que especifica si se va a realizar una operación de desecho 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 si el objeto especificado es igual al objeto actual.Determines whether the specified object is equal to the current object.

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

Libera todos los recursos asociados al identificador.Frees all resources associated with the handle.

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

Sirve como la función hash predeterminada.Serves as the default hash function.

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

Obtiene el Type de la instancia actual.Gets the Type of the current instance.

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

Crea una copia superficial del objeto Object actual.Creates a shallow copy of the current Object.

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

Cuando se invalida en una clase derivada, ejecuta el código necesario para liberar el identificador.When overridden in a derived class, executes the code required to free the handle.

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

Establece el identificador en el identificador preexistente.Sets the handle to the specified pre-existing handle.

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

Marca un identificador para indicar que ya no se utiliza.Marks a handle as no longer used.

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

Devuelve una cadena que representa el objeto actual.Returns a string that represents the current object.

(Inherited from Object)

Seguridad

InheritanceDemand
para plena confianza para los desarrolladores de herederos.for full trust for inheritors. Este miembro no se puede heredar por código de confianza parcial.This member cannot be inherited by partially trusted code.

SecurityCriticalAttribute
requiere plena confianza para el llamador inmediato.requires full trust for the immediate caller. Este miembro no puede ser utilizado por código transparente o de confianza parcial.This member cannot be used by partially trusted or transparent code.

Se aplica a

Consulte también: