Handles sécurisés et finalisation critique

Avant la version 2.0 du .NET Framework, tous les handles du système d'exploitation pouvaient être encapsulés uniquement dans l'objet de wrapper managé IntPtr. Même si c'était un moyen pratique d'interagir avec le code natif, les handles pouvaient être divulgués par des exceptions asynchrones, telles qu'un abandon inattendu de 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 peuvent se produire presque n'importe où dans votre programme. Elles sont susceptibles de se produire dans des applications qui utilisent un hôte exécutant du code managé, tel que Microsoft SQL Server.

Dans certaines circonstances, les objets finalisables pouvaient être récupérés par le garbage collection lors de l'exécution d'une méthode dans un appel de plateforme. Si un finaliseur libérait le handle passé à cet appel de plateforme, cela pouvait entraîner l'altération du handle. Le handle pouvait également être récupéré lorsque votre méthode était bloquée pendant un appel de plateforme, comme lors de la lecture d'un fichier.

Plus important : étant donné que Windows recycle les handles de manière agressive, un handle pouvait être recyclé et pointer vers une autre ressource pouvant contenir des données sensibles. C'est ce qu'on appelle une attaque de recyclage ; elle peut éventuellement endommager des données et être une menace pour la sécurité.

Depuis le .NET Framework 2.0, la classe SafeHandle simplifie plusieurs de ces problèmes de durée de vie des objets et est intégrée à l'appel de plateforme de sorte que ces ressources de système d'exploitation ne soient pas divulguées. La classe SafeHandle résout les problèmes de durée de vie des objets en assignant et en libérant des handles sans interruption. Elle contient un finaliseur critique qui garantit la fermeture du handle et son exécution pendant le déchargement de AppDomain, même dans les cas où l'appel de plateforme est censé être dans un état endommagé.

Étant donné que SafeHandle hérite de CriticalFinalizerObject, tous les finaliseurs non critiques sont appelés avant les finaliseurs critiques. Les finaliseurs sont appelés sur des objets qui ne sont plus vivants pendant le même passage de garbage collection. Par exemple, un objet FileStream peut exécuter un finaliseur normal pour nettoyer les données mises en mémoire tampon existantes sans risquer que le handle soit divulgué ou recyclé. Ce classement très subtil entre les finaliseurs critiques et non critiques n'est pas destiné à être utilisé de manière générale. Il existe essentiellement pour faciliter la migration de bibliothèques existantes en leur permettant d'utiliser SafeHandle sans modifier leur sémantique. En outre, le finaliseur critique et tout ce qu'il peut appeler, par exemple la méthode SafeHandle.ReleaseHandle(), doivent être dans une région d'exécution limitée. Cela impose des contraintes sur le code qui peut être écrit dans le graphique des appels du finaliseur.

Depuis le .NET Framework version 2.0, les opérations d'appel de plateforme incrémentent automatiquement le décompte de références de handles encapsulés par un SafeHandle et les décrémentent lors de l'achèvement. Cela garantit que le handle ne sera pas recyclé ou fermé de manière inattendue.

Vous pouvez spécifier la propriété du handle sous-jacent lors de la construction d'objets SafeHandle. Cela contrôle si l'objet SafeHandle libérera le handle après la suppression de l'objet. C'est utile pour les handles avec des spécifications de durée de vie particulières ou pour consommer un handle dont la durée de vie est contrôlée par une autre personne.

Classes de handles sécurisés

La classe SafeHandle dans l'espace de noms System.Runtime.InteropServices est une classe wrapper abstraite pour les handles du 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 et canaux.

  • Vues de la mémoire.

  • Constructions de chiffrement.

  • Clés de registre.

  • Handles d'attente.

Voir aussi

Référence

SafeHandle

CriticalHandle

CriticalFinalizerObject