SafeHandle Klas

Definitie

Vertegenwoordigt een wrapper-klasse voor besturingsgrepen van het besturingssysteem. Deze klasse moet worden overgenomen.

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
Overname
SafeHandle
Overname
Afgeleid
Kenmerken
Implementeringen

Voorbeelden

In het volgende codevoorbeeld wordt een aangepaste veilige ingang gemaakt voor een bestandsingang van een besturingssysteem, afgeleid van SafeHandleZeroOrMinusOneIsInvalid. Het leest bytes uit een bestand en geeft de hexadecimale waarden weer. Het bevat ook een fouttestboom die ervoor zorgt dat de thread wordt afgebroken, maar de ingangswaarde wordt vrijgemaakt. Wanneer u een IntPtr greep gebruikt om ingangen weer te geven, wordt de greep af en toe gelekt vanwege de asynchrone thread die wordt afgebroken.

U hebt een tekstbestand nodig in dezelfde map als de gecompileerde toepassing. Ervan uitgaande dat u de toepassing hexViewer een naam geeft, is het gebruik van de opdrachtregel:

HexViewer <filename> -Fault

Geef desgewenst -Fault op dat de handgreep opzettelijk moet worden gelekt door de thread in een bepaald venster te afbreken. Gebruik het hulpprogramma Windows Perfmon.exe om het aantal ingangen bij het injecteren van fouten te controleren.

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

Opmerkingen

De SafeHandle klasse zorgt voor een kritische afronding van handleresources, waardoor handlers niet voortijdig kunnen worden vrijgemaakt door vuilnisopruiming en niet kunnen worden hergebruikt door het besturingssysteem om onbedoelde onbeheerde objecten te verwijzen.

Waarom SafeHandle?

Hoewel overschrijvingen naar de Object.Finalize methode het opruimen van onbeheerde resources toestaan wanneer een object door garbage collection wordt ingezameld, kunnen in sommige gevallen finaliseerbare objecten door garbage collection worden teruggewonnen tijdens het uitvoeren van een methode binnen een aanroep van een platform. Als een finalizer de handle vrijgeeft die door die platformaanroep wordt gebruikt, kan dit leiden tot beschadiging van de handle. De handle kan ook worden teruggevorderd terwijl uw methode wordt geblokkeerd tijdens een aanroep van een platform, zoals tijdens het lezen van een bestand.

Kritischer, omdat Windows agressief handles recyclet, kan een handle worden gerecycled en verwijzen naar een andere resource die gevoelige gegevens kan bevatten. Dit wordt een recycle-aanval genoemd en kan mogelijk gegevens beschadigen en een beveiligingsrisico vormen.

Wat SafeHandle doet

De SafeHandle klasse vereenvoudigt verschillende problemen met de levensduur van objecten en is geïntegreerd met het aanroepen van het platform, zodat besturingssysteembronnen niet worden gelekt. De SafeHandle klasse lost problemen met de levensduur van objecten op door grepen zonder onderbreking toe te wijzen en los te laten. Het bevat een kritieke finalizer die ervoor zorgt dat de handle wordt gesloten en gegarandeerd wordt uitgevoerd tijdens onverwachte AppDomain unload-operaties, zelfs in gevallen waarin wordt aangenomen dat de aanroep van het platform zich in een corrupte toestand bevindt.

Omdat SafeHandle afstamt van CriticalFinalizerObject, worden alle niet-kritieke finalizers aangeroepen vóór enige van de kritieke finalizers. De finalizers worden aangeroepen op objecten die niet langer leven tijdens dezelfde garbagecollectionpas. Een FileStream object kan bijvoorbeeld een normale finalizer draaien om bestaande gebufferde gegevens te legen zonder het risico dat de bestandsdescriptor wordt gelekt of gerecycled. Deze zeer zwakke volgorde tussen kritieke en niet-kritieke finalizers is niet bedoeld voor algemeen gebruik. Het bestaat voornamelijk om te helpen bij de migratie van bestaande bibliotheken door deze bibliotheken toe te staan te gebruiken SafeHandle zonder hun semantiek te wijzigen. Daarnaast moeten de kritieke finalizer en alles wat het aanroept, zoals de SafeHandle.ReleaseHandle() methode, zich in een beperkte uitvoeringsregio bevinden. Hiermee worden beperkingen opgelegd aan de code die binnen de oproepgrafiek van de finalizer kan worden geschreven.

Platformaanroepbewerkingen verhogen automatisch het referentieaantal van handles die zijn geïncapeleerd in een SafeHandle en verlagen deze na de voltooiing. Dit zorgt ervoor dat de greep niet hergebruikt of onverwacht gesloten wordt.

U kunt het eigendom van de onderliggende ingang opgeven bij het maken van SafeHandle objecten door een waarde op te geven aan het ownsHandle argument in de SafeHandle klasseconstructor. Hiermee bepaalt u of het SafeHandle object de ingang vrijgeeft nadat het object is verwijderd. Dit is handig voor handles met specifieke vereisten qua levensduur of voor het gebruik van een onderdeel waarvan de levensduur wordt beheerd door iemand anders.

Klassen die zijn afgeleid van SafeHandle

SafeHandle is een abstracte wrapper-klasse voor besturingsgrepen van het besturingssysteem. Het is moeilijk om van deze klasse af te leiden. Gebruik in plaats daarvan de afgeleide klassen in de Microsoft.Win32.SafeHandles naamruimte die veilige ingangen bieden voor het volgende:

Notities voor uitvoerders

Als u een klasse wilt maken die is afgeleid van SafeHandle, moet u weten hoe u een greep van een besturingssysteem maakt en vrij maakt. Dit proces verschilt voor verschillende handletypen omdat sommigen de functie CloseHandle gebruiken, terwijl andere meer specifieke functies gebruiken, zoals UnmapViewOfFile of FindClose. Daarom moet u een afgeleide klasse maken van SafeHandle voor elk type besturingssysteemgreep dat u in een veilige ingang wilt verpakken.

Wanneer u overdrat van SafeHandle, moet u de volgende leden overschrijven: IsInvalid en ReleaseHandle().

U moet ook een openbare parameterloze constructor opgeven die de basisconstructor aanroept met een waarde die een ongeldige ingangswaarde vertegenwoordigt en een Boolean waarde die aangeeft of de systeemeigen ingang eigendom is van de SafeHandle en dus moet worden vrijgemaakt wanneer deze SafeHandle is verwijderd.

Constructors

Name Description
SafeHandle(IntPtr, Boolean)

Initialiseert een nieuw exemplaar van de SafeHandle klasse met de opgegeven ongeldige ingangswaarde.

Velden

Name Description
handle

Hiermee geeft u de greep die moet worden verpakt.

Eigenschappen

Name Description
IsClosed

Hiermee wordt een waarde opgehaald die aangeeft of de ingang is gesloten.

IsInvalid

Wanneer deze wordt overschreven in een afgeleide klasse, wordt een waarde opgehaald die aangeeft of de ingangswaarde ongeldig is.

Methoden

Name Description
Close()

Markeert de ingang voor het vrijgeven en vrijmaken van resources.

DangerousAddRef(Boolean)

Hiermee wordt het referentiemeteritem SafeHandle voor exemplaren handmatig verhoogd.

DangerousGetHandle()

Retourneert de waarde van het handle veld.

DangerousRelease()

Hiermee wordt het verwijzingsteller voor een SafeHandle exemplaar handmatig gedegraded.

Dispose()

Alle resources die door de SafeHandle klasse worden gebruikt, worden vrijgegeven.

Dispose(Boolean)

Publiceert de niet-beheerde resources die door de SafeHandle klasse worden gebruikt en geeft aan of een normale verwijderingsbewerking moet worden uitgevoerd.

Equals(Object)

Bepaalt of het opgegeven object gelijk is aan het huidige object.

(Overgenomen van Object)
Finalize()

Alle resources die aan de ingang zijn gekoppeld, worden vrijgemaakt.

GetHashCode()

Fungeert als de standaardhashfunctie.

(Overgenomen van Object)
GetType()

Hiermee haalt u de Type huidige instantie op.

(Overgenomen van Object)
MemberwiseClone()

Hiermee maakt u een ondiepe kopie van de huidige Object.

(Overgenomen van Object)
ReleaseHandle()

Wanneer deze wordt overschreven in een afgeleide klasse, voert u de code uit die is vereist om de ingang vrij te maken.

SetHandle(IntPtr)

Hiermee stelt u de ingang in op de opgegeven bestaande ingang.

SetHandleAsInvalid()

Markeert een ingang als niet meer gebruikt.

ToString()

Retourneert een tekenreeks die het huidige object vertegenwoordigt.

(Overgenomen van Object)

Van toepassing op

Zie ook