SafeHandle SafeHandle SafeHandle SafeHandle Class

Definizione

Rappresenta una classe wrapper per gli handle del sistema operativo.Represents a wrapper class for operating system handles. La classe deve essere ereditata.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
Ereditarietà
Derivato
Attributi
Implementazioni

Esempi

Nell'esempio di codice seguente viene creato un handle sicuro personalizzato per un handle di file del sistema operativo SafeHandleZeroOrMinusOneIsInvalid, derivando da.The following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. Legge i byte da un file e Visualizza i relativi valori esadecimali.It reads bytes from a file and displays their hexadecimal values. Contiene anche un harness di test di errore che causa l'interruzione del thread, ma il valore dell'handle viene liberato.It also contains a fault testing harness that causes the thread to abort, but the handle value is freed. Quando si usa IntPtr un oggetto per rappresentare gli handle, l'handle viene occasionalmente divulgato a causa dell'interruzione del thread asincrono.When using an IntPtr to represent handles, the handle is occasionally leaked due to the asynchronous thread abort.

Sarà necessario un file di testo nella stessa cartella dell'applicazione compilata.You will need a text file in the same folder as the compiled application. Supponendo di assegnare all'applicazione il nome "HexViewer", l'utilizzo della riga di comando è:Assuming that you name the application "HexViewer", the command line usage is:

HexViewer <filename> -Fault

Facoltativamente, -Fault specificare per provare intenzionalmente a perdere l'handle interrompendo il thread in una determinata finestra.Optionally specify -Fault to intentionally attempt to leak the handle by aborting the thread in a certain window. Utilizzare lo strumento Perfmon. exe di Windows per monitorare i conteggi degli handle durante l'inserimento degli errori.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);
                }
            }

        }
    }
}

Commenti

La SafeHandle classe fornisce una finalizzazione critica delle risorse di gestione, impedendo il recupero anticipato degli handle da parte di Garbage Collection e il riciclaggio da parte di Windows per fare riferimento a oggetti non gestiti indesiderati.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.

Questo argomento include le sezioni seguenti:This topic includes the following sections:

Perché SafeHandle? Why SafeHandle?
Funzionalità di SafeHandle What SafeHandle does
Classi derivate da SafeHandleClasses derived from SafeHandle

Perché SafeHandle?Why SafeHandle?

Prima del .NET Framework versione 2,0, tutti gli handle del sistema operativo potevano essere incapsulati IntPtr solo nell'oggetto wrapper gestito.Before the .NET Framework version 2.0, all operating system handles could only be encapsulated in the IntPtr managed wrapper object. Sebbene si trattasse di un modo pratico per interagire con il codice nativo, gli handle potevano essere persi da eccezioni asincrone, ad esempio un thread che si interrompe in modo imprevisto o un overflow dello stack.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. Queste eccezioni asincrone costituiscono un ostacolo per la pulizia delle risorse del sistema operativo e possono verificarsi praticamente ovunque nell'app.These asynchronous exceptions are an obstacle to cleaning up operating system resources, and they can occur almost anywhere in your app.

Sebbene le sostituzioni Object.Finalize al metodo consentano la pulizia delle risorse non gestite quando un oggetto viene sottoposto a Garbage Collection, in alcune circostanze è possibile recuperare gli oggetti finalizzabili da Garbage Collection durante l'esecuzione di un metodo all'interno di un Platform Invoke chiamare.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 un finalizzatore libera l'handle passato a tale chiamata platform invoke, potrebbe causare la gestione del danneggiamento.If a finalizer frees the handle passed to that platform invoke call, it could lead to handle corruption. È anche possibile recuperare l'handle mentre il metodo viene bloccato durante una chiamata di platform invoke, ad esempio durante la lettura di un file.The handle could also be reclaimed while your method is blocked during a platform invoke call, such as while reading a file.

In modo più critico, poiché Windows riciclo in modo aggressivo gli handle, un handle può essere riciclato e puntare a un'altra risorsa che potrebbe contenere dati sensibili.More critically, because Windows aggressively recycles handles, a handle could be recycled and point to another resource that might contain sensitive data. Questa operazione è nota come un attacco di riciclo e può potenzialmente danneggiare i dati e rappresentare una minaccia per la sicurezza.This is known as a recycle attack and can potentially corrupt data and be a security threat.

Funzionalità di SafeHandleWhat SafeHandle does

La SafeHandle classe semplifica molti di questi problemi di durata degli oggetti ed è integrata con Platform Invoke in modo che le risorse del sistema operativo non vengano perse.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 classe risolve i problemi di durata degli oggetti assegnando e rilasciando handle senza interruzioni.The SafeHandle class resolves object lifetime issues by assigning and releasing handles without interruption. Contiene un finalizzatore critico che garantisce che l'handle venga chiuso ed è garantito che venga eseguito durante gli AppDomain incarichi imprevisti, anche nei casi in cui si presuppone che la chiamata platform invoke si trovi in uno stato danneggiato.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.

Dal SafeHandle momento che CriticalFinalizerObjecteredita da, tutti i finalizzatori non critici vengono chiamati prima di tutti i finalizzatori critici.Because SafeHandle inherits from CriticalFinalizerObject, all the noncritical finalizers are called before any of the critical finalizers. I finalizzatori vengono chiamati su oggetti che non sono più attivi durante lo stesso Garbage Collection superato.The finalizers are called on objects that are no longer live during the same garbage collection pass. Un FileStream oggetto, ad esempio, può eseguire un normale finalizzatore per scaricare i dati memorizzati nel buffer esistenti senza il rischio che l'handle venga perso o riciclato.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. Questo ordinamento molto debole tra finalizzatori critici e non critici non è destinato all'uso generale.This very weak ordering between critical and noncritical finalizers is not intended for general use. Esiste principalmente per facilitare la migrazione di librerie esistenti consentendo a tali librerie di utilizzare SafeHandle senza modificare la semantica.It exists primarily to assist in the migration of existing libraries by allowing those libraries to use SafeHandle without altering their semantics. Inoltre, il finalizzatore critico e qualsiasi elemento chiamato, ad esempio il SafeHandle.ReleaseHandle() metodo, deve trovarsi in un'area a esecuzione vincolata.Additionally, the critical finalizer and anything it calls, such as the SafeHandle.ReleaseHandle() method, must be in a constrained execution region. Questa operazione impone vincoli sul codice che è possibile scrivere all'interno del grafico delle chiamate del finalizzatore.This imposes constraints on what code can be written within the finalizer's call graph.

Le operazioni di Platform Invoke incrementano automaticamente il conteggio dei riferimenti degli handle SafeHandle incapsulati da un oggetto e ne decrementano il numero al completamento.Platform invoke operations automatically increment the reference count of handles encapsulated by a SafeHandle and decrement them upon completion. Ciò garantisce che l'handle non venga riciclato o chiuso in modo imprevisto.This ensures that the handle will not be recycled or closed unexpectedly.

È possibile specificare la proprietà dell'handle sottostante quando si costruiscono SafeHandle oggetti fornendo un valore ownsHandle all'argomento nel SafeHandle costruttore della 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. Consente di controllare se SafeHandle l'oggetto rilascerà l'handle dopo l'eliminazione dell'oggetto.This controls whether the SafeHandle object will release the handle after the object has been disposed. Questa operazione è utile per gli handle con requisiti di durata specifici o per l'utilizzo di un handle la cui durata è controllata da un altro utente.This is useful for handles with peculiar lifetime requirements or for consuming a handle whose lifetime is controlled by someone else.

Classi derivate da SafeHandleClasses derived from SafeHandle

SafeHandleè una classe wrapper astratta per gli handle del sistema operativo.SafeHandle is an abstract wrapper class for operating system handles. La derivazione da questa classe è difficile.Deriving from this class is difficult. Utilizzare invece le classi derivate nello spazio dei nomi Microsoft.Win32.SafeHandles che forniscono handle sicuri per gli elementi seguenti:Instead, use the derived classes in the Microsoft.Win32.SafeHandles namespace that provide safe handles for the following:

Note per gli eredi

Per creare una classe derivata da SafeHandle, è necessario saper creare e liberare un handle del sistema operativo.To create a class derived from SafeHandle, you must know how to create and free an operating system handle. Questo processo è diverso per i diversi tipi di handle perché alcuni usano la funzione CloseHandle , mentre altri usano funzioni più specifiche come 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. Per questo motivo, è necessario creare una classe derivata di SafeHandle per ogni tipo di handle del sistema operativo che si desidera includere in un handle sicuro.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.

Quando si eredita da SafeHandle, è necessario eseguire l'override dei seguenti membri: IsInvalid e ReleaseHandle().When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle().

È inoltre necessario fornire un costruttore senza parametri che chiama il costruttore di base con un valore che rappresenta un valore Boolean SafeHandle di handle non valido e un valore che indica se l'handle nativo è di proprietà di e, di conseguenza, deve essere liberato SafeHandle quando è stato eliminato.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.

Costruttori

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

Inizializza una nuova istanza della classe SafeHandle con il valore di handle non valido specificato.Initializes a new instance of the SafeHandle class with the specified invalid handle value.

Campi

handle handle handle handle

Specifica l'handle di cui eseguire il wrapping.Specifies the handle to be wrapped.

Proprietà

IsClosed IsClosed IsClosed IsClosed

Ottiene un valore che indica se l'handle è chiuso.Gets a value indicating whether the handle is closed.

IsInvalid IsInvalid IsInvalid IsInvalid

Quando è sottoposto a override in una classe derivata, consente di ottenere un valore che indica se il valore dell'handle non è valido.When overridden in a derived class, gets a value indicating whether the handle value is invalid.

Metodi

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

Contrassegna l'handle per il rilascio delle risorse.Marks the handle for releasing and freeing resources.

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

Incrementa manualmente il numero di riferimenti nelle istanze di SafeHandle.Manually increments the reference counter on SafeHandle instances.

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

Restituisce il valore del campo handle.Returns the value of the handle field.

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

Decrementa manualmente il numero di riferimenti in un'istanza di SafeHandle.Manually decrements the reference counter on a SafeHandle instance.

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

Rilascia tutte le risorse usate dalla classe SafeHandle.Releases all resources used by the SafeHandle class.

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

Rilascia le risorse non gestite utilizzate dalla classe SafeHandle specificando se eseguire una normale operazione di eliminazione.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 l'oggetto specificato è uguale all'oggetto corrente.Determines whether the specified object is equal to the current object.

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

Libera tutte le risorse associate all'handle.Frees all resources associated with the handle.

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

Funge da funzione hash predefinita.Serves as the default hash function.

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

Ottiene l'oggetto Type dell'istanza corrente.Gets the Type of the current instance.

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

Crea una copia superficiale dell'oggetto Object corrente.Creates a shallow copy of the current Object.

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

Quando viene sottoposto a override in una classe derivata, esegue il codice necessario per liberare l'handle.When overridden in a derived class, executes the code required to free the handle.

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

Imposta l'handle sull'handle preesistente specificato.Sets the handle to the specified pre-existing handle.

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

Contrassegna un handle come non più utilizzato.Marks a handle as no longer used.

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

Restituisce una stringa che rappresenta l'oggetto corrente.Returns a string that represents the current object.

(Inherited from Object)

Sicurezza

InheritanceDemand
per l'attendibilità totale per gli eredi.for full trust for inheritors. Questo membro non può essere ereditato da codice parzialmente attendibile.This member cannot be inherited by partially trusted code.

SecurityCriticalAttribute
richiede l'attendibilità totale per il chiamante immediato.requires full trust for the immediate caller. Questo membro non può essere utilizzato da codice parzialmente attendibile o trasparente.This member cannot be used by partially trusted or transparent code.

Si applica a

Vedi anche