用于保存和恢复用户打印机设置的小类
包装 GlobalAlloc API 的通用模板类
引言
这是一个简单的、可直接使用的 C++ 模板类,可以轻松保存和恢复用户的打印机选择和设置。它通过包装 HDEVMODE
和 HDEVNAMES
结构来实现。这个类是一个模板类,因此可以用于保存和恢复由 GlobalAlloc
API 分配的任何内存块。
背景
我喜欢编写简单易用的工具,不会让用户感到过于复杂或困惑。我经常发现用户通常总是使用相同的设置打印到同一台打印机。我自己,我喜欢大多数文档都以黑白打印,我发现每次打印时都要在打印对话框选项中重新选择我的打印机及其选项,这很烦人。所以必须有一种方法来持久化我的设置,以便所有内容都保持不变,除非我想更改它。
对于打印,实现这一点的方法是保存和恢复 Windows 和打印机驱动程序使用的 HDEVNAMES
和 HDEVMODE
结构。由于这些结构末尾经常分配可变数量的额外数据,因此这并非易事。
这里提供的类是我解决这个问题的方案。我的命名技巧不是很富有想象力,所以我想出的名字是 MyGHND
。“My
”是因为我写的,“GHND
”是因为这是我在代码中分配内存时与 GlobalAlloc
API 函数一起使用的标志。这个类的一个优点是它是一个通用的模板类,因此可以由任何已分配或需要由 GlobalAlloc
API 分配的数据块使用。
Using the Code
首先,我将向您展示一些使用此类的代码。第一个代码块使用 MFC 的 CPrintDialog
来显示打印设置对话框。它被初始化为先前保存的设置,或者如果不存在已保存的设置或已保存的设置在某种程度上已损坏,则初始化为系统默认设置。
void CSchedDlg::OnBnClickedButtonPrintSetup()
{
// Print Setup Dialog
CPrintDialog PD(TRUE);
// SchedData.GetString() reads a text string from an XML file
// Here we show the MyGHND c'tor that takes a text string
MyGHND<DEVMODE> GDevMode(SchedData.GetString(L"DevMode"));
// Check if the DEVMODE structure is valid
if (GDevMode)
{
// make the hDevMode of the PRINTDLG structure point to our DEVMODE
PD.m_pd.hDevMode = GDevMode;
// Here we use the default c'tor
MyGHND<DEVNAMES> GDevNames;
// And then the assignment operator that takes a text string
GDevNames = SchedData.GetString(L"DevNames");
if (GDevNames)
{
PD.m_pd.hDevNames = GDevNames;
}
if (IDOK == PD.DoModal())
{
// User pressed OK so we save our settings
// Reinitialize our DEVMODE to the one in the PRINTDLG
GDevMode = PD.m_pd.hDevMode;
// SchedData.SetString saves a text string to a XML file
SchedData.SetString(L"DevMode", GDevMode);
GDevNames = PD.m_pd.hDevNames;
SchedData.SetString(L"DevNames", GDevNames);
}
}
下一个代码块显示了如何使用保存的数据创建打印机设备上下文。
void CSchedDlg::OnBnClickedButtonPrint()
{
// Print a test page
// Load the DEVNAMES and DEVMODE structures from the XML file
MyGHND<DEVMODE> GDevMode(SchedData.GetString(L"DevMode"));
MyGHND<DEVNAMES> GDevNames(SchedData.GetString(L"DevNames"));
// Check if they are valid
if (GDevNames && GDevMode)
{
// Get the pointer to our DEVNAMES
LPDEVNAMES pDevNames = GDevNames;
// And extract the device names from it
std::wstring Driver = (LPWSTR)pDevNames + GDevNames->wDriverOffset;
std::wstring Device = (LPWSTR)pDevNames + GDevNames->wDeviceOffset;
std::wstring Output = (LPWSTR)pDevNames + GDevNames->wOutputOffset;
// Create the printer DC and print
CDC PrinterDC;
if (PrinterDC.CreateDCW(Driver.c_str(),
Device.c_str(),
Output.c_str(),
(LPDEVMODE)GDevMode))
{
PrinterDC.StartDocW(L"Test Document");
PrinterDC.StartPage();
PrinterDC.TextOutW(0, 0, L"Hello World");
PrinterDC.EndPage();
PrinterDC.EndDoc();
return;
}
}
MessageBox(L"First Select a Printer using \"Print Setup...\"",
L"Unable to Print",
MB_OK | MB_ICONINFORMATION);
return;
}
虽然此代码使用了 SetString()
和 GetString()
函数,但也可以使用任何用于从任何类型存储设置或获取文本字符串的方法。它可以是简单的文本文件、注册表,甚至是某种数据库。用于保存数据的文本字符串将只包含可打印的 ASCII 字符。它不会包含回车符、换行符或 NULL
字符。您选择用于保存和读取这些文本字符串的方法超出了本文的范围。
MyGHND 类成员
构造函数
MyGHND
类有五个构造函数
template <typename TYPE>
class MyGHND
{
public:
// Default c'tor. Allocate sizeof(TYPE) bytes
MyGHND (bool AutoFree = true);
// Allocate specified number of bytes
MyGHND (size_t ByteCount, bool AutoFree = true);
// AutoFree is false because we did not allocate the memory, so do not free it.
MyGHND (HGLOBAL Object, bool AutoFree = false);
// copy c'tor
MyGHND (const MyGHND<TYPE>> &Other, bool AutoFree = true);
// Decode an encoded text string
MyGHND (std::tstring EncodedString, bool AutoFree = true);
参数
AutoFree
指定当 HGLOBAL
句柄不再需要时是否应调用 GlobalFree
API 函数。对于大多数构造函数,它默认为 true
,因为该类分配内存,因此应该负责释放它。唯一默认值为 false
的情况是当传入现有的 HGLOBAL
句柄时。由于该类未分配内存,因此不应删除它。
ByteCount
指定要分配的内存量(以字节为单位)。
Object
是一个先前分配的 HGLOBAL
句柄,将由此 MyGHND
对象包装。
Other
是一个 MyGHND
对象,将复制到此新的 MyGHND
对象中。
EncodedString
是一个 std::tstring
,它之前已被 ToString
成员函数编码。此 string
将被解码,数据将被输入到此 MyGHND
对象中。
五个构造函数中的四个如果 GlobalAlloc
函数失败,可能会抛出 std::runtime_error
异常。唯一不抛出异常的构造函数是接受 HGLOBAL
句柄的那个,因为它不调用 GlobalAlloc
。
析构函数
public: ~MyGHND ()
析构函数将对锁定的句柄调用 GlobalUnlock()
,如果 AutoFree
(在构造函数中指定)设置为 true
,则析构函数将调用 GlobalFree
来释放已分配的内存。
内存管理函数
MyGHND
有三个内存管理函数。
public:
// calls GlobalAlloc(HGND, ByteCount) to allocate the memory
void Init(size_t ByteCount);
// calls GlobalFree() to free the memory
void Free();
// get the amount of memory allocated, in bytes
size_t Size();
Init()
将首先释放先前已分配的任何内存,然后再分配新内存。如果 GlobalAlloc
函数失败,它可能会抛出 std::runtime_error
。
使用 Free()
时要小心,因为它会调用 GlobalFree
,而不考虑 AutoFree
标志(在构造函数中指定)的状态。更安全的清除内存的方法是调用 Init(0)
,并将 ByteCount
设置为零。
Size()
简单地返回已分配内存块的大小(以字节为单位)。返回值可能大于 Init()
中调用的值。
类型转换运算符
MyGHND
中有五个类型转换运算符
public:
// return a pointer to TYPE
operator TYPE * ();
// Allow access to TYPE's members
TYPE * operator -> ();
// get the HGLOBAL handle
operator HGLOBAL ()
// Get an encoded string in order to serialize this object
operator std::tstring ();
// check the validity of this object
operator bool ();
如果底层句柄为 NULL
或内存块大小为零,则 bool()
运算符返回 false
。
赋值运算符
MyGHND
有五个赋值运算符。它们都复制数据,而不修改原始数据。
public:
// copy the data
MyGHND<TYPE> & operator =(const TYPE *Pointer);
// copy the data
MyGHND<TYPE> & operator =(const TYPE &Reference);
// copy the data
MyGHND<TYPE> & operator =(const MyGHND<TYPE> &Other);
// copy the data
MyGHND<TYPE> & operator =(const HGLOBAL OtherGlobalHandle);
// Decode and assign the EncodedString
MyGHND<TYPE> & operator =(const std::tstring &EncodedString);
序列化函数
public:
// Encode the data to a text string and return the encoded string.
std::tstring ToString();
// Take the previously encoded string and initialize this object with it.
bool FromString(const std::tstring &EncodedString);
ToString
返回编码的 string
。如果遇到错误,编码的 string
将为空。
FromString
如果成功则返回 true
,如果提供的 string
无效或发生其他错误则返回 false
。
历史
- 2014 年 1 月 22 日 - 文章已发布