WinHandle is a simple and easy to use C++20 wrapper for managing the lifetime of Windows handles. It mimics a shared pointer to ensure the handle is released when its reference count drops to zero.
Download WinHandle.h from this repository and place it in the include path of your project.
The WinHandle template takes the type of the owned handle as the first template parameter.
#include "WinHandle.h"
int main(int argc, char** argv)
{
WinHandle<HANDLE> dc{ &DeleteDC };
dc = CreateDC(TEXT("DISPLAY"), nullptr, nullptr, nullptr);
...
}
Some Windows handles use a value other than NULL to signify an invalid handle value. The null value can be specified as the second template parameter. For instance, CreateFile returns INVALID_HANDLE_VALUE to signal an invalid handle.
WinHandle<HANDLE, INVALID_HANDLE_VALUE> hFile { &CloseHandle };
hFile = CreateFile(TEXT("file.txt"), GENERIC_READ, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 0, nullptr);
if (hFile.valid())
{
...
}
Some handles are acquired by calling a function with a pointer to the handle as a parameter. WinHandle supports this using the .ptr() member function.
WinHandle<HCRYPTPROV> hProv;
if (CryptAcquireContext(hProv.ptr(), TEXT("MyKeyContainer"), NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))
{
...
}
If the WinHandle already contains a valid handle, that handle will be released and replaced by the new handle.
Some handles must be released using functions that take more parameters than just the handle. If the handle is the first parameter of the release function, the additional parameters can be specified in the WinHandle constructor. For instance, CryptReleaseContext takes an additional DWORD parameter.
WinHandle<HCRYPTPROV> hProv{ &CryptReleaseContext, static_cast<DWORD>(0) };
if (CryptAcquireContext(hProv.ptr(), TEXT("MyKeyContainer"), NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))
{
...
}
You can specify a custom function to be called when the handle should be released. WinHandle supports std::function, plain function pointers and member function pointers as handle release functions.
// std::function
WinHandle<HANDLE> { [](Handle h) { return TRUE; } };
// Function pointer
WinHandle<HANDLE> { &CloseHandle };
// Member function pointer
MyClass cls;
WinHandle<HANDLE> { &MyClass::releaseHandle, &cls };
// Initialize a WinHandle with a HANDLE to a file
WinHandle<HANDLE, INVALID_HANDLE_VALUE> handle1 { CreateFile(TEXT("File1.txt"), GENERIC_READ, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 0, nullptr), &CloseHandle };
// Assigning a new handle will release the original handle.
handle1 = CreateFile(TEXT("File2.txt", GENERIC_READ, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 0, nullptr);
// Create a new WinHandle
WinHandle<HANDLE, INVALID_HANDLE_VALUE> handle2 { CreateFile(TEXT("File1.txt"), GENERIC_READ, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 0, nullptr), &CloseHandle };
// The handle owned by handle1 is released and handle1 and handle2 will reference the same handle
handle1 = handle2;
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.