Freigeben über


SafeHandle-Klasse

 

Veröffentlicht: Oktober 2016

Stellt eine Wrapperklasse für Betriebssystemhandles dar. Die Klasse muss geerbt werden.

Namespace:   System.Runtime.InteropServices
Assembly:  mscorlib (in mscorlib.dll)

Vererbungshierarchie

System.Object
  System.Runtime.ConstrainedExecution.CriticalFinalizerObject
    System.Runtime.InteropServices.SafeHandle
      Microsoft.Win32.SafeHandles.SafeAccessTokenHandle
      Microsoft.Win32.SafeHandles.SafeHandleMinusOneIsInvalid
      Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid

Syntax

[SecurityCriticalAttribute]
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, 
    UnmanagedCode = true)]
public abstract class SafeHandle : CriticalFinalizerObject, IDisposable
[SecurityCriticalAttribute]
[SecurityPermissionAttribute(SecurityAction::InheritanceDemand, 
    UnmanagedCode = true)]
public ref class SafeHandle abstract : CriticalFinalizerObject, 
    IDisposable
[<AbstractClass>]
[<SecurityCriticalAttribute>]
[<SecurityPermissionAttribute(SecurityAction.InheritanceDemand,
    UnmanagedCode = true)>]
type SafeHandle = 
    class
        inherit CriticalFinalizerObject
        interface IDisposable
    end
<SecurityCriticalAttribute>
<SecurityPermissionAttribute(SecurityAction.InheritanceDemand,
    UnmanagedCode := True)>
Public MustInherit Class SafeHandle
    Inherits CriticalFinalizerObject
    Implements IDisposable

Konstruktoren

Name Beschreibung
System_CAPS_protmethod SafeHandle(IntPtr, Boolean)

Initialisiert eine neue Instanz der SafeHandle-Klasse mit dem angegebenen ungültigen Handlewert.

Eigenschaften

Name Beschreibung
System_CAPS_pubproperty IsClosed

Ruft einen Wert ab, der angibt, ob das Handle geschlossen ist.

System_CAPS_pubproperty IsInvalid

Ruft beim Überschreiben in einer abgeleiteten Klasse einen Wert ab, der angibt, ob dieses Handle ungültig ist.

Methoden

Name Beschreibung
System_CAPS_pubmethod Close()

Markiert das Handle für das Freigeben und Verfügbarmachen von Ressourcen.

System_CAPS_pubmethod DangerousAddRef(Boolean)

Inkrementiert manuell den Verweiszähler für SafeHandle-Instanzen.

System_CAPS_pubmethod DangerousGetHandle()

Gibt den Wert des handle-Felds zurück.

System_CAPS_pubmethod DangerousRelease()

Dekrementiert manuell den Verweiszähler für eine SafeHandle-Instanz.

System_CAPS_pubmethod Dispose()

Gibt alle von der SafeHandle-Klasse verwendeten Ressourcen frei.

System_CAPS_protmethod Dispose(Boolean)

Gibt die von der SafeHandle-Klasse verwendeten, nicht verwalteten Ressourcen frei und gibt an, ob ein normaler Freigabevorgang ausgeführt werden soll.

System_CAPS_pubmethod Equals(Object)

Bestimmt, ob das angegebene Objekt mit dem aktuellen Objekt identisch ist.(Geerbt von „Object“.)

System_CAPS_protmethod Finalize()

Gibt alle dem Handle zugeordneten Ressourcen frei.(Überschreibt CriticalFinalizerObject.Finalize().)

System_CAPS_pubmethod GetHashCode()

Fungiert als die Standardhashfunktion.(Geerbt von „Object“.)

System_CAPS_pubmethod GetType()

Ruft den Type der aktuellen Instanz ab.(Geerbt von „Object“.)

System_CAPS_protmethod MemberwiseClone()

Erstellt eine flache Kopie des aktuellen Object.(Geerbt von „Object“.)

System_CAPS_protmethod ReleaseHandle()

Führt beim Überschreiben in einer abgeleiteten Klasse den Code aus, der für das Freigeben des Handles erforderlich ist.

System_CAPS_protmethod SetHandle(IntPtr)

Legt das Handle für das angegebene, bereits vorhandene Handle fest.

System_CAPS_pubmethod SetHandleAsInvalid()

Markiert ein Handle als nicht mehr verwendet.

System_CAPS_pubmethod ToString()

Gibt eine Zeichenfolge zurück, die das aktuelle Objekt darstellt.(Geerbt von „Object“.)

Felder

Name Beschreibung
System_CAPS_protfield handle

Gibt das zu umschließende Handle an.

Hinweise

Die SafeHandle -Klasse stellt Kritische Finalisierung für Handleressourcen, verhindert, dass Handles vorzeitig von der Garbagecollection und von Windows zu unbeabsichtigten nicht verwaltete Objekte verweisen wiederverwendet wird.

Dieses Thema enthält die folgenden Abschnitte:

Warum SafeHandle?
Funktionsweise von SafeHandle
Von SafeHandle abgeleitete Klassen

Warum SafeHandle?

Vor .NET Framework, Version 2.0, alle Betriebssystem Handles konnte nur gekapselt werden, in der IntPtr verwaltete Wrapper-Objekt. Dies war eine bequeme Möglichkeit für die Interoperabilität mit systemeigenem Code könnte Handles durch asynchrone Ausnahmen, z. B. einen unerwarteten Threadabbruch oder einen Stapelüberlauf verloren. Diese asynchronen Ausnahmen sind ein Hindernis, das Bereinigen von Betriebssystemressourcen und praktisch von überall in Ihrer Anwendung auftreten können.

Obwohl außer Kraft gesetzt werden die Object.Finalize Methode Bereinigen nicht verwalteter Ressourcen ermöglichen, wenn ein Objekt mit Garbage Collection, in einigen Fällen wird, um finalisierbare Objekte freigegeben werden können durch eine Garbage Collection beim Ausführen einer Methode innerhalb einer Plattform aufrufen. Wenn ein Finalizer der Plattform übergebene Handle freigibt aufrufen, konnte dies um zu einer Beschädigung zu behandeln. Das Handle kann auch freigegeben werden, während Ihre Methode während eines Plattformaufrufs blockiert ist aufrufen, z. B. beim Lesen einer Datei.

Da Windows Handles aggressiv wiederverwendet wird, könnte ein Handle so, wiederverwendet werden und auf eine andere Ressource, die möglicherweise vertrauliche Daten enthalten. Dies kann wird als Angriff Wiederverwendung bezeichnet und Daten beschädigt werden und ein Sicherheitsrisiko darstellen.

Funktionsweise von SafeHandle

Die SafeHandle Klasse vereinfacht einige dieser Probleme mit der Objektlebensdauer und Plattform integriert ist aufrufen, damit Ressourcen des Betriebssystems nicht weitergegeben werden. Die SafeHandle -Klasse löst Probleme mit der Objektlebensdauer, indem Sie Handles ohne Unterbrechung zuweist und freigibt. Sie enthält einen kritischen Finalizer, der wird sichergestellt, dass das Handle geschlossen und garantiert ist, während unerwarteter AppDomain wird angenommen, dass entladen wird, auch in Fällen, wenn der Plattformaufruf in einem beschädigten Zustand befindet.

Da SafeHandle erbt von CriticalFinalizerObject, alle nicht kritischen Finalizer immer vor den kritischen Finalizer aufgerufen. Die Finalizer werden für Objekte aufgerufen, die während der gleichen Garbage Collection-Durchgangs nicht mehr aktiv sind. Angenommen, ein FileStream Objekt führe einen normalen Finalizer vorhandene gepufferte Daten ohne das Risiko des Handles verloren oder wiederverwendet wird geleert. Diese sehr schwache Reihenfolge von kritischen und nicht kritischen Finalizer ist nicht für die allgemeine Verwendung vorgesehen. Vorhanden ist, in erster Linie um bei der Migration von vorhandenen Bibliotheken unterstützen, indem Sie diesen Bibliotheken erlaubt verwenden SafeHandle ohne ihre Semantik zu verändern. Darüber hinaus die kritische Finalizer und alle aufgerufen, wie z. B. die SafeHandle.ReleaseHandle() -Methode muss in einem eingeschränkten Ausführungsbereich sein. Bedeutet dies eine Einschränkung, welchen Code innerhalb der Finalizer Aufrufdiagramm geschrieben werden kann.

Automatisch inkrementiert den Verweiszähler des gekapselten Handles eine SafeHandle und nach dem Abschluss zu verringern. Dadurch wird sichergestellt, dass das Handle nicht wiederverwendet oder unerwartet geschlossen.

Sie können den Besitz des zugrunde liegenden Handles angeben, beim Erstellen SafeHandle Objekte, indem der Wert auf die ownsHandle -Argument in der SafeHandle Klassenkonstruktor. Steuert, ob die SafeHandle -Objekt das Handle freigibt, nachdem das Objekt verworfen wird. Dies ist nützlich für Handles ungewöhnliche Lebensdauer Vorschriften oder für die Nutzung eines Handles, deren Lebensdauer von einer anderen Person gesteuert wird.

Von SafeHandle abgeleitete Klassen

SafeHandle ist eine abstrakte Wrapperklasse für Betriebssystemhandles. 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:

Hinweise für Vererber:

Erstellen Sie eine Klasse, die von abgeleiteten SafeHandle, müssen Sie wissen, wie erstellt und ein Betriebssystem-Handle freigegeben. Dieser Vorgang unterscheidet sich für die unterschiedlichen Handletypen, weil einige verwenden die CloseHandle -Funktion, während andere spezifische Funktionen, z. B. verwenden UnmapViewOfFile oder FindClose. Aus diesem Grund erstellen Sie eine abgeleitete Klasse von SafeHandle für jeden Typ von Betriebssystemhandle, die Sie in einem SafeHandle einschließen möchten.

Wichtig

Schreiben eigene Klassen von abgeleiteten SafeHandle ist eine erweiterte Funktion für die Programmierung. .NET Framework stellt einen Satz von vordefinierten von abgeleiteten Klassen SafeHandle in die Microsoft.Win32.SafeHandles Namespace. Diese Klassen bieten allgemeine Funktionen, die Unterstützung von Datei- und Betriebssystemhandles dienen.

Wenn Sie von SafeHandle erben, müssen die folgenden Member überschrieben werden: IsInvalid und ReleaseHandle.

Außerdem sollten Sie einen Standardkonstruktor, der den Basiskonstruktor mit einem Wert, der einen ungültigen Handlewert, ruft bereitstellen und ein Boolean Wert, der angibt, ob das systemeigene Handle gehört die SafeHandle und sollten daher beim freigegeben werden, die SafeHandle wurde verworfen.

Beispiele

Das folgende Codebeispiel erstellt ein benutzerdefiniertes sicheres Handle für ein Dateihandle des Betriebssystems Ableiten von SafeHandleZeroOrMinusOneIsInvalid. Es liest Bytes aus einer Datei und zeigt deren Hexadezimalwerte. Außerdem enthält es einen Fehler, der zu testende testen, die bewirkt, dass der Thread abgebrochen, aber der Wert des Handles wird freigegeben. Bei Verwendung einer IntPtr Darstellung von Handles, das Handle Threadabbruchs aufgrund des asynchronen Threadabbruchs.

Sie benötigen eine Textdatei im gleichen Ordner wie die kompilierte Anwendung. Vorausgesetzt, dass Sie die Anwendung "HexViewer" benennen, wird die Verwendung der Befehlszeile:

HexViewer <filename> -Fault

Geben Sie optional -Fault absichtlich versucht, das Handle Speicherverlust herbeizuführen, indem der Thread in einem bestimmten Fenster abgebrochen wird. Verwenden Sie das Tool Windows Perform.exe Handle Anzahlen während Fehler überwachen.

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);
                }
            }

        }
    }
}

Sicherheit

InheritanceDemand

for full trust for inheritors. This member cannot be inherited by partially trusted code.

SecurityCriticalAttribute

requires full trust for the immediate caller. This member cannot be used by partially trusted or transparent code.

Versionsinformationen

Universelle Windows-Plattform
Verfügbar seit 8
.NET Framework
Verfügbar seit 2.0
Portierbare Klassenbibliothek
Unterstützt in: portierbare .NET-Plattformen
Silverlight
Verfügbar seit 2.0
Windows Phone Silverlight
Verfügbar seit 7.0
Windows Phone
Verfügbar seit 8.1

Threadsicherheit

Alle öffentlichen statischen Member ( Shared in Visual Basic) dieses Typs sind threadsicher. Die Threadsicherheit für Instanzmember ist nicht garantiert.

Siehe auch

Microsoft.Win32.SafeHandles
CriticalHandle
CriticalFinalizerObject
System.Runtime.InteropServices-Namespace

Zurück zum Anfang