Introduction
I like to use auto_ptr
whenever is possible. It handles a lot of work for me, and saves a lot of debugging time. But couldn't I use the mechanism for other things than pointers? Of course: Win32 objects.
Background
There are some cases where I have to write a large initialization function that creates some handles. It is expected that I check the error code and, if something goes wrong somewhere, I close the created handles and exit the function, just like in the example below.
BOOL Start()
{
HANDLE hEvt = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == hEvt)
{
printf("Failed to create stop event\n");
return FALSE;
}
HKEY hRegKey;
if (RegCreateKey(HKEY_LOCAL_MACHINE, "File", &hkey))
{
printf("Failed to create registry key");
CloseHandle(hEvt);
return FALSE;
}
HANDLE hFile = CreateFile("registryfile.txt", ...);
if (NULL == hFile)
{
printf("Failed to create file\n");
RegCloseKey(hRegKey);
CloseHandle(hEvt);
return FALSE;
}
CloseHandle(hFile);
RegCloseKey(m_resVal.regKey);
CloseHandle(hEvt);
return TRUE;
}
I really don't like this style, because there are a lot of ways this can go wrong:
- I forget to add code for closing handles.
- Later in the development stage, I forget to add code for closing handles.
- Other programmers change my function and doesn't add code to close the handles.
- An unhandled exception is thrown.
Therefore, I developed a class that can dispose resources in the destructor, just like
auto_ptr
works. For the moment, there are only a few objects supported, but I intend to expand the class whenever I feel the need to add a new Win32 object.
Using the code
It's as simple as declaring an AutoRes
variable whose constructor receives the handle you want to forget about. This is how the function would look like now:
BOOL Start()
{
HANDLE hEvt = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == hEvt)
{
printf("Failed to create stop event\n");
return FALSE;
}
AutoRes evtRes(hEvt);
HKEY hRegKey;
if (RegCreateKey(HKEY_LOCAL_MACHINE, "File", &hkey))
{
printf("Failed to create registry key");
return FALSE;
}
AutoRes regRes(hRegKey);
HANDLE hFile = CreateFile("registryfile.txt", ...);
if (NULL == hFile)
{
printf("Failed to create file\n");
return FALSE;
}
AutoRes fileRes(hFile);
return true;
}
Points of Interest
The class is designed to work as fast as possible and be very small, and some flexibility was sacrificed in the name of speed and simplicity.
Since an instance can only hold a type of handle at a time, a union is used to hold the resource that is to be deleted. An instance can be initialized with specialized constructors, as well as with some specialized functions.
To add a new Win32 object, you'll have to:
- If the object is of type
HANDLE
and is closed with CloseHandle()
, use the existing constructor.
- If the object is of type
HANDLE
and is not closed with CloseHandle()
, add a new entry in the ResType
enumeration, add to the specialized constructor that receives a HANDLE
and an enum, then add to the Release()
function code for releasing the HANDLE
.
- If it's not a
HANDLE
object, add a new entry in the ResType
enum and in the ResVal
union, add a specialized constructor, and edit the Release()
function.
- That's all.
After I finished the university, I worked for two years in gaming industry, as a programmer in DirectX environment.
After that, I switched to computer security, which is a fascinating domain, where a lot of stuffs can be learn and experimented.