SafeHandle Klasse
Definition
Wichtig
Einige Informationen beziehen sich auf Vorabversionen, die vor dem Release ggf. grundlegend überarbeitet werden. Microsoft übernimmt hinsichtlich der hier bereitgestellten Informationen keine Gewährleistungen, seien sie ausdrücklich oder konkludent.
Stellt eine Wrapperklasse für Betriebssystemhandles dar. Die Klasse muss geerbt werden.
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
- Vererbung
-
SafeHandle
- Vererbung
- Abgeleitet
- Attribute
- Implementiert
Beispiele
Im folgenden Codebeispiel wird ein benutzerdefinierter sicherer Handle für einen Betriebssystemdateihandpunkt erstellt, der von SafeHandleZeroOrMinusOneIsInvalid. Es liest Bytes aus einer Datei und zeigt ihre Hexadezimalwerte an. Außerdem enthält es einen Fehlertest-Ziehpunkt, der dazu führt, dass der Thread abgebrochen wird, der Handlewert jedoch frei ist. Bei Verwendung eines IntPtr zu darstellenden Handles wird der Handle gelegentlich aufgrund des asynchronen Threadabbruchs geleeckt.
Sie benötigen eine Textdatei im gleichen Ordner wie die kompilierte Anwendung. Angenommen, Sie nennen die Anwendung "HexViewer", die Befehlszeilennutzung lautet:
HexViewer <filename> -Fault
-Fault Geben Sie optional an, dass sie absichtlich versuchen, den Handle zu verlecken, indem Sie den Thread in einem bestimmten Fenster abbricht. Verwenden Sie das Windows Perfmon.exe-Tool, um die Anzahl der Zähler zu überwachen, während Fehler injiziert werden.
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);
}
}
}
}
}
Hinweise
Die SafeHandle Klasse stellt eine kritische Finalisierung von Handle-Ressourcen bereit, um zu verhindern, dass Handle vorzeitig durch die Garbage Collection zurückgegeben werden und von Windows wiederverwendet werden, um auf nicht verwaltete Objekte zu verweisen.
Dieses Thema enthält die folgenden Abschnitte:
Warum SafeHandle?
Was SafeHandle tut
Von SafeHandle abgeleitete Klassen
Warum SafeHandle?
Vor der .NET Framework Version 2.0 konnten alle Betriebssystemhandpunkte nur im IntPtr verwalteten Wrapperobjekt gekapselt werden. Während dies eine bequeme Möglichkeit war, mit systemeigenem Code zu interagieren, könnten Handle durch asynchrone Ausnahmen, z. B. einen Threadabbruch unerwartet oder einen Stapelüberlauf, verloren gehen. Diese asynchronen Ausnahmen sind ein Hindernis für die Reinigung von Betriebssystemressourcen, und sie können fast überall in Ihrer App auftreten.
Obwohl die Methode die Bereinigung von nicht verwalteten Ressourcen ermöglicht, wenn ein Objekt nicht verwaltet wird, kann in einigen Fällen fertige Objekte durch die Garbage Collection während der Ausführung einer Methode innerhalb eines Plattformaufrufs Object.Finalize wieder abgerufen werden. Wenn ein Finalizer den an diesen Plattformaufruf übergebenen Handle freigibt, könnte es zu einer Korruption führen. Der Handle kann auch wieder abgerufen werden, während die Methode während eines Plattformaufrufs blockiert wird, z. B. während des Lesens einer Datei.
Kritischer, da Windows angriffsbereite Griffe wiederverwenden, könnte ein Handle wiederverwendet werden und auf eine andere Ressource verweisen, die vertrauliche Daten enthalten könnte. Dies ist als Recyclingangriff bekannt und kann potenziell beschädigte Daten sein und eine Sicherheitsbedrohung sein.
Was SafeHandle tut
Die SafeHandle Klasse vereinfacht mehrere dieser Objektlebensdauerprobleme und ist mit plattformübergreifendem Aufruf integriert, sodass Die Betriebssystemressourcen nicht verloren gehen. Die SafeHandle Klasse löst Objektlebensdauerprobleme durch Zuweisen und Freigeben von Handle ohne Unterbrechung. Es enthält einen kritischen Finalizer, der sicherstellt, dass der Handle geschlossen wird und während unerwarteter Unloads ausgeführt AppDomain wird, auch in Fällen, in denen der Aufruf der Plattform angenommen wird, sich in einem beschädigten Zustand befinden.
Da SafeHandle erbt von CriticalFinalizerObject, werden alle nicht kritischen Finalizer aufgerufen, bevor eine der kritischen Finalizer aufgerufen wird. Die Finalizer werden bei Objekten aufgerufen, die während desselben Garbage Collection-Pass nicht mehr live sind. Ein Objekt kann z. B. einen normalen Finalizer ausführen, um vorhandene pufferte Daten auszulöschen, ohne dass das Risiko besteht, FileStream dass der Handle geleckt oder recycelt wird. Diese sehr schwache Reihenfolge zwischen kritischen und nichtkritischen Finalizern ist nicht für die allgemeine Verwendung vorgesehen. Es besteht in erster Linie darin, die Migration vorhandener Bibliotheken zu unterstützen, indem diese Bibliotheken die Verwendung SafeHandle ermöglichen, ohne ihre Semantik zu ändern. Darüber hinaus muss der kritische Finalizer und alles, was sie aufruft, wie SafeHandle.ReleaseHandle() die Methode, in einem eingeschränkten Ausführungsbereich sein. Dadurch werden Einschränkungen für den Code im Aufrufdiagramm des Finalizers auferlegt.
Die Plattform ruft Vorgänge automatisch auf, um die Referenzanzahl von Handlen zu erhöhen, die nach Abschluss durch eine SafeHandle Kapselung gekapselt werden. Dadurch wird sichergestellt, dass der Handle nicht unerwartet wiederverwendet oder geschlossen wird.
Sie können den Besitz des zugrunde liegenden Handles beim Erstellen SafeHandle von Objekten angeben, indem Sie einen Wert für das ownsHandle Argument im SafeHandle Klassenkonstruktor angeben. Dadurch wird gesteuert, ob das SafeHandle Objekt den Handle freigeben wird, nachdem das Objekt entsorgt wurde. Dies ist nützlich für Handle mit besonderen Lebensdaueranforderungen oder für den Verbrauch eines Handles, dessen Lebensdauer von einer anderen Person gesteuert wird.
Von SafeHandle abgeleitete Klassen
SafeHandle ist eine abstrakte Wrapperklasse für Betriebssystemhandpunkte. Das Ableiten von dieser Klasse ist schwierig. Verwenden Sie stattdessen die abgeleiteten Klassen im Microsoft.Win32.SafeHandles-Namespace, die sichere Handles für Folgendes bereitstellen:
Dateien (die SafeFileHandle Klasse).
Speicher zugeordnete Dateien (die SafeMemoryMappedFileHandle Klasse).
Rohre (die SafePipeHandle Klasse).
Speicheransichten (klasse SafeMemoryMappedViewHandle ).
Kryptografiekonstrukte (die SafeNCryptHandle, , SafeNCryptKeyHandleSafeNCryptProviderHandleund SafeNCryptSecretHandle Klassen).
Prozesse (die SafeProcessHandle Klasse).
Registrierungsschlüssel (die SafeRegistryHandle Klasse).
Warten Sie die Handle (die SafeWaitHandle Klasse).
Hinweise für Ausführende
Um eine von ihnen SafeHandleabgeleitete Klasse zu erstellen, müssen Sie wissen, wie Sie einen Betriebssystemhandpunkt erstellen und freigeben. Dieser Prozess unterscheidet sich von unterschiedlichen Handle-Typen, da einige die CloseHandle-Funktion verwenden, während andere bestimmte Funktionen wie "UnmapViewOfFile " oder "FindClose" verwenden. Aus diesem Grund müssen Sie eine abgeleitete Klasse für SafeHandle jeden Betriebssystemhandtyp erstellen, den Sie in einen sicheren Handle umschließen möchten.
Wenn Sie von SafeHandle erben, müssen die folgenden Member überschrieben werden: IsInvalid und ReleaseHandle().
Sie sollten auch einen öffentlichen parameterlosen Konstruktor bereitstellen, der den Basiskonstruktor mit einem Wert aufruft, der einen ungültigen Handle-Wert darstellt, und einen Boolean Wert, der angibt, ob der native Handle im Besitz des SafeHandle systemeigenen Handles ist und daher frei sein sollte, wenn dies SafeHandle entsorgt wurde.
Konstruktoren
| SafeHandle() | |
| SafeHandle(IntPtr, Boolean) |
Initialisiert eine neue Instanz der SafeHandle-Klasse mit dem angegebenen ungültigen Handlewert. |
Felder
| handle |
Gibt das zu umschließende Handle an. |
Eigenschaften
| IsClosed |
Ruft einen Wert ab, der angibt, ob das Handle geschlossen ist. |
| IsInvalid |
Ruft beim Überschreiben in einer abgeleiteten Klasse einen Wert ab, der angibt, ob dieses Handle ungültig ist. |
Methoden
| Close() |
Markiert das Handle für das Freigeben und Verfügbarmachen von Ressourcen. |
| DangerousAddRef(Boolean) |
Inkrementiert manuell den Verweiszähler für SafeHandle-Instanzen. |
| DangerousGetHandle() |
Gibt den Wert des handle-Felds zurück. |
| DangerousRelease() |
Dekrementiert manuell den Verweiszähler für eine SafeHandle-Instanz. |
| Dispose() |
Gibt alle von der SafeHandle-Klasse verwendeten Ressourcen frei. |
| Dispose(Boolean) |
Gibt die von der SafeHandle-Klasse verwendeten, nicht verwalteten Ressourcen frei und gibt an, ob ein normaler Freigabevorgang ausgeführt werden soll. |
| Equals(Object) |
Bestimmt, ob das angegebene Objekt gleich dem aktuellen Objekt ist. (Geerbt von Object) |
| Finalize() |
Gibt alle dem Handle zugeordneten Ressourcen frei. |
| GetHashCode() |
Fungiert als Standardhashfunktion. (Geerbt von Object) |
| GetType() |
Ruft den Type der aktuellen Instanz ab. (Geerbt von Object) |
| MemberwiseClone() |
Erstellt eine flache Kopie des aktuellen Object. (Geerbt von Object) |
| ReleaseHandle() |
Führt beim Überschreiben in einer abgeleiteten Klasse den Code aus, der für das Freigeben des Handles erforderlich ist. |
| SetHandle(IntPtr) |
Legt das Handle für das angegebene, bereits vorhandene Handle fest. |
| SetHandleAsInvalid() |
Markiert ein Handle als nicht mehr verwendet. |
| ToString() |
Gibt eine Zeichenfolge zurück, die das aktuelle Objekt darstellt. (Geerbt von Object) |