65.9K
CodeProject 正在变化。 阅读更多。
Home

GlobalLock 的模板包装器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.38/5 (5投票s)

2006年12月7日

CPOL

2分钟阅读

viewsIcon

24932

安全管理句柄( 适用于打印机 DEVMODE 和 DEVNAMES)。

引言

我经常处理打印机。我的很多工作项目主要涉及打印,因此我有很多看起来像这样的代码

// Apply printer settings
DEVMODE* pDevMode = ::GlobalLock(hDevMode);
if (pDevMode != NULL)
{
 pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
 ...
}
::GlobalUnlock(hDevMode);

有时是一个 DEVMODE 句柄,有时是一个 DEVNAMES,偶尔也可能是处理剪贴板时使用的句柄。 每次操作都相同 - 锁定句柄以返回指针,对内存执行操作,然后解锁句柄 - 这非常适合使用辅助类。

CGlobalLock

CGlobalLock 是一个模板,允许您锁定对句柄的访问并返回任何类型的指针 - DEVMODEDEVNAMES 等。 该代码确保句柄被自动锁定和解锁,并且提供了一些有用的运算符重载,允许您在原本使用指针的地方使用 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 句柄。

示例

由于此模板内容不多,因此我没有提供任何可下载的示例。 但是,如果需要,我很乐意提供一些。 如果有任何问题或疑问,请随时添加评论。

© . All rights reserved.