Introduction
This is simple drop in C++ template class that makes it easy to save and restore a user's printer selection and settings. It does so by wrapping the HDEVMODE
and HDEVNAMES
structures. This class is a template class so it can be used to save and restore any memory block that has been allocated by the GlobalAlloc
API.
Background
I like to write simple easy to use tools that are not overly complicated or confusing to my users. One of the things I find most often is that users usually always print to the same printer using the same settings. Myself, I like to print most documents in black and white, and I find it annoying having to go through the print dialog options to reselect my printer and its options every time I print. So there must be a way to persist my settings so that everything stays the same unless I want to change it.
For printing, the way to accomplish this is to save and restore the HDEVNAMES
and HDEVMODE
structures that are used by windows and the printer drivers. Not always an easy task due to the variable amount of extra data that is often allocated onto the end of these structures.
The class presented here is my solution to this problem. My naming skills are not very imaginative so the name I came up with is MyGHND
. "My
" because I wrote it, and "GHND
" because that is the flag I used with the GlobalAlloc
API function when allocating memory within this code. One of the nice things about this class is that it is a generic template class so it can actually be used by any block of data that has been or needs to be allocated by the GlobalAlloc
API.
Using the Code
First, I am going to show you some code that uses this class. This first code block uses the MFC CPrintDialog
to show a Print Setup dialog. It gets initialized to either the previously saved settings or to the system default settings if there are no saved settings or the saved settings are somehow corrupted.
void CSchedDlg::OnBnClickedButtonPrintSetup()
{
CPrintDialog PD(TRUE);
MyGHND<DEVMODE> GDevMode(SchedData.GetString(L"DevMode"));
if (GDevMode)
{
PD.m_pd.hDevMode = GDevMode;
MyGHND<DEVNAMES> GDevNames;
GDevNames = SchedData.GetString(L"DevNames");
if (GDevNames)
{
PD.m_pd.hDevNames = GDevNames;
}
if (IDOK == PD.DoModal())
{
GDevMode = PD.m_pd.hDevMode;
SchedData.SetString(L"DevMode", GDevMode);
GDevNames = PD.m_pd.hDevNames;
SchedData.SetString(L"DevNames", GDevNames);
}
}
This next code block shows how to create a printer device context using the saved data.
void CSchedDlg::OnBnClickedButtonPrint()
{
MyGHND<DEVMODE> GDevMode(SchedData.GetString(L"DevMode"));
MyGHND<DEVNAMES> GDevNames(SchedData.GetString(L"DevNames"));
if (GDevNames && GDevMode)
{
LPDEVNAMES pDevNames = GDevNames;
std::wstring Driver = (LPWSTR)pDevNames + GDevNames->wDriverOffset;
std::wstring Device = (LPWSTR)pDevNames + GDevNames->wDeviceOffset;
std::wstring Output = (LPWSTR)pDevNames + GDevNames->wOutputOffset;
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;
}
While this code used a SetString()
and a GetString()
function, any method used to set or get text strings from any type of storage can be used. It could be simple text files, the registry, or even a database of some sort. The text string used to save the data will only ever contain printable ASCII characters. It will not have any carriage return, new line, nor NULL
characters in it. The method you choose to use to save and read these text strings is beyond the scope of this article.
The MyGHND Class Members
Constructors
The MyGHND
class has five constructors:
template <typename TYPE>
class MyGHND
{
public:
MyGHND (bool AutoFree = true);
MyGHND (size_t ByteCount, bool AutoFree = true);
MyGHND (HGLOBAL Object, bool AutoFree = false);
MyGHND (const MyGHND<TYPE>> &Other, bool AutoFree = true);
MyGHND (std::tstring EncodedString, bool AutoFree = true);
Parameters
AutoFree
specifies if the GlobalFree
API function should be called on the HGLOBAL
handle when it is no longer needed. It defaults to true
for most of the constructors since this class allocates the memory it should be responsible for freeing it. The only time it defaults to false
is when an existing HGLOBAL
handle is passed in. Since this class did not allocate the memory, it should not delete it.
ByteCount
specifies the amount of memory, in bytes, that is to be allocated.
Object
is a previously allocated HGLOBAL
handle that is to be wrapped by this MyGHND
object.
Other
is a MyGHND
object that will be copied into this new MyGHND
object.
EncodedString
is a std::tstring
that was previously encoded by the ToString
member function. This string
will be decoded and the data entered into this MyGHND
object.
Four of the five constructors can throw a std::runtime_error
exception if the GlobalAlloc
function fails. The only constructor that does not throw an exception is the one that takes an HGLOBAL
handle as it does not call GlobalAlloc
.
Destructor
public: ~MyGHND ()
The destructor will call GlobalUnlock()
on the locked handle, and if AutoFree
(specified in the constructor) is set to true
, the destructor will call GlobalFree
to free the memory allocated.
Memory Management Functions
MyGHND
has three memory management functions.
public:
void Init(size_t ByteCount);
void Free();
size_t Size();
Init()
will first free any memory that had been previously allocated before allocating the new memory. It can throw a std::runtime_error
if the GlobalAlloc
function fails.
Be careful when using Free()
as it will call GlobalFree
regardless of the state of the AutoFree flag (specified in the constructor). A safer way to clear the memory is to call Init(0)
with the ByteCount
set to zero.
Size()
simple returns the size of the memory block allocated, in bytes. The return value can be larger then the value called for in Init()
.
Type Casting Operators
There are five type casting operators in MyGHND
:
public:
operator TYPE * ();
TYPE * operator -> ();
operator HGLOBAL ()
operator std::tstring ();
operator bool ();
The bool()
operator returns false
if the underlying handle is NULL
or the size of the memory block is zero.
Assignment Operators
MyGHND
has five assignment operators. They all make a copy of the data, leaving the original data untouched.
public:
MyGHND<TYPE> & operator =(const TYPE *Pointer);
MyGHND<TYPE> & operator =(const TYPE &Reference);
MyGHND<TYPE> & operator =(const MyGHND<TYPE> &Other);
MyGHND<TYPE> & operator =(const HGLOBAL OtherGlobalHandle);
MyGHND<TYPE> & operator =(const std::tstring &EncodedString);
Serialization Functions
public:
std::tstring ToString();
bool FromString(const std::tstring &EncodedString);
ToString
returns the encoded string
. The encoded string
will be empty if an error was encountered.
FromString
returns true
if successful or false
if the supplied string
was invalid or another error occurred.
History
- January 22, 2014 - Article published