SafeHandle Classe
Définition
Important
Certaines informations portent sur la préversion du produit qui est susceptible d’être en grande partie modifiée avant sa publication. Microsoft exclut toute garantie, expresse ou implicite, concernant les informations fournies ici.
Représente une classe wrapper pour des handles de système d'exploitation. Cette classe doit être héritée.
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
- Héritage
-
SafeHandle
- 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 de système d’exploitation, dérivant de SafeHandleZeroOrMinusOneIsInvalid. Il lit les octets d’un fichier et affiche leurs valeurs hexadécimales. Il contient également un harnais de test d’erreur qui entraîne l’abandon du thread, mais la valeur du handle est libérée. Lors de l’utilisation d’un IntPtr pour représenter des handles, le handle est occasionnellement divulguée en raison de l’abandon asynchrone du thread.
Vous aurez besoin d’un fichier texte dans le même dossier que l’application compilée. En supposant que vous nommez l’application « HexViewer », l’utilisation de la ligne de commande est la suivante :
HexViewer <filename> -Fault
Si vous le souhaitez, spécifiez -Fault
pour tenter intentionnellement de faire fuiter le handle en abandonnant le thread dans une certaine fenêtre. Utilisez l’outil Windows Perfmon.exe pour surveiller le nombre de handles lors de l’injection d’erreurs.
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);
}
}
}
}
}
Remarques
La SafeHandle classe fournit la finalisation critique des ressources de handle, ce qui empêche les handles d’être récupérés prématurément par le garbage collection et d’être recyclés par Windows pour référencer des objets non managés involontaires.
Cette rubrique contient les sections suivantes :
Pourquoi SafeHandle ?Que fait SafeHandleClasses dérivées de SafeHandle
Pourquoi SafeHandle ?
Avant .NET Framework version 2.0, tous les handles de système d’exploitation ne pouvaient être encapsulés que dans l’objet IntPtr wrapper managé. Bien qu’il s’agissait d’un moyen pratique d’interagir avec du code natif, les handles pouvaient être divulgués par des exceptions asynchrones, comme un abandon inattendu d’un thread ou un dépassement de capacité de la pile. Ces exceptions asynchrones constituent un obstacle au nettoyage des ressources du système d’exploitation, et elles peuvent se produire presque n’importe où dans votre application.
Bien que les remplacements de la méthode autorisent le Object.Finalize nettoyage des ressources non managées lorsqu’un objet est en cours de nettoyage de la mémoire, dans certaines circonstances, les objets finalisables peuvent être récupérés par garbage collection lors de l’exécution d’une méthode dans un appel d’appel de plateforme. Si un finaliseur libère le handle passé à cet appel d’appel d’appel de plateforme, cela peut entraîner la gestion de l’altération. Le handle peut également être récupéré lorsque votre méthode est bloquée lors d’un appel d’appel de plateforme, par exemple lors de la lecture d’un fichier.
Plus critiquement, étant donné que Windows recycle de manière agressive les handles, un handle peut être recyclé et pointer vers une autre ressource qui peut contenir des données sensibles. Il s’agit d’une attaque de recyclage qui peut potentiellement endommager des données et constituer une menace de sécurité.
Ce que fait SafeHandle
La SafeHandle classe simplifie plusieurs de ces problèmes de durée de vie des objets et est intégrée à l’appel de plateforme afin que les ressources du système d’exploitation ne soient pas divulguées. La SafeHandle classe résout les problèmes de durée de vie des objets en affectant et en libérant des handles sans interruption. Il contient un finaliseur critique qui garantit que le handle est fermé et qu’il est garanti de s’exécuter pendant les déchargements inattendus AppDomain , même dans les cas où l’appel d’appel de plateforme est supposé être dans un état endommagé.
Étant donné qu’hérite SafeHandle de CriticalFinalizerObject, tous les finaliseurs non critiques sont appelés avant l’un des finaliseurs critiques. Les finaliseurs sont appelés sur les objets qui ne sont plus actifs pendant le même passage de garbage collection. 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 handle. Cet ordre très faible entre les finaliseurs critiques et non critiques n’est pas destiné à une utilisation générale. Il existe principalement pour faciliter la migration des bibliothèques existantes en permettant à ces bibliothèques d’utiliser SafeHandle sans modifier leur sémantique. En outre, le finaliseur critique et tout ce qu’il appelle, comme la SafeHandle.ReleaseHandle() méthode, doivent se trouver dans une région d’exécution contrainte. Cela impose des contraintes sur le code qui peut être écrit dans le graphique d’appel du finaliseur.
Les opérations d’appel de plateforme incrémentent automatiquement le nombre de références de handles encapsulés par un SafeHandle et les décrémentent à l’achèvement. Cela garantit que la poignée ne sera pas recyclée ou fermée de manière inattendue.
Vous pouvez spécifier la propriété du handle sous-jacent lors de la construction d’objets SafeHandle en fournissant une valeur à l’argument ownsHandle
dans le constructeur de SafeHandle classe. Cela contrôle si l’objet SafeHandle libère le handle une fois l’objet supprimé. Cela est utile pour les poignées avec des exigences de durée de vie particulières ou pour la consommation d’un handle dont la durée de vie est contrôlée par quelqu’un d’autre.
Classes dérivées de SafeHandle
SafeHandle est une classe wrapper abstraite pour les handles de système d’exploitation. La dérivation de cette classe est difficile. 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 :
Fichiers (classe SafeFileHandle ).
Fichiers mappés en mémoire (classe SafeMemoryMappedFileHandle ).
Pipes (classe SafePipeHandle ).
Affichages mémoire (classe SafeMemoryMappedViewHandle ).
Constructions de chiffrement (classes SafeNCryptHandle, SafeNCryptKeyHandle, SafeNCryptProviderHandleet SafeNCryptSecretHandle ).
Processus (classe SafeProcessHandle ).
Clés de Registre (classe SafeRegistryHandle ).
Handles d’attente (classe SafeWaitHandle ).
Notes pour les responsables de l’implémentation
Pour créer une classe dérivée de SafeHandle, vous devez savoir comment créer et libérer un handle de système d’exploitation. Ce processus est différent pour différents types de handles, car certains utilisent la fonction CloseHandle , tandis que d’autres utilisent des fonctions plus spécifiques telles que UnmapViewOfFile ou FindClose. Pour cette raison, vous devez créer une classe dérivée de pour chaque type de SafeHandle handle de système d’exploitation que vous souhaitez inclure dans un handle sécurisé.
Lorsque vous héritez de SafeHandle, vous devez substituer les membres suivants : IsInvalid et ReleaseHandle().
Vous devez également fournir un constructeur public sans paramètre qui appelle le constructeur de base avec une valeur qui représente une valeur de handle non valide, et une Boolean valeur indiquant si le handle natif appartient à et SafeHandle doit donc être libéré lorsque celui-ci SafeHandle a été supprimé.
Constructeurs
SafeHandle() |
Représente une classe wrapper pour des handles de système d'exploitation. Cette classe doit être héritée. |
SafeHandle(IntPtr, Boolean) |
Initialise une nouvelle instance de la classe SafeHandle avec la valeur de handle non valide spécifiée. |
Champs
handle |
Spécifie le handle à encapsuler. |
Propriétés
IsClosed |
Obtient une valeur indiquant si le handle est fermé. |
IsInvalid |
En cas de substitution dans une classe dérivée, obtient une valeur indiquant si la valeur du handle n'est pas valide. |
Méthodes
Close() |
Marque le handle pour libérer des ressources. |
DangerousAddRef(Boolean) |
Incrémente manuellement le compteur de références sur les instances de SafeHandle. |
DangerousGetHandle() |
Retourne la valeur du champ handle. |
DangerousRelease() |
Décrémente manuellement le compteur de références sur une instance de SafeHandle. |
Dispose() |
Libère toutes les ressources utilisées par la classe SafeHandle. |
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. |
Equals(Object) |
Détermine si l'objet spécifié est égal à l'objet actuel. (Hérité de Object) |
Finalize() |
Libère toutes les ressources associées au handle. |
GetHashCode() |
Fait office de fonction de hachage par défaut. (Hérité de Object) |
GetType() |
Obtient le Type de l'instance actuelle. (Hérité de Object) |
MemberwiseClone() |
Crée une copie superficielle du Object actuel. (Hérité de Object) |
ReleaseHandle() |
En cas de substitution dans une classe dérivée, exécute le code nécessaire pour libérer le handle. |
SetHandle(IntPtr) |
Définit le handle au handle préexistant spécifié. |
SetHandleAsInvalid() |
Marque un handle comme n'étant plus utilisé. |
ToString() |
Retourne une chaîne qui représente l'objet actuel. (Hérité de Object) |
S’applique à
Voir aussi
Commentaires
Envoyer et afficher des commentaires pour