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 save and restore any memory
block that has been allocated by the GlobalAlloc API.
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 it's 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 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
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.
PD.m_pd.hDevMode = GDevMode;
GDevNames = SchedData.GetString(L"DevNames");
PD.m_pd.hDevNames = GDevNames;
if (IDOK == PD.DoModal())
GDevMode = PD.m_pd.hDevMode;
GDevNames = PD.m_pd.hDevNames;
This next code block shows how to create a printer device context using the saved data.
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;
PrinterDC.TextOutW(0, 0, L"Hello World");
MessageBox(L"First Select a Printer using \"Print Setup...\"",
L"Unable to Print",
MB_OK | MB_ICONINFORMATION);
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
The MyGHND class has five constructors.
template <typename TYPE>
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);
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
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 throow an exception
is the one that takes an
HGLOBAL handle as it does not call
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.
void Init(size_t ByteCount);
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
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.
simple returns the size of the memory block allocated, in bytes. The return value
can be larger then the value called for in
Type casting operators
There are five type casting operators in MyGHND
operator TYPE * ();
TYPE * operator -> ();
operator HGLOBAL ()
operator std::tstring ();
operator bool ();
bool() operator return false if the under lying handle if NULL or the size of the
memory block is zero.
MyGHND has five assignment operators. They all make a copy of the data, leaving the original data
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);
bool FromString(const std::tstring &EncodedString);
ToString returns the encoded string. The encoded string will be empty if an error
FromString returns true if successful or false if the supplied string was invalid
or another error occured.
January 22, 2014 - Article published