Implementera en avyttra-metod
Metoden Dispose implementeras främst för att frigöra ohanterade resurser. När du arbetar med instansmedlemmar som är IDisposable-implementationer är det vanligt att kedja Dispose-anrop. Det finns andra orsaker till att implementera Dispose, till exempel för att frigöra minne som allokerats, ta bort ett objekt som har lagts till i en samling eller signalera lanseringen av ett lås som har hämtats.
.NET-skräpinsamlaren allokerar eller frigör inte ohanterat minne. Mönstret för att ta bort ett objekt, som kallas mönstret för bortskaffande, ålägger ordningen på livslängden för ett objekt. Dispose-mönstret används för objekt som implementerar IDisposable-gränssnittet. Det här mönstret är vanligt när du interagerar med fil- och rörhandtag, registerhandtag, väntehandtag eller pekare till block med ohanterat minne, eftersom skräpinsamlaren inte kan frigöra ohanterade objekt.
För att säkerställa att resurserna alltid rensas på rätt sätt bör en Dispose-metod vara idempotent, så att den kan anropas flera gånger utan att utlösa ett undantag. Dessutom bör efterföljande anrop av Dispose inte göra någonting.
Kodexemplet som tillhandahålls för metoden GC.KeepAlive visar hur skräpinsamling kan orsaka att en finalizer körs medan en ohanterad referens till objektet eller dess medlemmar fortfarande är i användning. Det kan vara klokt att använda GC.KeepAlive för att göra objektet olämpligt för skräpinsamling från början av den aktuella rutinen till den punkt där den här metoden anropas.
Tips
När det gäller beroendeinjektion hanteras IServiceCollection implicit för din räkning när tjänster registreras i en . IServiceProvider och motsvarande IHost orkestrerar resursrengöring. Mer specifikt tas implementeringar av IDisposable och IAsyncDisposable bort korrekt i slutet av den angivna livslängden.
Mer information finns i Beroendeinmatning i .NET.
Kaskad avslutningsanrop
Om klassen äger en instans av en annan typ som implementerar IDisposablebör den innehållande klassen också implementera IDisposable. Vanligtvis är en klass som instansierar en IDisposable implementering och lagrar den som instansmedlem (eller egenskap) också ansvarig för rensningen. Detta säkerställer att de refererade disponibla typerna ges möjlighet att deterministiskt utföra rensning via metoden Dispose. I följande exempel är klassen sealed
(eller NotInheritable
i Visual Basic).
using System;
public sealed class Foo : IDisposable
{
private readonly IDisposable _bar;
public Foo()
{
_bar = new Bar();
}
public void Dispose() => _bar.Dispose();
}
Public NotInheritable Class Foo
Implements IDisposable
Private ReadOnly _bar As IDisposable
Public Sub New()
_bar = New Bar()
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
_bar.Dispose()
End Sub
End Class
Tips
- Om din klass har ett IDisposable fält eller egenskap men inte äger den, behöver klassen inte implementera IDisposable. Vanligtvis blir en klass som skapar och lagrar det IDisposable underordnade objektet också ägare, men i vissa fall kan ägarskapet överföras till en annan IDisposable typ.
- Det finns fall då du kanske vill utföra
null
-kontroll i en finaliserare (som inkluderar metodenDispose(false)
som anropas av en finaliserare). En av de främsta orsakerna är om du är osäker på om instansen har initierats helt (till exempel kan ett undantag utlösas i en konstruktor).
Dispose() och Dispose(bool)
Gränssnittet IDisposable kräver implementering av en enda parameterlös metod Dispose. Dessutom bör alla icke-förseglade klasser ha en Dispose(bool)
överlagringsmetod.
Metodsignaturer är:
-
public
icke-virtuell (NotOverridable
i Visual Basic) (IDisposable.Dispose implementering). -
protected virtual
(Overridable
i Visual Basic)Dispose(bool)
.
Metoden Dispose()
Eftersom den parameterlösa och icke-virtuella Dispose
-metoden (NotOverridable
i Visual Basic) anropas när den inte längre behövs av en typens konsument, är dess syfte att frigöra ohanterade resurser, utföra allmän städning och att ange att finalizern, om det finns en, inte behöver köras. Att frigöra det faktiska minnet som är associerat med ett hanterat objekt är alltid domänen för skräpinsamlare. På grund av detta har den en standardimplementering:
public void Dispose()
{
// Dispose of unmanaged resources.
Dispose(true);
// Suppress finalization.
GC.SuppressFinalize(this);
}
Public Sub Dispose() _
Implements IDisposable.Dispose
' Dispose of unmanaged resources.
Dispose(True)
' Suppress finalization.
GC.SuppressFinalize(Me)
End Sub
Metoden Dispose
utför all objektrensning, så skräpinsamlaren behöver inte längre anropa objektens Object.Finalize åsidosättning. Därför förhindrar anropet till SuppressFinalize-metoden sopinsamlaren från att köra finalizern. Om typen inte har någon finalizer har anropet till GC.SuppressFinalize ingen effekt. Den faktiska rensningen utförs av metodöverlagringen av Dispose(bool)
.
Överlagring av Dispose(bool)-metoden
I överlagringen är parametern disposing
en Boolean som anger om metodanropet kommer från en Dispose -metod (dess värde är true
) eller från en finalizer (dess värde är false
).
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// Dispose managed state (managed objects).
// ...
}
// Free unmanaged resources.
// ...
_disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
If disposed Then Exit Sub
If disposing Then
' Free managed resources.
' ...
End If
' Free unmanaged resources.
' ...
disposed = True
End Sub
Viktig
Parametern disposing
ska vara false
när den anropas från en finalizer, och true
när den anropas från metoden IDisposable.Dispose. Med andra ord är det true
när deterministiskt kallas och false
när nondeterministiskt anropas.
Metodens brödtext består av tre kodblock:
Ett block för villkorlig retur om objektet redan har tagits bort.
Ett villkorligt block som frigör hanterade resurser. Det här blocket körs om värdet för
disposing
ärtrue
. De hanterade resurser som frigörs kan vara:- Hanterade objekt som implementerar IDisposable. Det villkorliga blocket kan användas för att anropa deras Dispose implementering (kaskaderering). Om du har använt en härledd klass av System.Runtime.InteropServices.SafeHandle för att omsluta din icke-hanterade resurs, bör du här anropa implementeringen av SafeHandle.Dispose().
- Hanterade objekt som förbrukar stora mängder minne eller förbrukar knappa resurser. Tilldela stora referenser till hanterade objekt till
null
för att göra dem svårare att nå. Detta frigör dem snabbare än om de hade återtagits på ett icke-deterministiskt sätt.
Ett block som frigör ej hanterade resurser. Det här blocket körs oavsett värdet för parametern
disposing
.
Om metodanropet kommer från en finalizer ska endast den kod som frigör ohanterade resurser köras. Implementeraren ansvarar för att se till att den falska sökvägen inte interagerar med hanterade objekt som kan ha tagits bort. Detta är viktigt eftersom ordningen i vilken skräpinsamlaren kasserar hanterade objekt under slutförande är icke-terministisk.
Implementera mönstret för bortskaffning
Alla icke-förseglade klasser (eller Visual Basic-klasser som inte har ändrats som NotInheritable
) bör betraktas som en potentiell basklass eftersom de kan ärvas. Om du implementerar mönstret för bortskaffning för eventuella basklasser måste du lägga till följande metoder i klassen:
- En Dispose implementering som anropar metoden
Dispose(bool)
. - En
Dispose(bool)
metod som utför den faktiska rensningen. - Om klassen hanterar ohanterade resurser, tillhandahåller du antingen en åsidosättning till metoden Object.Finalize eller omsluter du den ohanterade resursen i en SafeHandle.
Viktig
En avslutare (en Object.Finalize-överskrivning) krävs bara om du direkt refererar till ohanterade resurser. Det här är ett mycket avancerat scenario som vanligtvis kan undvikas:
- Om klassen endast refererar till hanterade objektär det fortfarande möjligt för klassen att implementera mönstret för bortskaffning. Du behöver inte implementera en finalizer.
- Om du behöver hantera ohanterade resurserrekommenderar vi starkt att du omsluter den ohanterade IntPtr-handtaget i en SafeHandle. SafeHandle tillhandahåller en finalator så att du inte behöver skriva en själv. För mer information, se stycket Säkra handtag.
Basklass med hanterade resurser
Här är ett allmänt exempel på hur du implementerar mönstret för bortskaffning för en basklass som bara äger hanterade resurser.
using System;
using System.IO;
public class DisposableBase : IDisposable
{
// Detect redundant Dispose() calls.
private bool _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
// Dispose managed state.
_managedResource?.Dispose();
_managedResource = null;
}
}
}
}
Imports System.IO
Public Class DisposableBase
Implements IDisposable
' Detect redundant Dispose() calls.
Private _isDisposed As Boolean
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
' Dispose managed state.
_managedResource?.Dispose()
_managedResource = Nothing
End If
End If
End Sub
End Class
Anteckning
I föregående exempel används ett dummy-MemoryStream objekt för att illustrera mönstret. Vilken som helst IDisposable kunde användas istället.
Basklass med ohanterade resurser
Här är ett exempel för att implementera mönstret för rensning för en basklass som åsidosätter Object.Finalize för att rensa ohanterade resurser som den äger. Exemplet visar också ett sätt att implementera Dispose(bool)
på ett trådsäkert sätt. Synkronisering kan vara kritisk när du hanterar ohanterade resurser i ett program med flera trådar. Som tidigare nämnts är detta ett avancerat scenario.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
public class DisposableBaseWithFinalizer : IDisposable
{
// Detect redundant Dispose() calls in a thread-safe manner.
// _isDisposed == 0 means Dispose(bool) has not been called yet.
// _isDisposed == 1 means Dispose(bool) has been already called.
private int _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// A pointer to 10 bytes allocated on the unmanaged heap.
private IntPtr _unmanagedResource = Marshal.AllocHGlobal(10);
~DisposableBaseWithFinalizer() => Dispose(false);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
// In case _isDisposed is 0, atomically set it to 1.
// Enter the branch only if the original value is 0.
if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
{
if (disposing)
{
_managedResource?.Dispose();
_managedResource = null;
}
Marshal.FreeHGlobal(_unmanagedResource);
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Threading
Public Class DisposableBaseWithFinalizer
Implements IDisposable
' Detect redundant Dispose() calls in a thread-safe manner.
' _isDisposed == 0 means Dispose(bool) has not been called yet.
' _isDisposed == 1 means Dispose(bool) has been already called.
Private _isDisposed As Integer
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' A pointer to 10 bytes allocated on the unmanaged heap.
Private _unmanagedResource As IntPtr = Marshal.AllocHGlobal(10)
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
' In case _isDisposed is 0, atomically set it to 1.
' Enter the branch only if the original value is 0.
If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
If disposing Then
_managedResource?.Dispose()
_managedResource = Nothing
End If
Marshal.FreeHGlobal(_unmanagedResource)
End If
End Sub
End Class
Anteckning
- I föregående exempel används AllocHGlobal för att allokera 10 byte på den ohanterade högen i konstruktorn och för att frigöra bufferten i
Dispose(bool)
genom att anropa FreeHGlobal. Det här är en exempelallokering för illustrativa syften. - Återigen rekommenderar vi att du undviker att implementera en finalizer. Se Implementera mönstret för frisläppande med hjälp av ett anpassat säkert handtag för en motsvarighet till föregående exempel som delegerar icke-deterministisk avslutning och synkronisering till SafeHandle.
Tips
I C# implementerar du en slutförande genom att tillhandahålla en finaliserare, inte genom att åsidosätta Object.Finalize. I Visual Basic skapar du en finalator med Protected Overrides Sub Finalize()
.
Implementera dispose-mönstret för en härledd klass
En klass som härleds från en klass som implementerar IDisposable-gränssnittet bör inte implementera IDisposableeftersom basklassimplementeringen av IDisposable.Dispose ärvs av dess härledda klasser. För att rensa en härledd klass anger du i stället följande:
- En
protected override void Dispose(bool)
metod som åsidosätter basklassmetoden och utför den faktiska rensningen av den härledda klassen. Den här metoden måste också anropabase.Dispose(bool)
-metoden (MyBase.Dispose(bool)
i Visual Basic) och skicka den avyttringsstatusen (bool disposing
parameter) som ett argument. - Antingen en klass som härleds från SafeHandle som omsluter din ohanterade resurs (rekommenderas), eller ett åsidosättande av metoden Object.Finalize. Klassen SafeHandle innehåller en finalator som gör att du inte behöver koda en. Om du anger en finalizer måste den anropa med
Dispose(bool)
-överlagringen ochfalse
-argumentet.
Här är ett exempel på det allmänna mönstret för att implementera dispose-mönstret för en härledd klass som använder ett säkert handtag.
using System.IO;
public class DisposableDerived : DisposableBase
{
// To detect redundant calls
private bool _isDisposed;
// Instantiate a disposable object owned by this class.
private Stream? _managedResource = new MemoryStream();
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
_managedResource?.Dispose();
_managedResource = null;
}
}
// Call base class implementation.
base.Dispose(disposing);
}
}
Imports System.IO
Public Class DisposableDerived
Inherits DisposableBase
' To detect redundant calls
Private _isDisposed As Boolean
' Instantiate a disposable object owned by this class.
Private _managedResource As Stream = New MemoryStream()
' Protected implementation of Dispose pattern.
Protected Overrides Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
_managedResource?.Dispose()
_managedResource = Nothing
End If
End If
' Call base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class
Anteckning
I föregående exempel används ett SafeFileHandle-objekt för att illustrera mönstret; vilket som helst objekt som härleds från SafeHandle kan användas istället. Observera att exemplet inte instansierar sitt SafeFileHandle objekt korrekt.
Här är det allmänna mönstret för att implementera dispose-mönstret för en härledd klass som överskrider Object.Finalize:
using System.Threading;
public class DisposableDerivedWithFinalizer : DisposableBaseWithFinalizer
{
// Detect redundant Dispose() calls in a thread-safe manner.
// _isDisposed == 0 means Dispose(bool) has not been called yet.
// _isDisposed == 1 means Dispose(bool) has been already called.
private int _isDisposed;
~DisposableDerivedWithFinalizer() => Dispose(false);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
// In case _isDisposed is 0, atomically set it to 1.
// Enter the branch only if the original value is 0.
if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
}
// Call the base class implementation.
base.Dispose(disposing);
}
}
Imports System.Threading
Public Class DisposableDerivedWithFinalizer
Inherits DisposableBaseWithFinalizer
' Detect redundant Dispose() calls in a thread-safe manner.
' _isDisposed == 0 means Dispose(bool) has not been called yet.
' _isDisposed == 1 means Dispose(bool) has been already called.
Private _isDisposed As Integer
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
' Protected implementation of Dispose pattern.
Protected Overrides Sub Dispose(disposing As Boolean)
' In case _isDisposed is 0, atomically set it to 1.
' Enter the branch only if the original value is 0.
If Interlocked.CompareExchange(_isDisposed, 1, 0) = 0 Then
If disposing Then
' TODO: dispose managed state (managed objects).
End If
' TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
' TODO: set large fields to null.
End If
' Call the base class implementation.
MyBase.Dispose(disposing)
End Sub
End Class
Säkra handtag
Att skriva kod för ett objekts finaliserare är en komplex uppgift som kan orsaka problem om det inte görs korrekt. Därför rekommenderar vi att du skapar System.Runtime.InteropServices.SafeHandle objekt i stället för att implementera en finalizer.
En System.Runtime.InteropServices.SafeHandle är en abstrakt hanterad typ som omsluter en System.IntPtr som identifierar en ohanterad resurs. I Windows kan det identifiera ett handtag och på Unix en filbeskrivning.
SafeHandle
tillhandahåller all logik som krävs för att säkerställa att den här resursen släpps en gång och bara en gång, antingen när SafeHandle
tas bort eller när alla referenser till SafeHandle
har tagits bort och SafeHandle
-instansen har slutförts.
System.Runtime.InteropServices.SafeHandle är en abstrakt basklass. Härledda klasser tillhandahåller specifika instanser för olika typer av handtag. Dessa härledda klasser verifierar vilka värden för System.IntPtr som anses vara ogiltiga och hur du faktiskt frigör handtaget. Till exempel härleds SafeFileHandle från SafeHandle
för att omsluta IntPtrs
som identifierar öppna filreferenser och deskriptorer och åsidosätter dess SafeHandle.ReleaseHandle()-metod för att stänga den (via funktionen close
på Unix eller CloseHandle
i Windows). De flesta API:er i .NET-bibliotek som skapar en ohanterad resurs omsluter den i en SafeHandle
och returnerar den SafeHandle
till dig efter behov i stället för att lämna tillbaka den råa pekaren. I situationer där du interagerar med en ohanterad komponent och får en IntPtr
för en ohanterad resurs kan du skapa en egen typ av SafeHandle
för att inkapsla den. Som ett resultat behöver få icke-SafeHandle
-typer implementera finalizers. De flesta implementeringar av disponibla mönster omsluter bara andra hanterade resurser, varav vissa kan vara SafeHandle
objekt.
Implementera mönstret för bortskaffning med hjälp av en anpassad säker handtag
Följande kod visar hur du hanterar ohanterade resurser genom att implementera en SafeHandle.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
// Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
class LocalAllocHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private LocalAllocHandle() : base(ownsHandle: true) { }
// No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
// Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
public static LocalAllocHandle Allocate(int numberOfBytes)
{
IntPtr nativeHandle = Marshal.AllocHGlobal(numberOfBytes);
LocalAllocHandle safeHandle = new LocalAllocHandle();
safeHandle.SetHandle(nativeHandle);
return safeHandle;
}
}
public class DisposableBaseWithSafeHandle : IDisposable
{
// Detect redundant Dispose() calls.
private bool _isDisposed;
// Managed disposable objects owned by this class
private LocalAllocHandle? _safeHandle = LocalAllocHandle.Allocate(10);
private Stream? _otherUnmanagedResource = new MemoryStream();
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
// Dispose managed state.
_otherUnmanagedResource?.Dispose();
_safeHandle?.Dispose();
_otherUnmanagedResource = null;
_safeHandle = null;
}
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles
' Wraps the IntPtr allocated by Marshal.AllocHGlobal() into a SafeHandle.
Public Class LocalAllocHandle
Inherits SafeHandleZeroOrMinusOneIsInvalid
Private Sub New()
MyBase.New(True)
End Sub
' No need to implement a finalizer - SafeHandle's finalizer will call ReleaseHandle for you.
Protected Overrides Function ReleaseHandle() As Boolean
Marshal.FreeHGlobal(handle)
Return True
End Function
' Allocate bytes with Marshal.AllocHGlobal() and wrap the result into a SafeHandle.
Public Shared Function Allocate(numberOfBytes As Integer) As LocalAllocHandle
Dim nativeHandle As IntPtr = Marshal.AllocHGlobal(numberOfBytes)
Dim safeHandle As New LocalAllocHandle()
safeHandle.SetHandle(nativeHandle)
Return safeHandle
End Function
End Class
Public Class DisposableBaseWithSafeHandle
Implements IDisposable
' Detect redundant Dispose() calls.
Private _isDisposed As Boolean
' Managed disposable objects owned by this class
Private _safeHandle As LocalAllocHandle = LocalAllocHandle.Allocate(10)
Private _otherUnmanagedResource As Stream = New MemoryStream()
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If Not _isDisposed Then
_isDisposed = True
If disposing Then
' Dispose managed state.
_otherUnmanagedResource?.Dispose()
_safeHandle?.Dispose()
_otherUnmanagedResource = Nothing
_safeHandle = Nothing
End If
End If
End Sub
End Class
Anteckning
Beteendet för klassen DisposableBaseWithSafeHandle
motsvarar beteendet för klassen DisposableBaseWithFinalizer
i ett tidigare exempel, men den metod som visas här är säkrare:
- Det finns inget behov av att implementera en finalator, eftersom SafeHandle tar hand om slutförande.
- Det finns inget behov av synkronisering för att garantera trådsäkerheten. Även om det finns ett konkurrenstillstånd i
Dispose
genomförandet avDisposableBaseWithSafeHandle
garanterar SafeHandle att SafeHandle.ReleaseHandle bara kommer att anropas en gång.
Inbyggda säkra handtag i .NET
Följande härledda klasser i Microsoft.Win32.SafeHandles-namnområdet ger säkra referenser.
Klass | Resurser som den innehåller |
---|---|
SafeFileHandle SafeMemoryMappedFileHandle SafePipeHandle |
Filer, minnesmappade filer och rör |
SafeMemoryMappedViewHandle | Minnesvyer |
SafeNCryptKeyHandle SafeNCryptProviderHandle SafeNCryptSecretHandle |
Kryptografikonstruktioner |
SafeRegistryHandle | Registernycklar |
SafeWaitHandle | Väntehandtag |