GlobalLock 的模板包装器






4.38/5 (5投票s)
安全管理句柄(
引言
我经常处理打印机。我的很多工作项目主要涉及打印,因此我有很多看起来像这样的代码
// Apply printer settings
DEVMODE* pDevMode = ::GlobalLock(hDevMode);
if (pDevMode != NULL)
{
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
...
}
::GlobalUnlock(hDevMode);
有时是一个 DEVMODE
句柄,有时是一个 DEVNAMES
,偶尔也可能是处理剪贴板时使用的句柄。 每次操作都相同 - 锁定句柄以返回指针,对内存执行操作,然后解锁句柄 - 这非常适合使用辅助类。
CGlobalLock
CGlobalLock
是一个模板,允许您锁定对句柄的访问并返回任何类型的指针 - DEVMODE
、DEVNAMES
等。 该代码确保句柄被自动锁定和解锁,并且提供了一些有用的运算符重载,允许您在原本使用指针的地方使用 CGlobalLock
。
例如,上面的代码可以写成
rec::CGlobalLock<DEVMODE> dm(hDevMode);
dm->dmOrientation = DMORIENT_LANDSCAPE;
只需确保将所需的指针类型作为模板参数传递,然后让类完成其余操作。 这比原始代码更简单,并且没有句柄保持解锁的风险。
同样,您可能想要锁定一个 DEVNAMES
句柄
rec::CGlobalLock<DEVNAMES> dn(hDevNames);
运算符重载意味着您可以将 CGlobalLock
对象传递给期望指定指针类型指针的函数,例如:
rec::CGlobalLock<DEVMODE> dm(hDevMode);
...
HDC hDC = ::CreateDC(_T("Driver"), _T("Device"), _T("Output"), dm);
在这种情况下,CreateDC
函数将 DEVMODE*
作为最后一个参数。
如果您仍然需要直接访问底层指针,请使用 Get
方法
rec::CGlobalLock<DEVMODE> dm(hDevMode);
...
DEVMODE* pDevMode = dm.Get();
由于 operator*
重载,您还可以复制指针内容。 例如
rec::CGlobalLock<DEVMODE> dm(hDevMode);
...
DEVMODE dmCopy = *dm;
global_lock.h
这是完整的代码
#pragma once
#include <assert.h>
#include <stdexcept>
namespace rec
{
/// Template class used to lock a HANDLE and expose a pointer
template <typename T>
class CGlobalLock
{
private:
HANDLE m_h; ///< The handle to lock/unlock
T* m_p; ///< Pointer returned by ::GlobalLock
public:
/// Default constructor
CGlobalLock() : m_h(NULL), m_p(NULL)
{
}
/// Constructor from a HANDLE
CGlobalLock(HANDLE h) : m_h(h)
{
Lock();
}
/// Destructor - Unlocks the handle
~CGlobalLock()
{
Unlock();
}
/// HANDLE assignment operator
CGlobalLock& operator=(HANDLE h)
{
Unlock();
m_h = h;
Lock();
}
/// Return the pointer
T* Get() const
{
assert(m_p != NULL);
return m_p;
}
/// Operator overloads
T* operator->() const
{
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:
/// Lock the handle
void Lock()
{
if (m_h != NULL)
{
m_p = reinterpret_cast<T*>(::GlobalLock(m_h));
// Did the lock succeed?
if (m_p == NULL)
{
// The handle is probably invalid
throw std::runtime_error("Failed to lock handle");
}
}
else
{
m_p = NULL;
}
}
/// Unlock the handle
void Unlock()
{
if (m_h != NULL)
{
::GlobalUnlock(m_h);
m_h = NULL;
}
}
};
}
异常
如果 GlobalLock
函数未能返回指针,则会抛出 std::runtime_error
,因为很可能传递了一个错误的句柄,我认为这是一个异常情况。 但是,我也理解异常并不是每个人都喜欢的方式,因此您可以随意删除此代码,如果需要,将 m_p
设置为 NULL
。 请注意,每个返回 m_p
的方法也会在指针为 NULL
时进行 assert
断言,您也可以随意更改(您可能希望使用 MFC ASSERT
宏,或 ATLASSERT
等)。
WTL
请注意,如果您正在使用 Windows 模板库,则为此提供了一个专门用于 DEVMODE
句柄的包装器(称为 CDevModeT
),但该类更通用,并且据我所知,没有 WTL 等效的包装器类用于 DEVNAMES
句柄。
示例
由于此模板内容不多,因此我没有提供任何可下载的示例。 但是,如果需要,我很乐意提供一些。 如果有任何问题或疑问,请随时添加评论。