SafeHandle Osztály

Definíció

Az operációsrendszer-leírók burkolóosztályát jelöli. Ezt az osztályt örökölni kell.

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
Öröklődés
SafeHandle
Öröklődés
Származtatott
Attribútumok
Megvalósítás

Példák

Az alábbi példakód egy egyéni biztonságos leírót hoz létre az operációs rendszer fájlleíróihoz, amely a következőből SafeHandleZeroOrMinusOneIsInvalidszármazik: . Bájtokat olvas be egy fájlból, és megjeleníti a hexadecimális értékeket. Emellett tartalmaz egy hibatesztelési hevedert is, amely megszakítja a szálat, de a fogópont értéke felszabadul. IntPtr Fogópontok ábrázolásakor a fogópont időnként kiszivárog az aszinkron szál megszakadása miatt.

Szüksége lesz egy szövegfájlra a lefordított alkalmazással megegyező mappában. Feltételezve, hogy a "HexViewer" nevet adja az alkalmazásnak, a parancssori használat a következő:

HexViewer <filename> -Fault

-Fault Megadhatja, hogy szándékosan megkísérelje kiszivárogtatni a fogópontot úgy, hogy megszakítja a szálat egy adott ablakban. A Windows Perfmon.exe eszközzel figyelheti a hibák kezelése közbeni számokat.

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

Megjegyzések

Az SafeHandle osztály kritikus fontosságú véglegesítést biztosít a kezelőerőforrásokhoz, megakadályozva, hogy a leírókat a szemétgyűjtés idő előtt visszanyerje, és hogy az operációs rendszer újrahasznosítsa a nem felügyelt objektumokra való hivatkozás érdekében.

Miért a SafeHandle?

Bár a Object.Finalize metódus felülbírálása lehetővé teszi a nem felügyelt erőforrások törlését az objektumok szemétgyűjtése során, bizonyos körülmények között a véglegesíthető objektumok visszaigényelhetők szemétgyűjtéssel, miközben egy metódust egy platformhívási híváson belül hajtanak végre. Ha a véglegesítő felszabadítja a platformhívásnak átadott leírót, az sérülés kezeléséhez vezethet. A fogantyút vissza is lehet venni, miközben a metódus blokkolva van egy platformhívás során, például fájl olvasása közben.

Fontosabb szempontból, mivel a Windows agresszíven újrahasznosítja a leírókat, egy leíró újrahasznosítható, és egy másik erőforrásra mutathat, amely bizalmas adatokat tartalmazhat. Ez az úgynevezett újrahasznosítási támadás, amely potenciálisan adattorzulást okozhat, és biztonsági fenyegetést jelenthet.

A SafeHandle feladata

Az SafeHandle osztály leegyszerűsíti az objektumok élettartamával kapcsolatos problémákat, és a platformhívással integrálva van, így az operációs rendszer erőforrásai nem szivárognak ki. Az SafeHandle osztály megszakítás nélkül megoldja az objektum élettartamához kapcsolódó problémákat a kezelők kiosztásával és felszabadításával. Tartalmaz egy kritikus véglegesítőt, amely biztosítja, hogy a leíró bezáruljon, és a váratlan AppDomain kioldások során is garantáltan fusson, még akkor is, ha a platformhívás feltételezetten hibás állapotban van.

Mivel SafeHandle örökli az CriticalFinalizerObject osztálytól, minden nem kritikus véglegesítő meghívásra kerül, mielőtt a kritikus véglegesítők lennének meghívva. A véglegesítők olyan objektumokra lesznek meghívva, amelyek már nem élnek ugyanazon szemétgyűjtési áthaladás során. Egy FileStream objektum például futtathat egy normál véglegesítőt, hogy anélkül ürítse ki a meglévő pufferelt adatokat, hogy a leíró kiszivárogna vagy újrafeldolgozódna. A kritikus és nem kritikus finalizálók közötti nagyon gyenge rendezés nem általános használatra szolgál. Elsősorban azért létezik, hogy segítse a meglévő kódtárak migrálását azáltal, hogy lehetővé teszi ezeknek a kódtáraknak a használatát SafeHandle a szemantikák módosítása nélkül. Emellett a kritikus véglegesítőnek és minden hívásnak, például a SafeHandle.ReleaseHandle() metódusnak korlátozott végrehajtási régióban kell lennie. Ez korlátozza, hogy milyen kód írható a véglegesítő hívási grafikonján.

A platformhívási műveletek automatikusan növelik az egybefoglalt SafeHandle leírók referenciaszámát, és a befejezéskor meg is decrementálják őket. Ez biztosítja, hogy a fogantyú ne legyen újrahasznosítva vagy váratlanul bezárva.

Az SafeHandle objektumok létrehozásakor megadhatja a mögöttes leíró tulajdonjogát úgy, hogy a ownsHandle osztálykonstruktor SafeHandle argumentumának megad egy értéket. Ez szabályozza, hogy az SafeHandle objektum felszabadítja-e a leírót az objektum megsemmisítése után. Ez akkor hasznos, ha azonosítók különleges élettartam-követelményekkel rendelkeznek, vagy olyan azonosítót használunk, amelynek élettartamát valaki más ellenőrzi.

A SafeHandle-ből származó osztályok

SafeHandle az operációs rendszer-leírók absztrakt burkolóosztálya. Ebből az osztályból nehéz származni. Ehelyett használja a származtatott osztályokat a Microsoft.Win32.SafeHandles névtérben, amelyek biztonságos leírókat biztosítanak a következőkhöz:

Megjegyzések az implementálókhoz

A származtatott SafeHandleosztály létrehozásához tudnia kell, hogyan hozhat létre és szabadíthat fel egy operációsrendszer-leírót. Ez a folyamat különbözik a különböző leírótípusoktól, mert egyesek a CloseHandle függvényt használják, míg mások konkrétabb függvényeket, például UnmapViewOfFile vagy FindClose függvényt. Ezért létre kell hoznia SafeHandle egy származtatott osztályt minden olyan operációsrendszer-leírótípushoz, amelyet biztonságos fogópontba szeretne csomagolni.

Ha örököl, SafeHandlefelül kell bírálnia a következő tagokat: IsInvalid és ReleaseHandle().

Olyan nyilvános paraméter nélküli konstruktort is meg kell adnia, amely az alapkonstruktort érvénytelen leíróértéket képviselő értékkel hívja meg, valamint egy Boolean olyan értéket, amely jelzi, hogy a natív leíró a SafeHandle tulajdonos-e, és ezért az elidegenítéskor SafeHandle fel kell szabadítani.

Konstruktorok

Name Description
SafeHandle(IntPtr, Boolean)

Inicializálja az osztály új példányát a SafeHandle megadott érvénytelen leíró értékkel.

Mezők

Name Description
handle

Megadja a becsomagolandó fogópontot.

Tulajdonságok

Name Description
IsClosed

Beolvas egy értéket, amely jelzi, hogy a leíró bezárva van-e.

IsInvalid

Ha egy származtatott osztályban felül van bírálva, egy értéket kap, amely jelzi, hogy a leíró értéke érvénytelen-e.

Metódusok

Name Description
Close()

Megjelöli az erőforrások felszabadítására és felszabadítására szolgáló leírót.

DangerousAddRef(Boolean)

Manuálisan növeli a hivatkozásszámlálót a SafeHandle példányokon.

DangerousGetHandle()

A mező értékét handle adja vissza.

DangerousRelease()

Manuálisan dekrementeli a hivatkozásszámlálót egy SafeHandle példányon.

Dispose()

Az osztály által SafeHandle használt összes erőforrást felszabadítja.

Dispose(Boolean)

Felszabadítja az osztály által SafeHandle használt nem felügyelt erőforrásokat, amelyek meghatározzák, hogy normál megsemmisítési műveletet hajtanak-e végre.

Equals(Object)

Meghatározza, hogy a megadott objektum egyenlő-e az aktuális objektummal.

(Öröklődés forrása Object)
Finalize()

Felszabadítja a leíróhoz társított összes erőforrást.

GetHashCode()

Ez az alapértelmezett kivonatoló függvény.

(Öröklődés forrása Object)
GetType()

Lekéri az Type aktuális példányt.

(Öröklődés forrása Object)
MemberwiseClone()

Az aktuális Objectpéldány sekély másolatát hozza létre.

(Öröklődés forrása Object)
ReleaseHandle()

Ha egy származtatott osztályban felül van bírálva, végrehajtja a leíró felszabadításához szükséges kódot.

SetHandle(IntPtr)

A fogópontot a megadott, már meglévő leíróra állítja.

SetHandleAsInvalid()

Már nem használt fogópontot jelöl meg.

ToString()

Az aktuális objektumot jelképező sztringet ad vissza.

(Öröklődés forrása Object)

A következőre érvényes:

Lásd még