Compartir a través de


Clase SafeHandle

 

Publicado: octubre de 2016

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

Espacio de nombres:   System.Runtime.InteropServices
Ensamblado:  mscorlib (en mscorlib.dll)

Jerarquía de herencia

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

Sintaxis

[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

Constructores

Nombre Descripción
System_CAPS_protmethod SafeHandle(IntPtr, Boolean)

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

Propiedades

Nombre Descripción
System_CAPS_pubproperty IsClosed

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

System_CAPS_pubproperty IsInvalid

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

Métodos

Nombre Descripción
System_CAPS_pubmethod Close()

Marca el identificador para soltar y liberar recursos.

System_CAPS_pubmethod DangerousAddRef(Boolean)

Incrementa manualmente el contador de referencia de las instancias SafeHandle.

System_CAPS_pubmethod DangerousGetHandle()

Devuelve el valor del campo handle.

System_CAPS_pubmethod DangerousRelease()

Disminuye manualmente el contador de referencia de la instancia SafeHandle.

System_CAPS_pubmethod Dispose()

Libera todos los recursos que usa la clase SafeHandle.

System_CAPS_protmethod 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.

System_CAPS_pubmethod Equals(Object)

Determina si el objeto especificado es igual al objeto actual.(Heredado de Object).

System_CAPS_protmethod Finalize()

Libera todos los recursos asociados al identificador.(Invalida CriticalFinalizerObject.Finalize()).

System_CAPS_pubmethod GetHashCode()

Sirve como la función hash predeterminada.(Heredado de Object).

System_CAPS_pubmethod GetType()

Obtiene el Type de la instancia actual.(Heredado de Object).

System_CAPS_protmethod MemberwiseClone()

Crea una copia superficial del Object actual.(Heredado de Object).

System_CAPS_protmethod ReleaseHandle()

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

System_CAPS_protmethod SetHandle(IntPtr)

Establece el identificador en el identificador preexistente.

System_CAPS_pubmethod SetHandleAsInvalid()

Marca un identificador para indicar que ya no se utiliza.

System_CAPS_pubmethod ToString()

Devuelve una cadena que representa el objeto actual.(Heredado de Object).

Campos

Nombre Descripción
System_CAPS_protfield handle

Especifica el identificador que se va a ajustar.

Comentarios

La SafeHandle clase proporciona una finalización crítica de recursos de identificador, evitar que los identificadores reclame prematuramente por la recolección y se recicle por Windows para hacer referencia a objetos no administrados imprevistos.

En este tema, se incluyen las siguientes secciones:

¿Por qué SafeHandle?
¿Qué SafeHandle
Clases derivadas de SafeHandle

¿Por qué SafeHandle?

Antes de .NET Framework versión 2.0, todos los identificadores sólo se podían encapsular en el sistema operativo del IntPtr objeto de contenedor administrado. Aunque esto era una manera cómoda de interoperar con código nativo, podría perderá controla las excepciones asincrónicas, como un subproceso que se anulase inesperadamente o un desbordamiento de pila. Estas excepciones asincrónicas son un obstáculo para limpiar los recursos del sistema operativo y se pueden producir casi en cualquier parte de la aplicación.

Aunque las invalidaciones el Object.Finalize método permitir la limpieza de recursos no administrados cuando un objeto se recolectan, en algunas circunstancias, pueden reclamar objetos finalizables por elementos no utilizados al ejecutar un método dentro de una plataforma de llamada de invocación. Si un finalizador libera el identificador pasado a esa plataforma llamada de invocación, se podía llegar a dañar el identificador. También se podía reclamar el identificador mientras el método se bloquea durante una plataforma de la llamada, por ejemplo, al leer un archivo.

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

¿Qué SafeHandle

La SafeHandle clase simplifica algunos de estos problemas de duración de objetos y se integra con la plataforma se invoca para que no haya fugas de recursos del sistema operativo. La SafeHandle clase resuelve los problemas de duración de objetos asignando y liberando identificadores sin interrupción. Contiene un finalizador crítico que garantiza que el identificador se cierra y se garantiza que se ejecutan durante inesperado AppDomain descarga, incluso en el caso cuando la llamada de invocación de la plataforma se supone que en un estado dañado.

Porque SafeHandle hereda de CriticalFinalizerObject, se denominan todos los finalizadores no críticos antes que cualquiera de los finalizadores críticos. Se llama a los finalizadores en objetos que ya no están activos durante el mismo paso de recopilació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 que se pierda o se recicle. Esta clasificación muy poco sólida entre finalizadores críticos y no está pensada para uso general. Existe principalmente para ayudar en la migración de bibliotecas existentes al permitir a esas bibliotecas utilizar SafeHandle sin modificar su semántica. Además, el finalizador crítico y todo lo las llamadas, 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 automáticamente incrementar el recuento de referencias de identificadores encapsulados por un SafeHandle y los reducen tras la finalización. Esto garantiza que el identificador no se recicle o cerró de forma inesperada.

Puede especificar la propiedad del identificador subyacente al construir SafeHandle objetos proporcionando un valor para el ownsHandle argumento en el SafeHandle constructor de clase. Esta opción controla si el SafeHandle objeto libere el identificador después de que el objeto se ha eliminado. Esto es útil para identificadores con requisitos de duración específicos o para utilizar 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 para desarrolladores de herederos:

Para crear una clase derivada de SafeHandle, debe saber cómo crear y liberar un identificador del sistema operativo. Este proceso es diferente para los tipos de identificador distintos porque algunos utilizan el CloseHandle funcione, mientras que otros utilizan funciones más específicas como UnmapViewOfFile o FindClose. Por este motivo, debe crear una clase derivada de SafeHandle para cada tipo de identificador de sistema operativo que se va a incluir en un controlador seguro.

Importante

Escribir sus propias clases derivadas de SafeHandle es una característica de programación avanzada. .NET Framework proporciona un conjunto de clases preescritas derivado de SafeHandle en el Microsoft.Win32.SafeHandles espacio de nombres. Estas clases están diseñadas para proporcionar funcionalidad común para admitir identificadores de archivos y el sistema operativo.

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

También debe proporcionar un constructor predeterminado que llama al constructor base con un valor que representa un valor de identificador no válido, y un Boolean valor que indica si el identificador nativo es propiedad del SafeHandle y, por consiguiente, se debería liberar cuando que SafeHandle se ha eliminado.

Ejemplos

En el ejemplo de código siguiente se crea un identificador seguro personalizado para un identificador de archivos del sistema operativo, deriva de SafeHandleZeroOrMinusOneIsInvalid. Lee los bytes de un archivo y muestra sus valores hexadecimales. También contiene un error en la herramienta de comprobación que hace que el subproceso se anule, pero el valor del identificador se libera. Cuando se usa un IntPtr para representar los identificadores, el identificador ocasionalmente se pierde debido a la anulación asincrónica del subproceso.

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

HexViewer <filename> -Fault

Opcionalmente, especifique -Fault intencionadamente intente perder el identificador anulando el subproceso en una ventana de determinados. Utilice la herramienta Perform.exe de Windows para supervisar los contadores del identificador al insertar los errores.

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

        }
    }
}

Seguridad

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.

Información de versión

Plataforma universal de Windows
Disponible desde 8
.NET Framework
Disponible desde 2.0
Biblioteca de clases portable
Se admite en: plataformas portátiles de .NET
Silverlight
Disponible desde 2.0
Windows Phone Silverlight
Disponible desde 7.0
Windows Phone
Disponible desde 8.1

Seguridad para subprocesos

Cualquier miembro ( Compartido en Visual Basic) estático público de este tipo es seguro para subprocesos. No se garantiza que los miembros de instancia sean seguros para subprocesos.

Ver también

Microsoft.Win32.SafeHandles
CriticalHandle
CriticalFinalizerObject
Espacio de nombres System.Runtime.InteropServices

Volver al principio