When using system handles such as file handles, process handles, or any other handles provided by the kernel, you should take care to release them correctly when you don't need them anymore. The native APIs often provides a method to get a handle and a method to release it, plus sometimes a few methods to work with the resource. For instance, you can get a file handle using the method
CreateFile, release it using
CloseHandle, and for instance, write to the file using
WriteFile. If you don't want to leak the resource, you need to ensure you call the method
CloseHandle as soon as you have finished using the file. You must call this method once and only once. What if there is an exception in the code before you could release the handle. Maybe the exception is not directly from your code, but for instance, someone could abort the thread you are running on, or you could run out of memory.
SafeHandle was introduced in .NET but it is still not commonly used (except in the .NET Framework of course). SafeHandle provides many advantages:
- The finalizer logic is already implemented, preventing you from doing mistake while implementing them. Also, they inherits from
CriticalFinalizerObject making them more reliable than other objects (could be freed even when
OutOfMemoryException are raised).
- The objects that own a
SafeHandle do not need to have a finalizer, so they are easier to write.
- Objects with finalizer have special treatments in the Garbage Collector. You should keep them as small as possible to avoid promoting a large object graph due to finalization. SafeHandles are minimal wrappers around unmanaged resources, so it reduce the issue.
- SafeHandles are well-integrated with P/Invoke. You can use
SafeHandle-derived class in the definition of the method instead of
IntPtr, so they are strongly-typed.
- SafeHandles are well-integrated with the Garbage Collector. You don't need to use
GC.KeepAlive explicitly when calling a native method, the CLR will do what is needed to avoid finalizing the
SafeHandle while calling a native method.
- SafeHandles prevent handle-recycling vulnerability: Lifetime, GC.KeepAlive, handle recycling
SafeHandle is an abstract class, so you need to inherit from it. When you inherit from
SafeHandle, you must override the following members:
ReleaseHandle. You should also provide a default constructor that calls the base constructor with a value that represents 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. Note that the
ReleaseHandle method is guaranteed to be called only once and only if the handle is valid as defined by the
IsInvalid property. You can also inherit from
SafeHandleZeroOrMinusOneIsInvalid which handle the invalid handle.
// inherits from SafeHandleZeroOrMinusOneIsInvalid, so IsInvalid is already implemented.
internal sealed class MySafeHandle : SafeHandleZeroOrMinusOneIsInvalid
// A default constructor is required for P/Invoke to instantiate the class
: base(ownsHandle: true)
protected override bool ReleaseHandle()
internal static class NativeMethods
// Returns the SafeHandle instead of IntPtr
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
internal extern static MySafeHandle CreateFile(String fileName, int dwDesiredAccess, System.IO.FileShare dwShareMode, IntPtr securityAttrs_MustBeZero, System.IO.FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr TemplateFile_MustBeZero);
// Take a SafeHandle in parameter instead of IntPtr
[DllImport("kernel32", SetLastError = true)]
internal extern static int ReadFile(MySafeHandle handle, byte bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);
[DllImport("kernel32", SetLastError = true)]
internal extern static bool CloseHandle(IntPtr handle);
As all the hard work is done by the
SafeHandle, your code is much simpler! Here's an example of usage of
public sealed class MyFileWrapper : IDisposable
private readonly MySafeHandle _handle;
public MyFileWrapper(string fullPath)
_handle = NativeMethods.CreateFile(fullPath, ...);
// - There is no need to implement a finalizer, MySafeHandle already has one
// - You do not need to protect against multiple disposing, MySafeHandle already does
public void Dispose()
When working with unmanaged resources, you should consider:
- Using an existing
SafeHandle if possible
- If not possible, subclass
SafeHandle to create one that meets your needs. This class should not do anything more than managing unmanaged resources. It should be sealed.
- If that's not possible, create your own class which implements
IDisposable and a finalizer
- The class should be sealed
- If sealing the class is not possible, add a method
protected void Dispose(bool disposing), so subclasses can implements the dispose pattern correctly.
Do you have a question or a suggestion about this post? Contact me on Twitter or by email!