Introduction
I work with printers a lot. Many of my work projects deal primarily with printing, and, as a consequence, I have a lot of code that looks something like this:
DEVMODE* pDevMode = ::GlobalLock(hDevMode);
if (pDevMode != NULL)
{
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
...
}
::GlobalUnlock(hDevMode);
Sometimes it's a DEVMODE
handle, other times a DEVNAMES
, and occasionally it might be a handle used when dealing with the Clipboard. All of the time the actions are the same - lock the handle to return a pointer, do something with the memory, then unlock the handle - a perfect candidate for a helper class.
CGlobalLock
CGlobalLock
is a template that allows you to lock access to a handle and return any type of pointer - DEVMODE
, DEVNAMES
, etc. The code ensures the handle is locked and unlocked automatically, and some useful operator overloads have been provided that allow you to use CGlobalLock
where you would have had a pointer.
For example, the above code could be written as:
rec::CGlobalLock<DEVMODE> dm(hDevMode);
dm->dmOrientation = DMORIENT_LANDSCAPE;
Simply ensure that the type of pointer you require is passed as the template argument, and let the class do the rest. This is simpler than the original code, and there is no chance of that pesky handle staying unlocked.
Similarly, you might want to lock a DEVNAMES
handle:
rec::CGlobalLock<DEVNAMES> dn(hDevNames);
The operator overloads mean that you can pass a CGlobalLock
object to a function that expects a pointer to the specified pointer type, e.g.:
rec::CGlobalLock<DEVMODE> dm(hDevMode);
...
HDC hDC = ::CreateDC(_T("Driver"), _T("Device"), _T("Output"), dm);
In this case, the CreateDC
function takes a DEVMODE*
as the final argument.
If you still need direct access to the underlying pointer, then use the Get
method:
rec::CGlobalLock<DEVMODE> dm(hDevMode);
...
DEVMODE* pDevMode = dm.Get();
Thanks to the operator*
overload, you can also copy the pointer contents. For example:
rec::CGlobalLock<DEVMODE> dm(hDevMode);
...
DEVMODE dmCopy = *dm;
global_lock.h
Here is the code in full:
#pragma once
#include <assert.h>
#include <stdexcept>
namespace rec
{
template <typename T>
class CGlobalLock
{
private:
HANDLE m_h; {
assert(m_p != NULL);
return m_p;
}
T& operator*() const
{
assert(m_p != NULL);
return *m_p;
}
operator T*() const
{
assert(m_p != NULL);
return m_p;
}
private:
void Lock()
{
if (m_h != NULL)
{
m_p = reinterpret_cast<T*>(::GlobalLock(m_h));
if (m_p == NULL)
{
throw std::runtime_error("Failed to lock handle");
}
}
else
{
m_p = NULL;
}
}
void Unlock()
{
if (m_h != NULL)
{
::GlobalUnlock(m_h);
m_h = NULL;
}
}
};
}
Exceptions
If the GlobalLock
function fails to return a pointer, then a std::runtime_error
is thrown, as the chances are a bad handle has been passed, which I think is an exceptional circumstance. However, I also appreciate that exceptions are not everybody's cup of tea, so feel free to remove this code and leave m_p
as NULL
if you prefer. Note that each method that returns m_p
will also assert
if the pointer is NULL
, which you are also free to change (you may want to use the MFC ASSERT
macro instead, or ATLASSERT
, etc.).
WTL
Note that if you are using the Windows Template Library, then there is a wrapper specifically for DEVMODE
handles provided (called CDevModeT
), but this class is more generic, and, as far as I can see, there is no WTL equivalent wrapper class for DEVNAMES
handles.
Samples
As there isn't much to this template, I haven't provided any downloadable samples. However, I am happy to put something together if requested. If there are problems or questions, feel free to add a comment.