This plug-in demonstrates a replacement of the standard MFC print preview window with an enhanced version that supports:

Multipage display : 1, 2, 3, 4, 6 or 9 pages at a time

Switching the page layout between Portrait and Landscape

Print setup available in preview mode

The mouse wheel will work for scrolling pages

Once again I will review how I went about designing the plug-in and implementing its features. Along the
way I will also cover problems encountered and how they were solved.

When I look at the plug-ins I can write, it seems to me that I will be able to take most of
my existing article series and convert each as I go. It has been a good starting point for upgrading
this library. If users of the class wish to see a specific plug-in created, they should visit my
blog page and post a suggesstion there.

With the V1.2 upgrade to the library, I was able to return true for all CView derived classes
so that we can replace their existing print preview functionality.

bool CEnhancedPrintPreview::IsPlugInFor(CRuntimeClass *pClass)
{
if (pClass->IsDerivedFrom(RUNTIME_CLASS(CView)))
{
// we are only a plug-in for any view class// as the view classes support the preview optionreturntrue;
}
returnfalse;
}

For the Pre call for the message we suppress the standard implementation of it using SuppressThisMessage(),
as we do not want multiple preview windows being displayed.

void CEnhancedPrintPreview::OnFilePrintPreview()
{
if (IsPreCall())
{
// were replacing the standard MFC print preview and replacing it with// our enhanced version.
SuppressThisMessage();
// replace the default print preview with ours!// In derived classes, implement special window handling here// Be sure to Unhook Frame Window close if hooked.// must not create this on the frame. Must outlive this function
CPrintPreviewState* pState = new CPrintPreviewState;
// DoPrintPreview's return value does not necessarily indicate that// Print preview succeeded or failed, but rather what actions are necessary// at this point. If DoPrintPreview returns TRUE, it means that// OnEndPrintPreview will be (or has already been) called and the// pState structure will be/has been deleted.// If DoPrintPreview returns FALSE, it means that OnEndPrintPreview// WILL NOT be called and that cleanup, including deleting pState// must be done here.
CView *pView = static_cast<CView*>(m_pPlugInFor);
if (!pView->DoPrintPreview(IDD_PREVIEW, pView,
RUNTIME_CLASS(CMultiPagePreviewView), pState))
{
// In derived classes, reverse special window handling here for// Preview failure case
TRACE0("Error: DoPrintPreview failed.\n");
AfxMessageBox(AFX_IDP_COMMAND_FAILURE);
delete pState; // preview failed to initialize, delete State now
pState = NULL;
}
}
}

Well, once I had this and the standard code from the enhanced print preview code merged sufficiently to get this far,
I ran some tests to see how well it worked.

And it failed very badly. :( In fact I could not immediately see why it was failing, so I placed judicious TRACE
statements throughout the code. One of the symptoms of the failure was I was getting to see the standard MFC print preview
dialog bar:

Now during the print preview initialisation, this standard dialog bar is being destroyed and replaced with our own, which
enabled the mouse wheel messages, so why is it being shown? This was the first clue to my problem. It seems that even though
I was trying to suppress the ID_FILE_PRINT_PREVIEW message in the standard MFC code, I was failing to, and two
preview views were being created, mine and MFCs. This was the main source of the problem. So how do I fix it?

Message suppression in the library

In the MFC extension library that handles the plug-ins, each plug-in had a bool m_bSuppressThisMessage variable
which when we needed to suppress the message was being set to true, so I checked the state of the flag in the
ProcessCommandMessageMaps() function after my plug-in message had just been processed.

And it was false!

After some thought it occurred to me that when we create the preview view and we go about displaying it, other messages may be being
processed by the library in the background, probably due to a message pump being done due to a SendMessage() call, and
as we only have a single flag for all passes through the library, any additional messages that do get processed before we return from
our plug-in function will reset the suppressed message flag. This is not what we want to happen.

Now that we have identified the flaw in the library we need to fix it.

Fixing the problem

Well the problem we have here is in the library, and not our plug-in, so I went back to the library source and decided that we needed
a stack of suppression flags, one gets added for every message being processed, and is removed when that message gets completed. This led
to some changes in the CPlugInMap class as follows:

Well this also made me think about a problem that was originally experienced by early users of the plug-in library, that is
that when WM_NCDESTROY messages were processed, some objects called delete this on themselves, causing
Post message handler to fail with a GPF when trying to access the deleted map objects. I did not want to re-introduce the
same problem, so had to also change the ProcessCommandMessageMaps() and ProcessWindowMessageMaps()
functions to check the CPIState object related to the plug-in object.

Once all this had been done, I rebuilt the project and the problem had been fixed.

This new version of the plug-in library is now V1.3 - It seems every time I create a new plug-in, I
discover another flaw in it. Hopefully there will be less and less of these in the future.

Making the preview view plug-in enabled

I also wanted the preview view to make use of any other plug-ins that may enhance its
appearance. One such is the owner-drawn menu plug-in.
So I added versions of the OnCmdMsg and OnWndMsg functions along with the additional
support varaibles:

This made the preview view plug-in enabled, so it makes use of the owner-drawn menu plug-in for the popup menu
used to select how many pages the user wishes to preview at a time. A toolbar resource, which is loaded by the
owner-drawn menu plug-in was added with the correct images.

So how did it look?

At this point I had all the code that changes the print orientation and printer disabled, so
what we had was this:

I Now needed to enable the printer selection and the paper orientation features.

In the original aritcle, I had a class which enumerates all the printers installed on the local system. Along
with this class, the user had to add some functions to their CWinApp derived class to allow the
page orientation and the printer to be changed. But we cannot do this in our plug-in. We have to find a different
method of doing this because we cannot modify the CWinApp or CPlugInApp derived classes.

In this version we had to call the relevant function of the CWinApp derived class as
we needed access to the CWinApp::m_hDevMode global handle, which manages the currently
selected printer. We need to find a different way of doing this.

After looking through the MSDN, I discovered that calling the function CWinApp::GetPrinterDeviceDefaults()
returns the required handles in a PRINTDLG structure. So we can extract the function from the CEnumPrinters
class and rewrite it as follows:

Well again we need to avoid using functions added to the CWinApp derived class so we need
to make use of existing MFC code. The most obvious of which was CWinApp::SelectPrinter(), but
after playing with this for a while, I could still only get GPFs!

So I went back to my original article and took a look at the comments added by users of the code, and found the
following entry by asconaga:

So, following his advice, I removed the printer combo box, and added a print setup button and mapped the relevant code.
And hey presto, it works without any problems.

Conclusion

Another article in the series and another flaw in the library exposed and killed, hopefully the last of nay present.

This plug-in demonstrates the replacement and enhancement of a standard MFC feature, again in a modular fashion, building
on my previous article series to produce a single MFC upgrade path.

I hope you have enjoyed reading (and using) this article.

References

Here are a list of related articles used in the making of this plug-in

Share

About the Author

A research and development programmer working for a pharmaceutical instrument company for the past 17 years.

I am one of those lucky people who enjoys his work and spends more time than he should either doing work or reseaching new stuff. I can also be found on playing DDO on the Cannith server (Send a tell to "Maetrim" who is my current main)

I am also a keep fit fanatic, doing cross country running and am seriously into [url]http://www.ryushinkan.co.uk/[/url] Karate at this time of my life, training from 4-6 times a week and recently achieved my 1st Dan after 6 years.