SafeHandle Třída

Definice

Představuje obálkovou třídu pro obslužné rutiny operačních systémů.Represents a wrapper class for operating system handles. Tato třída musí být zděděná.This class must be inherited.

public ref class SafeHandle abstract : System::Runtime::ConstrainedExecution::CriticalFinalizerObject, IDisposable
[System.Security.SecurityCritical]
public abstract class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, IDisposable
type SafeHandle = class
    inherit CriticalFinalizerObject
    interface IDisposable
Public MustInherit Class SafeHandle
Inherits CriticalFinalizerObject
Implements IDisposable
Dědičnost
Odvozené
Atributy
Implementuje

Příklady

Následující příklad kódu vytvoří vlastní bezpečný popisovač pro popisovač souborů operačního systému, který je odvozen z SafeHandleZeroOrMinusOneIsInvalid.The following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. Načte bajty ze souboru a zobrazí jejich hexadecimální hodnoty.It reads bytes from a file and displays their hexadecimal values. Obsahuje také kabel selhání testování, který způsobuje přerušení vlákna, ale hodnota popisovače je uvolněna.It also contains a fault testing harness that causes the thread to abort, but the handle value is freed. Při použití prvku IntPtr k reprezentace popisovačů je občas nevrácen popisovač z důvodu přerušení asynchronního vlákna.When using an IntPtr to represent handles, the handle is occasionally leaked due to the asynchronous thread abort.

Budete potřebovat textový soubor ve stejné složce jako kompilovaná aplikace.You will need a text file in the same folder as the compiled application. Za předpokladu, že pojmenování aplikace "HexViewer", je použití příkazového řádku následující:Assuming that you name the application "HexViewer", the command line usage is:

HexViewer <filename> -Fault

Volitelně můžete -Fault zadat úmyslně se pokusit zpracovat popisovač zrušením vlákna v určitém okně.Optionally specify -Fault to intentionally attempt to leak the handle by aborting the thread in a certain window. Pomocí nástroje Windows Perfmon. exe monitorujte počty popisovačů při vkládání chyb.Use the Windows Perfmon.exe tool to monitor handle counts while injecting faults.

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

        }
    }
}

Poznámky

SafeHandle Třída poskytuje kritickou finalizaci prostředků manipulačního programu a znemožňuje jejich opětovné vyřazení uvolňováním paměti a recyklaci systémem Windows k odkazování na nezamýšlené nespravované objekty.The SafeHandle class provides critical finalization of handle resources, preventing handles from being reclaimed prematurely by garbage collection and from being recycled by Windows to reference unintended unmanaged objects.

Toto téma obsahuje následující oddíly:This topic includes the following sections:

Proč SafeHandle? Why SafeHandle?
Co dělá SafeHandle What SafeHandle does
Třídy odvozené z SafeHandleClasses derived from SafeHandle

Proč SafeHandle?Why SafeHandle?

Před .NET Framework verze 2,0 se všechny obslužné rutiny operačního systému daly zapouzdřit jenom do IntPtr spravovaného objektu obálky.Before the .NET Framework version 2.0, all operating system handles could only be encapsulated in the IntPtr managed wrapper object. Přestože se jednalo o pohodlný způsob, jak vzájemně spolupracovat s nativním kódem, mohou být popisovače Nevráceny asynchronními výjimkami, jako je například neočekávaně přerušeno vlákno nebo přetečení zásobníku.While this was a convenient way to interoperate with native code, handles could be leaked by asynchronous exceptions, such as a thread aborting unexpectedly or a stack overflow. Tyto asynchronní výjimky jsou překážkou pro čištění prostředků operačního systému a můžou se nacházet téměř kdekoli v aplikaci.These asynchronous exceptions are an obstacle to cleaning up operating system resources, and they can occur almost anywhere in your app.

I když přepsání Object.Finalize metody umožní vyčištění nespravovaných prostředků při uvolňování paměti objektu, v některých případech je možné finalizační objekty uvolnit uvolňováním paměti při provádění metody v volání platformy. volání.Although overrides to the Object.Finalize method allow cleanup of unmanaged resources when an object is being garbage collected, in some circumstances, finalizable objects can be reclaimed by garbage collection while executing a method within a platform invoke call. Pokud finalizační metoda uvolní popisovač předaný této platformě volání vyvolání, může to vést k poškození.If a finalizer frees the handle passed to that platform invoke call, it could lead to handle corruption. Obslužná rutina může být také uvolněna, zatímco vaše metoda je zablokovaná během volání vyvolání platformy, například při čtení souboru.The handle could also be reclaimed while your method is blocked during a platform invoke call, such as while reading a file.

V důsledku toho, vzhledem k tomu, že Windows výrazně recykluje obslužné rutiny, může být popisovač recyklován a odkazovat na jiný prostředek, který může obsahovat citlivá data.More critically, because Windows aggressively recycles handles, a handle could be recycled and point to another resource that might contain sensitive data. To se označuje jako útok z recyklace a může potenciálně poškodit data a představovat bezpečnostní riziko.This is known as a recycle attack and can potentially corrupt data and be a security threat.

Co dělá SafeHandleWhat SafeHandle does

SafeHandle Třída zjednodušuje některé z těchto problémů životního cyklu objektu a je integrována s voláním platformy, takže prostředky operačního systému nebudou Nevráceny.The SafeHandle class simplifies several of these object lifetime issues, and is integrated with platform invoke so that operating system resources are not leaked. SafeHandle Třída řeší problémy životnosti objektů přiřazením a uvolněním popisovačů bez přerušení.The SafeHandle class resolves object lifetime issues by assigning and releasing handles without interruption. Obsahuje kritický finalizační metodu, která zajišťuje, že je popisovač uzavřený a je zaručeno jeho spuštění během AppDomain neočekávaných uvolnění, dokonce i v případech, kdy se volání metody Invoke v případě, že je volání platformy poškozené.It contains a critical finalizer that ensures that the handle is closed and is guaranteed to run during unexpected AppDomain unloads, even in cases when the platform invoke call is assumed to be in a corrupted state.

Vzhledem SafeHandle k tomu CriticalFinalizerObject, že dědí z, všechny nekritické finalizační metody jsou volány před některým z kritických finalizační metody.Because SafeHandle inherits from CriticalFinalizerObject, all the noncritical finalizers are called before any of the critical finalizers. Finalizační metody jsou volány pro objekty, které již nejsou živé během stejného průchodu uvolňování paměti.The finalizers are called on objects that are no longer live during the same garbage collection pass. Například FileStream objekt může spustit normální finalizační metodu pro vyprázdnění existujících dat v bufferu bez rizika, že dojde k úniku nebo recyklaci popisovače.For example, a FileStream object can run a normal finalizer to flush out existing buffered data without the risk of the handle being leaked or recycled. Toto velmi slabé řazení mezi kritickými a nekritickými finalizační metodami není určené pro obecné použití.This very weak ordering between critical and noncritical finalizers is not intended for general use. Tato knihovna je primárně určena pro pomoc při migraci stávajících knihoven tím, že umožňuje použití SafeHandle těchto knihoven bez změny sémantiky.It exists primarily to assist in the migration of existing libraries by allowing those libraries to use SafeHandle without altering their semantics. Kromě toho kritické finalizační metoda a cokoli, co volá, například SafeHandle.ReleaseHandle() metoda, musí být v oblasti omezeného provádění.Additionally, the critical finalizer and anything it calls, such as the SafeHandle.ReleaseHandle() method, must be in a constrained execution region. To představuje omezení toho, jaký kód lze zapsat v grafu volání finalizační metody.This imposes constraints on what code can be written within the finalizer's call graph.

Operace vyvolání platformy automaticky zvýší počet odkazů popisovačů SafeHandle , které jsou zapouzdřeny a sníženy po dokončení.Platform invoke operations automatically increment the reference count of handles encapsulated by a SafeHandle and decrement them upon completion. Tím je zajištěno, že popisovač nebude recyklován ani ukončen neočekávaně.This ensures that the handle will not be recycled or closed unexpectedly.

Můžete určit vlastnictví podkladového popisovače při vytváření SafeHandle objektů zadáním hodnoty ownsHandle do argumentu v SafeHandle konstruktoru třídy.You can specify ownership of the underlying handle when constructing SafeHandle objects by supplying a value to the ownsHandle argument in the SafeHandle class constructor. Tím se řídí, SafeHandle zda objekt uvolní popisovač po uvolnění objektu.This controls whether the SafeHandle object will release the handle after the object has been disposed. To je užitečné pro obslužné rutiny se zvláštními nároky na životnost nebo pro zpracování popisovače, jehož doba života je řízena někým jiným.This is useful for handles with peculiar lifetime requirements or for consuming a handle whose lifetime is controlled by someone else.

Třídy odvozené z SafeHandleClasses derived from SafeHandle

SafeHandleje abstraktní Obálková třída pro obslužné rutiny operačních systémů.SafeHandle is an abstract wrapper class for operating system handles. Odvození z této třídy je obtížné.Deriving from this class is difficult. Místo toho použijte odvozené třídy v Microsoft.Win32.SafeHandles oboru názvů, které poskytují bezpečné popisovače pro následující:Instead, use the derived classes in the Microsoft.Win32.SafeHandles namespace that provide safe handles for the following:

Poznámky pro dědice

Chcete-li vytvořit třídu odvozenou z SafeHandlenástroje, musíte znát, jak vytvořit a uvolnit popisovač operačního systému.To create a class derived from SafeHandle, you must know how to create and free an operating system handle. Tento proces se liší od různých typů popisovačů, protože některé využívají funkci CloseHandle , zatímco jiné využívají konkrétnější funkce, jako je UnmapViewOfFile nebo FindClose.This process is different for different handle types because some use the CloseHandle function, while others use more specific functions such as UnmapViewOfFile or FindClose. Z SafeHandle tohoto důvodu je nutné vytvořit odvozenou třídu pro každý typ popisovače operačního systému, který chcete zabalit do bezpečného popisovače.For this reason, you must create a derived class of SafeHandle for each operating system handle type that you want to wrap in a safe handle.

Při dědění z SafeHandleje nutné přepsat následující členy: IsInvalid a ReleaseHandle().When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle().

Měli byste také poskytnout konstruktor bez parametrů, který volá základní konstruktor s hodnotou, která představuje neplatnou hodnotu popisovače, a Boolean hodnotu, která označuje, zda nativní popisovač je vlastněn SafeHandle a následně by měl být uvolněn. SafeHandle kdy byla zrušena.You should also provide a parameterless constructor that calls the base constructor with a value that represent an invalid handle value, and a Boolean value indicating whether the native handle is owned by the SafeHandle and consequently should be freed when that SafeHandle has been disposed.

Konstruktory

SafeHandle()
SafeHandle(IntPtr, Boolean)

Inicializuje novou instanci SafeHandle třídy se zadanou neplatnou hodnotou popisovače.Initializes a new instance of the SafeHandle class with the specified invalid handle value.

Pole

handle

Určuje popisovač, který má být zabalen.Specifies the handle to be wrapped.

Vlastnosti

IsClosed

Načte hodnotu, která označuje, jestli je popisovač uzavřený.Gets a value indicating whether the handle is closed.

IsInvalid

Při přepsání v odvozené třídě získá hodnotu, která označuje, zda je hodnota popisovače neplatná.When overridden in a derived class, gets a value indicating whether the handle value is invalid.

Metody

Close()

Označí popisovač pro uvolnění a uvolnění prostředků.Marks the handle for releasing and freeing resources.

DangerousAddRef(Boolean)

Ručně zvýší referenční počítadlo SafeHandle instancí.Manually increments the reference counter on SafeHandle instances.

DangerousGetHandle()

Vrátí hodnotu handle pole.Returns the value of the handle field.

DangerousRelease()

Ručně sníží referenční čítač SafeHandle instance.Manually decrements the reference counter on a SafeHandle instance.

Dispose()

Uvolní všechny prostředky používané SafeHandle třídou.Releases all resources used by the SafeHandle class.

Dispose(Boolean)

Uvolní nespravované prostředky používané SafeHandle třídou určující, zda má být provedena operace normálního uvolnění.Releases the unmanaged resources used by the SafeHandle class specifying whether to perform a normal dispose operation.

Equals(Object)

Určuje, zda se zadaný objekt rovná aktuálnímu objektu.Determines whether the specified object is equal to the current object.

(Zděděno od Object)
Finalize()

Uvolní všechny prostředky přidružené k tomuto popisovači.Frees all resources associated with the handle.

GetHashCode()

Slouží jako výchozí funkce hash.Serves as the default hash function.

(Zděděno od Object)
GetType()

Type Získá aktuální instanci.Gets the Type of the current instance.

(Zděděno od Object)
MemberwiseClone()

Vytvoří kopii aktuálního Objectseznamu.Creates a shallow copy of the current Object.

(Zděděno od Object)
ReleaseHandle()

Při přepsání v odvozené třídě spustí kód potřebný k uvolnění popisovače.When overridden in a derived class, executes the code required to free the handle.

SetHandle(IntPtr)

Nastaví popisovač na zadaný dříve existující popisovač.Sets the handle to the specified pre-existing handle.

SetHandleAsInvalid()

Označí popisovač jako již nepoužitý.Marks a handle as no longer used.

ToString()

Vrací řetězec, který představuje aktuální objekt.Returns a string that represents the current object.

(Zděděno od Object)

Zabezpečení

InheritanceDemand
pro úplný vztah důvěryhodnosti pro dědicefor full trust for inheritors. Tento člen nemůže být zděděn částečně důvěryhodným kódem.This member cannot be inherited by partially trusted code.

SecurityCriticalAttribute
vyžaduje úplný vztah důvěryhodnosti pro bezprostředně volajícího.requires full trust for the immediate caller. Tohoto člena nelze použít v kódu s částečnou důvěryhodností ani v transparentním kódu.This member cannot be used by partially trusted or transparent code.

Platí pro

Viz také