SafeHandle Class

Represents a wrapper class for operating system handles. This class must be inherited.

Namespace:  System.Runtime.InteropServices
Assembly:  mscorlib (in mscorlib.dll)

[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
public abstract class SafeHandle : CriticalFinalizerObject, 
	IDisposable

The SafeHandle type exposes the following members.

  NameDescription
Protected methodSafeHandle()
Protected methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsSafeHandle(IntPtr, Boolean)Initializes a new instance of the SafeHandle class with the specified invalid handle value.
Top

  NameDescription
Public propertySupported by Portable Class LibrarySupported in .NET for Windows Store appsIsClosedGets a value indicating whether the handle is closed.
Public propertySupported by Portable Class LibrarySupported in .NET for Windows Store appsIsInvalidWhen overridden in a derived class, gets a value indicating whether the handle value is invalid.
Top

  NameDescription
Public methodCloseMarks the handle for releasing and freeing resources.
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsDangerousAddRefManually increments the reference counter on SafeHandle instances.
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsDangerousGetHandleReturns the value of the handle field.
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsDangerousReleaseManually decrements the reference counter on a SafeHandle instance.
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsDispose()Releases all resources used by the SafeHandle class.
Protected methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsDispose(Boolean)Releases the unmanaged resources used by the SafeHandle class specifying whether to perform a normal dispose operation.
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsEquals(Object)Determines whether the specified object is equal to the current object. (Inherited from Object.)
Protected methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsFinalizeFrees all resources associated with the handle. (Overrides CriticalFinalizerObject.Finalize().)
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsGetHashCodeServes as the default hash function. (Inherited from Object.)
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsGetTypeGets the Type of the current instance. (Inherited from Object.)
Protected methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsMemberwiseCloneCreates a shallow copy of the current Object. (Inherited from Object.)
Protected methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsReleaseHandleWhen overridden in a derived class, executes the code required to free the handle.
Protected methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsSetHandleSets the handle to the specified pre-existing handle.
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsSetHandleAsInvalidMarks a handle as no longer used.
Public methodSupported by Portable Class LibrarySupported in .NET for Windows Store appsToStringReturns a string that represents the current object. (Inherited from Object.)
Top

  NameDescription
Protected fieldSupported by Portable Class LibrarySupported in .NET for Windows Store appshandleSpecifies the handle to be wrapped.
Top

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. Before the .NET Framework version 2.0, all operating system handles could only be encapsulated in the IntPtr managed wrapper object.

The SafeHandle class contains a finalizer that ensures that the handle is closed and is guaranteed to run, even during unexpected AppDomain unloads when a host may not trust the consistency of the state of the AppDomain

For more information about the benefits of using a SafeHandle, see Safe Handles and Critical Finalization.

This class is abstract because you cannot create a generic handle. To implement SafeHandle, you must create a derived class. To create SafeHandle derived classes, you must know how to create and free an operating system handle. This process is different for different handle types because some use CloseHandle, while others use more specific methods such as UnmapViewOfFile or FindClose. For this reason, you must create a derived class of SafeHandle for each operating system handle type; such as MySafeRegistryHandle, MySafeFileHandle, and MySpecialSafeFileHandle. Some of these derived classes are prewritten and provided for you in the Microsoft.Win32.SafeHandles namespace.

NoteNote

Writing your own classes derived from SafeHandle is an advanced programming feature. A set of prewritten classes derived from SafeHandle is provided as abstract derivations, and this set is located in the Microsoft.Win32.SafeHandles namespace. These classes are designed to provide common functionality supporting file and operating system handles.

Notes to Inheritors

When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle.

You should also provide a default constructor that calls the base constructor with a value that represent an invalid handle value, and a Boolean value indicating whether the native handle will be owned by the SafeHandle, and consequently freed when that SafeHandle has been disposed.

The following code example creates a custom safe handle for an operating system file handle, deriving from SafeHandleZeroOrMinusOneIsInvalid. It reads bytes from a file and displays their hexadecimal values. It also contains a fault testing harness that causes the thread to abort, but the handle value is freed. When using an IntPtr to represent handles, the handle is occasionally leaked due to the asynchronous thread abort.

You will need a text file in the same folder as the compiled application. Assuming that you name the application "HexViewer", the command line usage is:

HexViewer <filename> -Fault

Optionally specify -Fault to intentionally attempt to leak the handle by aborting the thread in a certain window. Use the Windows Perform.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);
                }
            }

        }
    }
}

.NET Framework

Supported in: 4.5.2, 4.5.1, 4.5, 4, 3.5, 3.0, 2.0

.NET Framework Client Profile

Supported in: 4, 3.5 SP1

Portable Class Library

Supported in: Portable Class Library

.NET for Windows Store apps

Supported in: Windows 8

.NET for Windows Phone apps

Supported in: Windows Phone 8.1, Windows Phone 8, Silverlight 8.1

  • InheritanceDemand 

    for full trust for inheritors. This member cannot be inherited by partially trusted code.

  • SecurityCriticalAttribute 

    requires full trust for the immediate caller. This member cannot be used by partially trusted or transparent code.

Windows Phone 8.1, Windows Phone 8, Windows 8.1, Windows Server 2012 R2, Windows 8, Windows Server 2012, Windows 7, Windows Vista SP2, Windows Server 2008 (Server Core Role not supported), Windows Server 2008 R2 (Server Core Role supported with SP1 or later; Itanium not supported)

The .NET Framework does not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft