Modern C++

Resource Management in the Windows API

The use of constructor/destructor pairs for resource management is the most important feature that distinguishes C++ from its predecessor.

It's fair to say the use of constructor/destructor pairs for resource management is one of the most fundamental aspects of C++. I might even go out on a limb and say it's the most important feature that distinguishes C++ from its predecessor, and even younger languages such as Java and C#. This ability to manage resources reliably and predictably is what makes it practical to write large programs in C++ that are both correct and efficient. In this column, I'll explore a few options for managing resources in the Windows API and describe some of the pros and cons of each.

My unique_handle Class Template
The Windows API exposes numerous resources that clearly represent objects of some kind but are only indirectly accessible via C-style functions or COM-style interfaces. In my MSDN Magazine July 2011 column, I walked through the implementation of the unique_handle class template. I wrote this class shortly after the release of Visual Studio 2010, which provided support for move semantics. Since then I've used it extensively to manage resources in the Windows API. If you haven't already done so, I encourage you to read that column before reading further here.

My unique_handle class template is very much modeled after the standard unique_ptr class template, providing exclusive ownership, move semantics, a safe Boolean operator, and all the stuff you'd expect. It does, however, lack the dereferencing capability that characterizes smart pointers, because that's not how handles function. Using the unique_handle class template is quite straightforward. You simply need to provide a traits class that adequately describes the type and lifetime semantics for the particular type of handle. Here's an example that models a work object in the Windows thread pool:

struct work_traits
{
  typedef PTP_WORK pointer;

  static pointer invalid() throw()
  {
    return nullptr;
  }
 
  static void close(pointer value) throw()
  {
    CloseThreadpoolWork(value);
  }
};
 
typedef unique_handle<work_traits> work;

The pointer typedef provides the type of the handle. The invalid function returns the value of an invalid handle. This also happens to be the default value of any constructed unique_handle. Ultimately, the unique_handle is destroyed, at which point the close function is called. The unique_handle destructor is careful not to call this function if the handle is invalid. Given this typedef, it's quite simple to create and submit a thread pool work object (initialized with a lambda):

work w(CreateThreadpoolWork([] (PTP_CALLBACK_INSTANCE, void *, PTP_WORK)
{
  printf("work!\n");
},
nullptr, nullptr));

if (w) SubmitThreadpoolWork(w.get());

It's critical to not underestimate the importance of the preceding invalid function. The Windows API is rather inconsistent when it comes to the exact value of an invalid handle. As an example, here's a socket typedef I've used in the past:

struct socket_traits
{
  typedef SOCKET pointer;

  static pointer invalid() throw()
  {
    return INVALID_SOCKET;
  }

  static void close(pointer value) throw()
  {
    closesocket(value);
  }
};

typedef unique_handle<socket_traits> socket;

In this case, an invalid handle has a value of ~0 (the unsigned equivalent of -1) rather than nullptr (zero). You need to pay special attention to this. You can't even offer up the old "islands of consistency" excuse. Consider the CreateFile and CreateFileMapping functions that are naturally used together: the former returns INVALID_HANDLE_VALUE on failure while the latter returns NULL. To be fair, CreateFile is technically a file-management function, whereas CreateFileMapping is a memory-management function. Still, you need to be vigilant.

The WRL HandleT Class Template
C++ naturally allows you to fill in the gaps where various libraries fall short, but the best code is that which you don't need to write. Why maintain your own unique_handle when an adequate alternative might exist? Visual Studio 2012 shipped with the Windows SDK for Windows 8 that includes the Windows Runtime C++ Template Library (WRL). This library, while predominantly for writing COM servers and clients, also includes a set of wrapper classes for working with certain parts of the Windows API that aren't exposed through COM interfaces. Among these is the HandleT class template, which is functionally equivalent to my unique_handle class template. It also relies on a traits class. I could rewrite the thread pool work traits class shown previously to work with HandleT as shown in Listing 1.

Listing 1. A thread pool work traits class that works with HandleT.
struct work_traits
{
  typedef PTP_WORK Type;

  static Type GetInvalidValue() throw()
  { 
    return nullptr; 
  }

  static bool Close(Type value) throw()
  {
    CloseThreadpoolWork(value);
    return true;
  }
};

typedef HandleT<work_traits> work;

This doesn't provide the safe Boolean conversion that unique_handle does, but the IsValid method does the trick. I can then create and submit the thread pool work object in much the same way as before:

work w(CreateThreadpoolWork( ... ));
if (w.IsValid()) SubmitThreadpoolWork(w.Get());

I still prefer my unique_handle class template, but while HandleT is a little more verbose, it does provide a viable alternative and represents code I don't have to write and maintain myself. The only real drawback is that it's part of the Windows SDK for Windows 8, rather than Visual C++ itself. If you happen to be in the unfortunate position of targeting Windows XP, this solution won't be available. Unfortunately, HandleT also includes a virtual function, which means the resulting object will include a pointer to a vtable, thereby doubling the memory requirements compared to simply storing raw handles. This bloat is enough to keep me from using the WRL HandleT.

The Standard unique_ptr Class Template
My unique_handle class template isn't quite a smart pointer, in the sense that it doesn't provide any way to refer to the object being pointed to by the handle wrapper. This was by design, as the handles in the Windows API are for the most part opaque pointers. You must use a set of non-member functions to interact with the target resource.

Apart from this fact, unique_handle is very similar to the standard unique_ptr class template. Even though unique_ptr is designed predominantly for C++ pointers, it can also be used to handle the ownership of other types of resources, as well. unique_ptr relies on a template parameter that's called a deleter. The deleter can simply be a function or a function-like object that deletes the object owned by the unique_ptr. It doesn't take much to tweak my traits class to support this. Here's the updated thread pool example again:

struct work_traits
{
  typedef PTP_WORK pointer;

  void operator()(pointer value) throw()
  {
    CloseThreadpoolWork(value);
  }
};

typedef unique_ptr<PTP_WORK, work_traits> work;

Using this definition of work looks the same as when using my original unique_handle:

work w(CreateThreadpoolWork( ... ));
if (w) SubmitThreadpoolWork(w.get());

Can you spot the problem? Unfortunately, unique_ptr doesn't provide a way to define an invalid pointer value. This means relying on the unique_ptr Boolean operator is unreliable. It works reliably in this example because the Windows thread pool returns nullptr when it fails to create its various resources. But it doesn't work in the general case, with a file handle being the canonical example:

struct file_traits
{
  typedef HANDLE pointer;

  void operator()(pointer value) throw()
  {
    CloseHandle(value);
  }
};

typedef unique_ptr<HANDLE, file_traits> file;

If you were desperate, you might wrap the call to CloseHandle with a conditional expression that checks the value, but the more troubling aspect is that the unique_ptr Boolean operator is now broken:

file f(CreateFile( ... ));
if (f) // Success?

This would always succeed regardless of whether the file object was successfully created. Another solution is to wrap the Windows API creation functions in helper functions that return "smart" handles such as unique_handle. You can think of these as the equivalent of the standard make_unique and make_shared functions. This would even solve the problem with the unique_ptr Boolean operator.

The reality is that there's no simple answer. My unique_handle class template provides a correct and efficient solution, but is not in the box. The WRL HandleT is included, but only when using the Windows SDK for Windows 8 (and it adds some unnecessary overhead). Finally, unique_ptr is so very close to being perfect but lacks a way to express an alternative invalid pointer value. Repurposing unique_ptr for use with handles also feels a bit dirty, so I'll stick with my unique_handle class template. If you'd like to give it a try, then download a copy of handle.h from dx.codeplex.com. Still, any one of these is better than raw handles. The choice is yours!

About the Author

Kenny Kerr is a computer programmer based in Canada, an author for Pluralsight and a Microsoft MVP. He blogs at kennykerr.ca and you can follow him on Twitter at twitter.com/kennykerr.

comments powered by Disqus

Featured

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

  • TypeScript Tops New JetBrains 'Language Promise Index'

    In its latest annual developer ecosystem report, JetBrains introduced a new "Language Promise Index" topped by Microsoft's TypeScript programming language.

Subscribe on YouTube