SafeHandle Clase

Definición

Representa una clase contenedora para los identificadores del sistema operativo. Se debe heredar esta clase.

public ref class SafeHandle abstract : IDisposable
public ref class SafeHandle abstract : System::Runtime::ConstrainedExecution::CriticalFinalizerObject, IDisposable
[System.Security.SecurityCritical]
public abstract class SafeHandle : IDisposable
public abstract class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
[System.Security.SecurityCritical]
public abstract class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
[<System.Security.SecurityCritical>]
type SafeHandle = class
    interface IDisposable
type SafeHandle = class
    inherit CriticalFinalizerObject
    interface IDisposable
[<System.Security.SecurityCritical>]
type SafeHandle = class
    inherit CriticalFinalizerObject
    interface IDisposable
Public MustInherit Class SafeHandle
Implements IDisposable
Public MustInherit Class SafeHandle
Inherits CriticalFinalizerObject
Implements IDisposable
Herencia
SafeHandle
Herencia
Derivado
Atributos
Implementaciones

Ejemplos

En el ejemplo de código siguiente se crea un identificador seguro personalizado para un identificador de archivo del sistema operativo, que deriva de SafeHandleZeroOrMinusOneIsInvalid. Lee bytes de un archivo y muestra sus valores hexadecimales. También contiene un arnés de pruebas de errores que hace que el subproceso se anule, pero se libera el valor de identificador. Cuando se usa un IntPtr para representar identificadores, el identificador se filtra ocasionalmente debido a la anulación del subproceso asincrónico.

Necesitará un archivo de texto en la misma carpeta que la aplicación compilada. Suponiendo que asigne el nombre "HexViewer" a la aplicación, el uso de la línea de comandos es:

HexViewer <filename> -Fault

Opcionalmente, especifique -Fault para intentar filtrar intencionadamente el identificador anulando el subproceso en una ventana determinada. Use la herramienta Windows Perfmon.exe para supervisar los recuentos de identificadores al insertar errores.

using System;
using System.Runtime.InteropServices;
using System.IO;
using System.ComponentModel;
using System.Security;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Runtime.ConstrainedExecution;
using System.Security.Permissions;

namespace SafeHandleDemo
{
    internal class MySafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        // Create a SafeHandle, informing the base class
        // that this SafeHandle instance "owns" the handle,
        // and therefore SafeHandle should call
        // our ReleaseHandle method when the SafeHandle
        // is no longer in use.
        private MySafeFileHandle()
            : base(true)
        {
        }
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        override protected bool ReleaseHandle()
        {
            // Here, we must obey all rules for constrained execution regions.
            return NativeMethods.CloseHandle(handle);
            // If ReleaseHandle failed, it can be reported via the
            // "releaseHandleFailed" managed debugging assistant (MDA).  This
            // MDA is disabled by default, but can be enabled in a debugger
            // or during testing to diagnose handle corruption problems.
            // We do not throw an exception because most code could not recover
            // from the problem.
        }
    }

    [SuppressUnmanagedCodeSecurity()]
    internal static class NativeMethods
    {
        // Win32 constants for accessing files.
        internal const int GENERIC_READ = unchecked((int)0x80000000);

        // Allocate a file object in the kernel, then return a handle to it.
        [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
        internal extern static MySafeFileHandle CreateFile(String fileName,
           int dwDesiredAccess, System.IO.FileShare dwShareMode,
           IntPtr securityAttrs_MustBeZero, System.IO.FileMode dwCreationDisposition,
           int dwFlagsAndAttributes, IntPtr hTemplateFile_MustBeZero);

        // Use the file handle.
        [DllImport("kernel32", SetLastError = true)]
        internal extern static int ReadFile(MySafeFileHandle handle, byte[] bytes,
           int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

        // Free the kernel's file object (close the file).
        [DllImport("kernel32", SetLastError = true)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        internal extern static bool CloseHandle(IntPtr handle);
    }

    // The MyFileReader class is a sample class that accesses an operating system
    // resource and implements IDisposable. This is useful to show the types of
    // transformation required to make your resource wrapping classes
    // more resilient. Note the Dispose and Finalize implementations.
    // Consider this a simulation of System.IO.FileStream.
    public class MyFileReader : IDisposable
    {
        // _handle is set to null to indicate disposal of this instance.
        private MySafeFileHandle _handle;

        public MyFileReader(String fileName)
        {
            // Security permission check.
            String fullPath = Path.GetFullPath(fileName);
            new FileIOPermission(FileIOPermissionAccess.Read, fullPath).Demand();

            // Open a file, and save its handle in _handle.
            // Note that the most optimized code turns into two processor
            // instructions: 1) a call, and 2) moving the return value into
            // the _handle field.  With SafeHandle, the CLR's platform invoke
            // marshaling layer will store the handle into the SafeHandle
            // object in an atomic fashion. There is still the problem
            // that the SafeHandle object may not be stored in _handle, but
            // the real operating system handle value has been safely stored
            // in a critical finalizable object, ensuring against leaking
            // the handle even if there is an asynchronous exception.

            MySafeFileHandle tmpHandle;
            tmpHandle = NativeMethods.CreateFile(fileName, NativeMethods.GENERIC_READ,
                FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);

            // An async exception here will cause us to run our finalizer with
            // a null _handle, but MySafeFileHandle's ReleaseHandle code will
            // be invoked to free the handle.

            // This call to Sleep, run from the fault injection code in Main,
            // will help trigger a race. But it will not cause a handle leak
            // because the handle is already stored in a SafeHandle instance.
            // Critical finalization then guarantees that freeing the handle,
            // even during an unexpected AppDomain unload.
            Thread.Sleep(500);
            _handle = tmpHandle;  // Makes _handle point to a critical finalizable object.

            // Determine if file is opened successfully.
            if (_handle.IsInvalid)
                throw new Win32Exception(Marshal.GetLastWin32Error(), fileName);
        }

        public void Dispose()  // Follow the Dispose pattern - public nonvirtual.
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        // No finalizer is needed. The finalizer on SafeHandle
        // will clean up the MySafeFileHandle instance,
        // if it hasn't already been disposed.
        // However, there may be a need for a subclass to
        // introduce a finalizer, so Dispose is properly implemented here.
        protected virtual void Dispose(bool disposing)
        {
            // Note there are three interesting states here:
            // 1) CreateFile failed, _handle contains an invalid handle
            // 2) We called Dispose already, _handle is closed.
            // 3) _handle is null, due to an async exception before
            //    calling CreateFile. Note that the finalizer runs
            //    if the constructor fails.
            if (_handle != null && !_handle.IsInvalid)
            {
                // Free the handle
                _handle.Dispose();
            }
            // SafeHandle records the fact that we've called Dispose.
        }

        public byte[] ReadContents(int length)
        {
            if (_handle.IsInvalid)  // Is the handle disposed?
                throw new ObjectDisposedException("FileReader is closed");

            // This sample code will not work for all files.
            byte[] bytes = new byte[length];
            int numRead = 0;
            int r = NativeMethods.ReadFile(_handle, bytes, length, out numRead, IntPtr.Zero);
            // Since we removed MyFileReader's finalizer, we no longer need to
            // call GC.KeepAlive here.  Platform invoke will keep the SafeHandle
            // instance alive for the duration of the call.
            if (r == 0)
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (numRead < length)
            {
                byte[] newBytes = new byte[numRead];
                Array.Copy(bytes, newBytes, numRead);
                bytes = newBytes;
            }
            return bytes;
        }
    }

    static class Program
    {
        // Testing harness that injects faults.
        private static bool _printToConsole = false;
        private static bool _workerStarted = false;

        private static void Usage()
        {
            Console.WriteLine("Usage:");
            // Assumes that application is named HexViewer"
            Console.WriteLine("HexViewer <fileName> [-fault]");
            Console.WriteLine(" -fault Runs hex viewer repeatedly, injecting faults.");
        }

        private static void ViewInHex(Object fileName)
        {
            _workerStarted = true;
            byte[] bytes;
            using (MyFileReader reader = new MyFileReader((String)fileName))
            {
                bytes = reader.ReadContents(20);
            }  // Using block calls Dispose() for us here.

            if (_printToConsole)
            {
                // Print up to 20 bytes.
                int printNBytes = Math.Min(20, bytes.Length);
                Console.WriteLine("First {0} bytes of {1} in hex", printNBytes, fileName);
                for (int i = 0; i < printNBytes; i++)
                    Console.Write("{0:x} ", bytes[i]);
                Console.WriteLine();
            }
        }

        static void Main(string[] args)
        {
            if (args.Length == 0 || args.Length > 2 ||
                args[0] == "-?" || args[0] == "/?")
            {
                Usage();
                return;
            }

            String fileName = args[0];
            bool injectFaultMode = args.Length > 1;
            if (!injectFaultMode)
            {
                _printToConsole = true;
                ViewInHex(fileName);
            }
            else
            {
                Console.WriteLine("Injecting faults - watch handle count in perfmon (press Ctrl-C when done)");
                int numIterations = 0;
                while (true)
                {
                    _workerStarted = false;
                    Thread t = new Thread(new ParameterizedThreadStart(ViewInHex));
                    t.Start(fileName);
                    Thread.Sleep(1);
                    while (!_workerStarted)
                    {
                        Thread.Sleep(0);
                    }
                    t.Abort();  // Normal applications should not do this.
                    numIterations++;
                    if (numIterations % 10 == 0)
                        GC.Collect();
                    if (numIterations % 10000 == 0)
                        Console.WriteLine(numIterations);
                }
            }
        }
    }
}

Comentarios

La SafeHandle clase proporciona una finalización crítica de los recursos de control, lo que impide que los identificadores se recuperen prematuramente mediante la recolección de elementos no utilizados y se reciclan mediante Windows para hacer referencia a objetos no administrados no deseados.

Este tema incluye las siguientes secciones:

¿Por qué SafeHandle?Qué hace SafeHandleclases derivadas de SafeHandle

¿Por qué SafeHandle?

Antes de la versión 2.0 de .NET Framework, todos los identificadores del sistema operativo solo se podían encapsular en el IntPtr objeto contenedor administrado. Aunque esta era una manera cómoda de interoperar con código nativo, los identificadores podrían filtrarse por excepciones asincrónicas, como una anulación inesperada de subprocesos o un desbordamiento de pila. Estas excepciones asincrónicas son un obstáculo para limpiar los recursos del sistema operativo y pueden producirse casi en cualquier lugar de la aplicación.

Aunque las invalidaciones del Object.Finalize método permiten la limpieza de recursos no administrados cuando se recopila un objeto 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 llamada de invocación de plataforma. Si un finalizador libera el identificador pasado a esa llamada de invocación de plataforma, podría provocar daños. El identificador también se puede reclamar mientras el método se bloquea durante una llamada de invocación de plataforma, como al leer un archivo.

De forma más crítica, dado que Windows recicla de forma agresiva los identificadores, un identificador se podría reciclar y apuntar a otro recurso que podría contener datos confidenciales. Esto se conoce como un ataque de reciclaje y puede dañar los datos y ser una amenaza de seguridad.

Qué hace SafeHandle

La SafeHandle clase simplifica varios de estos problemas de duración de objetos y se integra con la invocación de plataforma para que los recursos del sistema operativo no se filtren. La SafeHandle clase resuelve los problemas de duración del objeto mediante la asignación y liberación de identificadores sin interrupción. Contiene un finalizador crítico que garantiza que el identificador está cerrado y se garantiza que se ejecute durante descargas inesperadas AppDomain , incluso en casos en los que se supone que la llamada de invocación de plataforma está en un estado dañado.

Dado que SafeHandle hereda de CriticalFinalizerObject, se llama a todos los finalizadores no críticos antes de cualquiera de los finalizadores críticos. Se llama a los finalizadores en objetos que ya no están activos durante el mismo paso de recolección de elementos no utilizados. 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 identificador se filtre o se recicla. Este orden muy débil entre finalizadores críticos y no críticos no está pensado para uso general. Existe principalmente para ayudar en la migración de bibliotecas existentes al permitir que esas bibliotecas se usen SafeHandle sin modificar su semántica. Además, el finalizador crítico y cualquier cosa que llame, como el SafeHandle.ReleaseHandle() método , debe estar en una región de ejecución restringida. Esto impone restricciones sobre qué código se puede escribir en el gráfico de llamadas del finalizador.

Las operaciones de invocación de plataforma incrementan automáticamente el recuento de referencias de los identificadores encapsulados por y SafeHandle los reducen al finalizar. Esto garantiza que el mango no se reciclará ni cerrará inesperadamente.

Puede especificar la propiedad del identificador subyacente al construir SafeHandle objetos proporcionando un valor al ownsHandle argumento en el constructor de clase SafeHandle . Esto controla si el SafeHandle objeto liberará el identificador después de eliminar el objeto. Esto es útil para manipuladores con requisitos de duración peculiares o para consumir un identificador cuya duración está controlada por otra persona.

Clases derivadas de SafeHandle

SafeHandle es una clase contenedora abstracta para los identificadores del sistema operativo. Es difícil derivar de esta clase. En su lugar, use las clases derivadas del espacio de nombres Microsoft.Win32.SafeHandles que proporcionan controladores seguros para lo siguiente:

Notas a los implementadores

Para crear una clase derivada de SafeHandle, debe saber cómo crear y liberar un identificador del sistema operativo. Este proceso es diferente para distintos tipos de identificadores porque algunos usan la función CloseHandle , mientras que otros usan funciones más específicas, como UnmapViewOfFile o FindClose. Por este motivo, debe crear una clase derivada de para cada tipo de SafeHandle identificador del sistema operativo que desee encapsular en un identificador seguro.

Al heredar de SafeHandle, es necesario reemplazar los miembros siguientes: IsInvalid y ReleaseHandle().

También debe proporcionar un constructor público sin parámetros que llame al constructor base con un valor que represente un valor de identificador no válido y un Boolean valor que indique si el identificador nativo es propiedad de SafeHandle y, por tanto, debe liberarse cuando se haya eliminado.SafeHandle

Constructores

SafeHandle()

Representa una clase contenedora para los identificadores del sistema operativo. Se debe heredar esta clase.

SafeHandle(IntPtr, Boolean)

Inicializa una nueva instancia de la clase SafeHandle con un valor de identificador no válido especificado.

Campos

handle

Especifica el identificador que se va a ajustar.

Propiedades

IsClosed

Obtiene un valor que indica si el identificador está cerrado.

IsInvalid

Cuando se invalida en una clase derivada, obtiene un valor que indica si este identificador es no válido.

Métodos

Close()

Marca el identificador para soltar y liberar recursos.

DangerousAddRef(Boolean)

Aumenta manualmente el recuento de referencias en instancias de SafeHandle.

DangerousGetHandle()

Devuelve el valor del campo handle.

DangerousRelease()

Disminuye manualmente el recuento de referencias en una instancia de SafeHandle.

Dispose()

Libera todos los recursos que utiliza la clase SafeHandle.

Dispose(Boolean)

Libera los recursos no administrados usados por la clase SafeHandle especificando si se lleva a cabo una operación de eliminación normal.

Equals(Object)

Determina si el objeto especificado es igual que el objeto actual.

(Heredado de Object)
Finalize()

Libera todos los recursos asociados al identificador.

GetHashCode()

Sirve como la función hash predeterminada.

(Heredado de Object)
GetType()

Obtiene el Type de la instancia actual.

(Heredado de Object)
MemberwiseClone()

Crea una copia superficial del Object actual.

(Heredado de Object)
ReleaseHandle()

Cuando se invalida en una clase derivada, ejecuta el código necesario para liberar el identificador.

SetHandle(IntPtr)

Establece el identificador en el identificador preexistente.

SetHandleAsInvalid()

Marca un identificador para indicar que ya no se utiliza.

ToString()

Devuelve una cadena que representa el objeto actual.

(Heredado de Object)

Se aplica a

Consulte también