Wrapping Unmanaged Resources

I recently wrote about three common causes of memory leaks in managed applications.  In that post, I mentioned that failing to release unmanaged resources was one cause.  Today, I'd like to talk a bit about one approach to working with unmanaged resources that has worked well in my code.

Wrapping Unmanaged Resources
While at MEDC 2005, I showed a simple example of wrapping a DeviceContext (a.k.a. DC) as part of my Debugging .NET Compact Framework Applications session.  Here, I would like to expand upon that example.  In the snippet below, I show finalization and disposal.  I will leave it to the reader to add P/Invoke signatures.

//--------------------------------------------------------------------- //THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY //KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE //IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A //PARTICULAR PURPOSE. //---------------------------------------------------------------------using System;using System.Diagnostics;using System.Runtime.InteropServices;namespace Snippet{    /// <summary>    /// Wrapper for the Win32 Device Context (DC)    /// </summary>    public class DeviceContext : IDisposable    {        // the Win32 Device Context (DC) object        private IntPtr hDC;        // window handle associated with the DC        private IntPtr hWnd;        /// <summary>        /// Constructor.        /// </summary>        /// <param name="hwnd">        /// The window handle for which to retrieve the device context        /// </param>        public DeviceContext(IntPtr hwnd)        {            // call the p/invoke to get the device context for the specified window handle            IntPtr hdc = GetDC(hwnd);            // verify that the GetDC call succeeded            if(hdc == IntPtr.Zero)            {                throw new Exception("Failed to get the DeviceContext for the specified window handle");            }            // store the window handle and device context for future reference            this.hWnd = hwnd;            this.hDC = hdc;        }        /// <summary>        /// Finalizer        /// </summary>        ~DeviceContext()        {            // dispose the object (unmanaged resources)            this.Dispose(false);        }        /// <summary>        /// Cleanup the object (implementation if IDisposable::Dispose)        /// </summary>        public void Dispose()        {            // clean up our resources (managed and unmanaged resources)            this.Dispose(true);                        // suppress finalization            //  the finalizer also calls our cleanup code            //  cleanup need only occur once            GC.SuppressFinalize(this);        }        /// <summary>        /// Cleanup resources used by the object        /// </summary>        /// <param name="disposing">        /// Are we fully disposing the object?          ///  True will release all managed resources, unmanaged resources are always released        /// </param>        protected virtual void Dispose(Boolean disposing)        {            if(disposing)            {              //*** release any managed resources            }            // release unmanaged resources            if(this.hDC == IntPtr.Zero)            {                // we're already been disposed, nothing left to do                return;            }            Int32 ret = ReleaseDC(this.hWnd, this.hDC);            this.hDC = IntPtr.Zero;            this.hWnd = IntPtr.Zero;                        // assert if the DC was not released.            Debug.Assert(ret != 0,                         "Failed to release DeviceContext.");        }        //*** add desired p/invoke definitions    }}

With the resource wrapped, your application can easily interoperate with the native resource.

// get the device context for the primary displayDeviceContext dc = new DeviceContext(IntPtr.Zero);//*** perform desired operations on the device context// cleanupdc.Dispose();
The above snippet uses a Debug.Assert to provide notification of a failure when releasing the unmanaged resource:

// assert if the DC was not released.Debug.Assert(ret != 0, "Failed to release DeviceContext.");
By running the debug build during development, you can identify scenarios where cleanup fails and take steps to address the issue before the product ships to your customers.

For additional information regarding finalization and disposal, I recommend reading Implementing Finalize and Dispose to Clean Up Unmanaged Resources, on MSDN.

-- DK

[Edit: Fixed assert]

This posting is provided "AS IS" with no warranties, and confers no rights.