Поделиться через


Реализуйте метод Dispose

Метод Dispose в основном реализуется для выпуска неуправляемых ресурсов. При работе с элементами экземпляра, которые являются реализацией IDisposable, обычно происходит каскадирование вызовов Dispose. Существуют и другие причины реализации Dispose, например для освобождения выделенной памяти, удаления элемента, добавленного в коллекцию, или сигнала о снятии блокировки, полученной.

сборщик мусора .NET не выделяет или не освобождает неуправляемую память. Шаблон удаления объекта, называемый шаблон удаления, накладывает порядок на время существования объекта. Шаблон удаления используется для объектов, реализующих интерфейс IDisposable. Этот шаблон распространен при взаимодействии с дескрипторами файлов и каналов, дескрипторами реестра, дескрипторами ожидания или указателями на блоки неуправляемой памяти, так как сборщик мусора не может освободить неуправляемые объекты.

Чтобы убедиться, что ресурсы всегда очищаются соответствующим образом, метод Dispose должен быть идемпотентным, так, чтобы его можно было вызывать несколько раз без возникновения исключений. Кроме того, последующие вызовы Dispose не должны ничего делать.

В примере кода, предоставленном для метода GC.KeepAlive, показано, как сборка мусора может привести к запуску финализатора, в то время как неуправляемая ссылка на объект или его члены все еще используется. Может потребоваться использовать GC.KeepAlive, чтобы сделать объект недопустимым для сборки мусора с начала текущей подпрограммы до точки вызова этого метода.

Совет

Что касается внедрения зависимостей, при регистрации служб в IServiceCollectionвремя существования службы управляется неявно в ваших интересах. IServiceProvider и соответствующий IHost координируют процесс очистки ресурсов. В частности, реализации IDisposable и IAsyncDisposable подлежат корректному удалению в конце указанного срока службы.

Дополнительные сведения см. в статье о внедрении зависимостей в .NET.

Каскадные вызовы удаления

Если класс владеет экземпляром другого типа, реализующего IDisposable, сам содержащий класс должен также реализовать IDisposable. Как правило, класс, который создает экземпляр реализации IDisposable и сохраняет его как член экземпляра (или свойство) также отвечает за очистку. Это помогает обеспечить, чтобы упомянутые удаляемые типы имели возможность детерминированно выполнить очистку с помощью метода Dispose. В следующем примере класс sealed (или NotInheritable в 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

Совет

  • Если класс имеет поле или свойство IDisposable, но не его собственный, то классу не нужно реализовать IDisposable. Как правило, класс, создающий и сохраняющий дочерний объект IDisposable, также становится владельцем, но в некоторых случаях владение может быть передано другому типу IDisposable.
  • Существуют случаи, когда может потребоваться выполнить null-проверку в финализаторе (который включает метод Dispose(false), вызываемый финализатором). Одна из основных причин заключается в том, что вы не уверены, был ли экземпляр полностью инициализирован (например, исключение могло быть сгенерировано в конструкторе).

Dispose() и Dispose(bool)

Интерфейс IDisposable требует реализации одного метода без параметров Dispose. Кроме того, любой незапечатанный класс должен иметь перегружаемый метод Dispose(bool).

Подписи методов:

  • public не виртуальные (NotOverridable в Visual Basic) (реализацияIDisposable.Dispose).
  • protected virtual (Overridable в Visual Basic Dispose(bool)).

Метод Dispose()

Так как метод public, невиртуальный (NotOverridable в Visual Basic), метод без параметров Dispose вызывается, когда он больше не требуется (потребителем типа), его цель — освободить неуправляемые ресурсы, выполнить общую очистку и указать, что финализатор, если он существует, может не выполняться. Освобождение фактической памяти, связанной с управляемым объектом, всегда является обязанностью сборщика мусора. Из-за этого она имеет стандартную реализацию:

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

Метод Dispose выполняет очистку всех объектов, поэтому сборщик мусора больше не требует вызова переопределения Object.Finalize объектов. Поэтому вызов метода SuppressFinalize предотвращает сборщику мусора запуск финализатора. Если тип не имеет финализатора, вызов GC.SuppressFinalize не оказывает эффекта. Фактическая очистка выполняется перегрузкой метода Dispose(bool).

Перегрузка метода Dispose(bool)

В перегруженной версии параметр disposing представляет собой Boolean, указывающий, происходит ли вызов метода из метода Dispose (его значение true) или из финализатора (его значение 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

Важное

Параметр disposing должен быть false при вызове из финализатора, а true при вызове из метода IDisposable.Dispose. Другими словами, это true при детерминированном вызове и false при недетерминированном вызове.

Текст метода состоит из трех блоков кода:

  • Блок условного возврата, если объект уже удален.

  • Условный блок, который освобождает управляемые ресурсы. Этот блок выполняется, если значение disposing равно true. Управляемые ресурсы, которые он освобождает, могут включать:

    • Управляемые объекты, реализующие IDisposable. Условный блок можно использовать для вызова реализации Dispose (каскадного удаления). Если вы использовали производный класс System.Runtime.InteropServices.SafeHandle для упаковки неуправляемого ресурса, необходимо вызвать реализацию SafeHandle.Dispose() здесь.
    • Управляемые объекты, использующие большие объемы памяти или использующие ограниченные ресурсы. Присвойте ссылки на крупные управляемые объекты null, чтобы сделать их более труднодоступными. Это освобождает их быстрее, чем если бы они были восстановлены недетерминированно.
  • Блок, который освобождает неуправляемые ресурсы. Этот блок выполняется независимо от значения параметра disposing.

Если вызов метода поступает из средства завершения, должен выполняться только код, который освобождает неуправляемые ресурсы. Реализатор несет ответственность за то, чтобы ложный путь не взаимодействовал с управляемыми объектами, которые могли быть очищены. Это важно, так как порядок удаления управляемых объектов сборщиком мусора во время завершения недетерминирован.

Реализация шаблона удаления

Все неконечные классы (или классы Visual Basic, не изменённые как NotInheritable) должны считаться потенциальными базовыми классами, так как они могут быть унаследованы. Если вы реализуете шаблон удаления для любого потенциального базового класса, необходимо добавить в класс следующие методы:

  • Реализация Dispose, которая вызывает метод Dispose(bool).
  • Метод Dispose(bool), выполняющий фактическую очистку.
  • Если ваш класс сталкивается с неуправляемыми ресурсами, предоставьте переопределение метода Object.Finalize или оберните неуправляемый ресурс в SafeHandle.

Важное

Финализатор (Object.Finalize переопределение) требуется только в том случае, если вы напрямую ссылаетесь на неуправляемые ресурсы. Это очень сложный сценарий, который обычно можно избежать:

  • Если класс ссылается только на управляемые объекты, класс по-прежнему может реализовать шаблон удаления. Нет необходимости реализовывать финализатор.
  • Если вам нужно иметь дело с неуправляемыми ресурсами, настоятельно рекомендуется упаковать неуправляемый IntPtr дескриптор в SafeHandle. SafeHandle предоставляет финализатор, поэтому вам не нужно писать его самостоятельно. Дополнительные сведения см. в абзаце раздела Безопасные дескрипторы.

Базовый класс с управляемыми ресурсами

Ниже приведен общий пример реализации шаблона удаления для базового класса, которому принадлежат только управляемые ресурсы.

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

Заметка

В предыдущем примере для иллюстрации шаблона используется фиктивный объектMemoryStream. Вместо этого можно использовать любую маркировку IDisposable.

Базовый класс с неуправляемыми ресурсами

Вот пример реализации шаблона освобождения для базового класса, который переопределяет Object.Finalize в целях очистки неуправляемых ресурсов, которыми он владеет. В этом примере также демонстрируется способ реализации Dispose(bool) в потокобезопасном режиме. Синхронизация может быть критической при работе с неуправляемыми ресурсами в многопотоковом приложении. Как упоминалось ранее, это расширенный сценарий.

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

Заметка

Совет

В C# вы реализуете финализацию, используя финализатор, а не переопределяя Object.Finalize. В Visual Basic создаётся финализатор с помощью Protected Overrides Sub Finalize().

Реализация шаблона удаления для производного класса

Класс, производный от класса, реализующего интерфейс IDisposable, не должен реализовывать IDisposable, так как реализация базового класса IDisposable.Dispose наследуется производными классами. Вместо этого, чтобы очистить производный класс, укажите следующее:

  • Метод protected override void Dispose(bool), который переопределяет метод базового класса и выполняет фактическую очистку производного класса. Этот метод также должен вызывать метод base.Dispose(bool) (MyBase.Dispose(bool) в Visual Basic), передавая его состояние удаления (параметрbool disposing) в качестве аргумента.
  • Класс, производный от SafeHandle, который упаковывает ваш неуправляемый ресурс (рекомендуется), или переопределение метода Object.Finalize. Класс SafeHandle предоставляет финализатор, который освобождает вас от необходимости писать собственный. Если вы предоставляете метод завершения, он должен вызвать перегрузку Dispose(bool) с аргументом false.

Ниже приведен пример общего шаблона реализации шаблона удаления для производного класса, использующего безопасный дескриптор:

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

Заметка

В предыдущем примере для иллюстрации шаблона используется объект SafeFileHandle; вместо этого можно использовать любой объект, производный от SafeHandle. Обратите внимание, что в примере не выполняется корректное создание экземпляра объекта SafeFileHandle.

Ниже приведен общий шаблон реализации шаблона удаления для производного класса, который переопределяет 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

Безопасные хендлы

Написание кода для средства завершения объекта — это сложная задача, которая может вызвать проблемы, если это не сделано правильно. Поэтому рекомендуется создавать объекты System.Runtime.InteropServices.SafeHandle вместо реализации финализатора.

System.Runtime.InteropServices.SafeHandle — это абстрактный управляемый тип, который обёртывает System.IntPtr, определяющий неуправляемый ресурс. В Windows он может идентифицировать хэндл, а в Unix — дескриптор файла. SafeHandle предоставляет всю необходимую логику для того, чтобы этот ресурс был освобожден один раз и только один раз, либо когда SafeHandle удаляется, либо когда все ссылки на SafeHandle удалены и экземпляр SafeHandle завершен.

System.Runtime.InteropServices.SafeHandle является абстрактным базовым классом. Производные классы предоставляют конкретные экземпляры для различных типов дескрипторов. Эти производные классы проверяют, какие значения для System.IntPtr считаются недопустимыми и как освободить дескриптор. Например, SafeFileHandle наследуется от SafeHandle для упаковки IntPtrs, которые определяют открытые файловые дескрипторы, и переопределяет свой метод SafeHandle.ReleaseHandle(), чтобы закрыть их (с помощью функции close в Unix или функции CloseHandle в Windows). Большинство API-интерфейсов в библиотеках .NET, создающих неуправляемый ресурс, оформляют его в виде объекта SafeHandle и возвращают этот SafeHandle вам по мере необходимости, вместо передачи необработанного указателя напрямую. В ситуациях, когда вы взаимодействуете с неуправляемым компонентом и получаете IntPtr для неуправляемого ресурса, можно создать собственный тип SafeHandle для его упаковки. В результате немногие типы, отличные отSafeHandle, нуждаются в реализации финализаторов. Большинство одноразовых реализаций шаблонов в конечном итоге упаковывают другие управляемые ресурсы, некоторые из которых могут быть SafeHandle объектами.

Реализация шаблона удаления с помощью пользовательского безопасного дескриптора

В следующем коде показано, как обрабатывать неуправляемые ресурсы путем реализации 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

Заметка

Поведение класса DisposableBaseWithSafeHandle эквивалентно поведению класса DisposableBaseWithFinalizer в предыдущем примере, однако приведенный здесь подход является более безопасным:

  • Нет необходимости реализовать финализатор, так как SafeHandle будет заботиться о завершении.
  • Для обеспечения безопасности потоков не требуется синхронизация. Несмотря на то что в реализации Dispose есть состояние гонки в DisposableBaseWithSafeHandle, SafeHandle гарантирует, что SafeHandle.ReleaseHandle будет вызван только один раз.

Встроенные безопасные дескрипторы SafeHandle в .NET

Следующие производные классы в пространстве имен Microsoft.Win32.SafeHandles предоставляют безопасные дескрипторы.

Класс Ресурсы, которые он содержит
SafeFileHandle
SafeMemoryMappedFileHandle
SafePipeHandle
Файлы, файлы, отображенные в памяти, и каналы
SafeMemoryMappedViewHandle Виды памяти
SafeNCryptKeyHandle
SafeNCryptProviderHandle
SafeNCryptSecretHandle
Конструкции криптографии
SafeRegistryHandle Ключи реестра
SafeWaitHandle Дескриптор ожидания

См. также