SafeHandle Osztály
Definíció
Fontos
Egyes információk olyan, kiadás előtti termékekre vonatkoznak, amelyek a kiadásig még jelentősen módosulhatnak. A Microsoft nem vállal kifejezett vagy törvényi garanciát az itt megjelenő információért.
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:
- Fájlok (az SafeFileHandle osztály).
- Memórialeképezett fájlok (az SafeMemoryMappedFileHandle osztály).
- Csövek (az SafePipeHandle osztály).
- Memórianézetek (az SafeMemoryMappedViewHandle osztály).
- Titkosítási szerkezetek (a SafeNCryptHandle, SafeNCryptKeyHandle, SafeNCryptProviderHandleés SafeNCryptSecretHandle osztályok).
- Folyamatok (az SafeProcessHandle osztály).
- Beállításkulcsok (az SafeRegistryHandle osztály).
- Várakozási fogópontok (az SafeWaitHandle osztály).
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) |