SafeHandle SafeHandle SafeHandle SafeHandle Class

Définition

Représente une classe wrapper pour des handles de système d'exploitation.Represents a wrapper class for operating system handles. Cette classe doit être héritée.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
Héritage
Dérivé
Attributs
Implémente

Exemples

L’exemple de code suivant crée un handle sécurisé personnalisé pour un handle de fichier du système d’exploitation SafeHandleZeroOrMinusOneIsInvalid, dérivant de.The following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. Il lit les octets d’un fichier et affiche leurs valeurs hexadécimales.It reads bytes from a file and displays their hexadecimal values. Il contient également un atelier de test d’erreur qui provoque l’abandon du thread, mais la valeur du handle est libérée.It also contains a fault testing harness that causes the thread to abort, but the handle value is freed. Lors de l' IntPtr utilisation d’un pour représenter des handles, le handle subit parfois une fuite en raison de l’abandon asynchrone du thread.When using an IntPtr to represent handles, the handle is occasionally leaked due to the asynchronous thread abort.

Vous aurez besoin d’un fichier texte dans le même dossier que l’application compilée.You will need a text file in the same folder as the compiled application. En supposant que vous nommez l’application « HexViewer », l’utilisation de la ligne de commande est la suivante :Assuming that you name the application "HexViewer", the command line usage is:

HexViewer <filename> -Fault

Si vous le -Fault souhaitez, spécifiez intentionnellement une fuite du handle en abandonnant le thread dans une certaine fenêtre.Optionally specify -Fault to intentionally attempt to leak the handle by aborting the thread in a certain window. Utilisez l’outil Windows Perfmon. exe pour surveiller les nombres de handles en injectant des erreurs.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);
                }
            }

        }
    }
}

Remarques

La SafeHandle classe fournit la finalisation critique des ressources de handle, empêchant les handles d’être récupérées prématurément par garbage collection et de les recycler par Windows pour référencer des objets non managés involontaires.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.

Cette rubrique comporte les sections suivantes :This topic includes the following sections:

Pourquoi SafeHandle ? Why SafeHandle?
Ce que SafeHandle fait What SafeHandle does
Classes dérivées de SafeHandleClasses derived from SafeHandle

Pourquoi SafeHandle ?Why SafeHandle?

Avant la version .NET Framework 2,0, tous les handles de système d’exploitation pouvaient uniquement être IntPtr encapsulés dans l’objet wrapper managé.Before the .NET Framework version 2.0, all operating system handles could only be encapsulated in the IntPtr managed wrapper object. Bien qu’il s’agisse d’un moyen pratique d’interagir avec le code natif, les handles peuvent être divulgués par des exceptions asynchrones, par exemple un thread qui abandonne de manière inattendue ou un dépassement de capacité de la pile.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. Ces exceptions asynchrones sont un obstacle au nettoyage des ressources du système d’exploitation et peuvent se produire presque n’importe où dans votre application.These asynchronous exceptions are an obstacle to cleaning up operating system resources, and they can occur almost anywhere in your app.

Bien que les substitutions à Object.Finalize la méthode autorisent le nettoyage des ressources non managées lorsqu’un objet est récupéré par le garbage collector, dans certains cas, les objets finalisables peuvent être récupérés par garbage collection lors de l’exécution d’une méthode dans un appel de code non managé invoqu.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 finaliseur libère le handle passé à cet appel de code non managé, cela peut entraîner une altération de la gestion.If a finalizer frees the handle passed to that platform invoke call, it could lead to handle corruption. Le descripteur peut également être récupéré pendant que votre méthode est bloquée pendant un appel de code non managé, par exemple lors de la lecture d’un fichier.The handle could also be reclaimed while your method is blocked during a platform invoke call, such as while reading a file.

Plus critique, étant donné que Windows recycle de manière agressive les descripteurs, un descripteur peut être recyclé et pointer vers une autre ressource qui peut contenir des données sensibles.More critically, because Windows aggressively recycles handles, a handle could be recycled and point to another resource that might contain sensitive data. C’est ce qu’on appelle une attaque de recyclage et peut endommager les données et constituer une menace pour la sécurité.This is known as a recycle attack and can potentially corrupt data and be a security threat.

Ce que SafeHandle faitWhat SafeHandle does

La SafeHandle classe simplifie plusieurs de ces problèmes de durée de vie des objets et est intégrée à l’appel de code non managé afin que les ressources du système d’exploitation ne soient pas divulguées.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 résout les problèmes de durée de vie des objets en assignant et en libérant des handles sans interruption.The SafeHandle class resolves object lifetime issues by assigning and releasing handles without interruption. Elle contient un finaliseur critique qui garantit que le handle est fermé et qu’il est garanti s’exécuter AppDomain pendant les déchargements inattendus, même dans les cas où l’appel de code non managé est supposé être dans un état endommagé.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.

Comme SafeHandle hérite de CriticalFinalizerObject, tous les finaliseurs non critiques sont appelés avant les finaliseurs critiques.Because SafeHandle inherits from CriticalFinalizerObject, all the noncritical finalizers are called before any of the critical finalizers. Les finaliseurs sont appelés sur des objets qui ne sont plus actifs pendant le même garbage collection passe.The finalizers are called on objects that are no longer live during the same garbage collection pass. Par exemple, un FileStream objet peut exécuter un finaliseur normal pour vider les données mises en mémoire tampon existantes sans risque de fuite ou de recyclage du descripteur.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. Ce classement très faible entre les finaliseurs critiques et non critiques n’est pas destiné à une utilisation générale.This very weak ordering between critical and noncritical finalizers is not intended for general use. Il existe principalement pour faciliter la migration des bibliothèques existantes en permettant à ces bibliothèques de s' SafeHandle utiliser sans modifier leur sémantique.It exists primarily to assist in the migration of existing libraries by allowing those libraries to use SafeHandle without altering their semantics. En outre, le finaliseur critique et tout ce qu’il appelle, tel SafeHandle.ReleaseHandle() que la méthode, doivent se trouver dans une région d’exécution restreinte.Additionally, the critical finalizer and anything it calls, such as the SafeHandle.ReleaseHandle() method, must be in a constrained execution region. Cela impose des contraintes sur le code qui peut être écrit dans le graphique des appels du finaliseur.This imposes constraints on what code can be written within the finalizer's call graph.

Les opérations d’appel de code non managé incrémentent automatiquement le décompte SafeHandle de références des handles encapsulés par un et les décrémentent après l’achèvement.Platform invoke operations automatically increment the reference count of handles encapsulated by a SafeHandle and decrement them upon completion. Cela permet de s’assurer que le handle ne sera pas recyclé ou fermé de manière inattendue.This ensures that the handle will not be recycled or closed unexpectedly.

Vous pouvez spécifier la propriété du handle sous-jacent lors SafeHandle de la construction d’objets en fournissant une ownsHandle valeur à l' SafeHandle argument dans le constructeur de 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. Détermine si l' SafeHandle objet doit libérer le handle après la suppression de l’objet.This controls whether the SafeHandle object will release the handle after the object has been disposed. Cela est utile pour les handles avec des exigences de durée de vie particulières ou pour l’utilisation d’un handle dont la durée de vie est contrôlée par une autre personne.This is useful for handles with peculiar lifetime requirements or for consuming a handle whose lifetime is controlled by someone else.

Classes dérivées de SafeHandleClasses derived from SafeHandle

SafeHandleest une classe wrapper abstraite pour les handles du système d’exploitation.SafeHandle is an abstract wrapper class for operating system handles. La dérivation de cette classe est difficile.Deriving from this class is difficult. Utilisez plutôt les classes dérivées dans l'espace de noms Microsoft.Win32.SafeHandles qui fournissent des handles sécurisés pour les éléments suivants :Instead, use the derived classes in the Microsoft.Win32.SafeHandles namespace that provide safe handles for the following:

Notes pour les héritiers

Pour créer une classe dérivée SafeHandlede, vous devez savoir comment créer et libérer un handle de système d’exploitation.To create a class derived from SafeHandle, you must know how to create and free an operating system handle. Ce processus est différent pour les différents types de handle, car certains utilisent la fonction CloseHandle , tandis que d’autres utilisent des fonctions plus spécifiques telles que UnmapViewOfFile ou 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. Pour cette raison, vous devez créer une classe dérivée SafeHandle de pour chaque type de handle de système d’exploitation que vous souhaitez encapsuler dans un handle sécurisé.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.

Lorsque vous héritez de SafeHandle, vous devez substituer les membres suivants : IsInvalid et ReleaseHandle().When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle().

Vous devez également fournir un constructeur sans paramètre qui appelle le constructeur de base avec une valeur qui représente une valeur de handle non valide Boolean , et une valeur indiquant si le handle natif appartient SafeHandle à et, par conséquent, doit être libéré SafeHandle lorsque a été supprimé.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.

Constructeurs

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

Initialise une nouvelle instance de la classe SafeHandle avec la valeur de handle non valide spécifiée.Initializes a new instance of the SafeHandle class with the specified invalid handle value.

Champs

handle handle handle handle

Spécifie le handle à encapsuler.Specifies the handle to be wrapped.

Propriétés

IsClosed IsClosed IsClosed IsClosed

Obtient une valeur indiquant si le handle est fermé.Gets a value indicating whether the handle is closed.

IsInvalid IsInvalid IsInvalid IsInvalid

En cas de substitution dans une classe dérivée, obtient une valeur indiquant si la valeur du handle n'est pas valide.When overridden in a derived class, gets a value indicating whether the handle value is invalid.

Méthodes

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

Marque le handle pour libérer des ressources.Marks the handle for releasing and freeing resources.

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

Incrémente manuellement le compteur de références sur les instances de SafeHandle.Manually increments the reference counter on SafeHandle instances.

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

Retourne la valeur du champ handle.Returns the value of the handle field.

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

Décrémente manuellement le compteur de références sur une instance de SafeHandle.Manually decrements the reference counter on a SafeHandle instance.

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

Libère toutes les ressources utilisées par la classe SafeHandle.Releases all resources used by the SafeHandle class.

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

Libère les ressources non managées utilisées par la classe SafeHandle, en spécifiant s'il faut exécuter une opération de suppression normale.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)

Détermine si l'objet spécifié est identique à l'objet actuel.Determines whether the specified object is equal to the current object.

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

Libère toutes les ressources associées au handle.Frees all resources associated with the handle.

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

Fait office de fonction de hachage par défaut.Serves as the default hash function.

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

Obtient le Type de l'instance actuelle.Gets the Type of the current instance.

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

Crée une copie superficielle de l'objet Object actuel.Creates a shallow copy of the current Object.

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

En cas de substitution dans une classe dérivée, exécute le code nécessaire pour libérer le handle.When overridden in a derived class, executes the code required to free the handle.

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

Définit le handle au handle préexistant spécifié.Sets the handle to the specified pre-existing handle.

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

Marque un handle comme n'étant plus utilisé.Marks a handle as no longer used.

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

Retourne une chaîne qui représente l'objet actuel.Returns a string that represents the current object.

(Inherited from Object)

Sécurité

InheritanceDemand
pour une confiance totale pour les héritiers.for full trust for inheritors. Ce membre ne peut pas être hérité par du code de confiance partielle.This member cannot be inherited by partially trusted code.

SecurityCriticalAttribute
Requiert une confiance totale pour l’appelant immédiat.requires full trust for the immediate caller. Ce membre ne peut pas être utilisé par du code de confiance partielle ou transparent.This member cannot be used by partially trusted or transparent code.

S’applique à

Voir aussi