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

用于保存和恢复用户打印机设置的小类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (2投票s)

2014年1月22日

CPOL

4分钟阅读

viewsIcon

13369

downloadIcon

385

包装 GlobalAlloc API 的通用模板类

引言

这是一个简单的、可直接使用的 C++ 模板类,可以轻松保存和恢复用户的打印机选择和设置。它通过包装 HDEVMODEHDEVNAMES 结构来实现。这个类是一个模板类,因此可以用于保存和恢复由 GlobalAlloc API 分配的任何内存块。

背景

我喜欢编写简单易用的工具,不会让用户感到过于复杂或困惑。我经常发现用户通常总是使用相同的设置打印到同一台打印机。我自己,我喜欢大多数文档都以黑白打印,我发现每次打印时都要在打印对话框选项中重新选择我的打印机及其选项,这很烦人。所以必须有一种方法来持久化我的设置,以便所有内容都保持不变,除非我想更改它。

对于打印,实现这一点的方法是保存和恢复 Windows 和打印机驱动程序使用的 HDEVNAMESHDEVMODE 结构。由于这些结构末尾经常分配可变数量的额外数据,因此这并非易事。

这里提供的类是我解决这个问题的方案。我的命名技巧不是很富有想象力,所以我想出的名字是 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 日 - 文章已发布 
© . All rights reserved.