Let's quickly review the mechanics of the abort procedure. You define an abort procedure that looks like this:
BOOL FAR PASCAL AbortProc (HDC hdcPrn, short nCode)
{
MSG msg ;
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return TRUE ;
}
You list AbortProc in the EXPORTS section of your module definition file. You obtain a pointer to the function using MakeProcInstance:
lpfnAbortProc = MakeProcInstance (AbortProc, hInstance) ;
When you want to print something, you give Windows this pointer with an Escape call:
Escape (hdcPrn, SETABORTPROC, 0, (LPSTR) lpfnAbortProc, NULL) ;
You make this Escape call before the Escape call for STARTDOC. And that's it.
Well, not quite. We've overlooked a problem with that PeekMessage loop in AbortProc—a big problem. AbortProc is called only while your program is in the midst of printing. Some very ugly things can happen if you retrieve a message in AbortProc and dispatch it to your own window procedure. A user could select Print from the menu again. But the program is already in the middle of the printing routine. A user could load a new file into the program while the program is trying to print the previous file. A user could even quit your program! If that happens, all your program's windows will be destroyed. You'll eventually return from the printing routine, but you'll have nowhere to go except to a window procedure that's no longer valid.
This stuff boggles the mind. Your program isn't prepared for it. For this reason, when you set an abort procedure, you should first disable your program's window so that it can't receive keyboard and mouse input. You do this with:
EnableWindow (hwnd, FALSE) ;
This prevents keyboard and mouse input from getting into the message queue. The user therefore can't do anything with your program during the time it's printing. When printing is finished, you reenable the window for input:
EnableWindow (hwnd, TRUE) ;
So why, you ask, do we even bother with the TranslateMessage and DispatchMessage calls in AbortProc when no keyboard or mouse messages will get into the message queue in the first place? It's true that the TranslateMessage call isn't strictly needed (although it's almost always included). But we must use DispatchMessage in case a WM_PAINT message gets in the message queue. If WM_PAINT isn't processed properly with a BeginPaint and EndPaint pair in the window procedure, the message will remain in the queue and clog up the works, because PeekMessage will never return a FALSE.
When you disable your window during the time you're printing, your program remains inert on the display. But a user can switch to another program and do some work there, and Print Manager can continue sending output files to the printer.
The PRINT2 program, shown in Figure 15-7, adds an abort procedure (and the necessary support) to the logic in PRINT1. More specifically, PRINT2 adds the abort procedure (including a listing in the EXPORTS section of PRINT2.DEF), a call to MakeProcInstance and Escape using the SETABORTPROC subfunction, a FreeProcInstance call at the end, and two calls to EnableWindow, the first to disable the window and the second to reenable it.
PRINT2.MAK
#----------------------
# PRINT2.MAK make file
#----------------------
print2.exe : print.obj print2.obj print2.def
link print2 print, /align:16, NUL, /nod slibcew libw, print2
rc print2.exe
print.obj : print.c
cl -c -Gsw -Ow -W2 -Zp print.c
print2.obj : print2.c
cl -c -Gsw -Ow -W2 -Zp print2.c
PRINT2.C
/*------------------------------------------
PRINT2.C -- Printing with Abort Function
(c) Charles Petzold, 1990
------------------------------------------*/
#include <windows.h>
HDC GetPrinterDC (void) ; // in PRINT.C
void PageGDICalls (HDC, short, short) ;
HANDLE hInst ;
char szAppName [] = "Print2" ;
char szCaption [] = "Print Program 2 (Abort Function)" ;
BOOL FAR PASCAL AbortProc (HDC hdcPrn, short nCode)
{
MSG msg ;
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return TRUE ;
}
BOOL PrintMyPage (HWND hwnd)
{
static char szMessage [] = "Print2: Printing" ;
BOOL bError = FALSE ;
FARPROC lpfnAbortProc ;
HDC hdcPrn ;
RECT rect ;
short xPage, yPage ;
if (NULL == (hdcPrn = GetPrinterDC ()))
return TRUE ;
xPage = GetDeviceCaps (hdcPrn, HORZRES) ;
yPage = GetDeviceCaps (hdcPrn, VERTRES) ;
EnableWindow (hwnd, FALSE) ;
lpfnAbortProc = MakeProcInstance (AbortProc, hInst) ;
Escape (hdcPrn, SETABORTPROC, 0, (LPSTR) lpfnAbortProc, NULL) ;
if (Escape (hdcPrn, STARTDOC, sizeof szMessage - 1, szMessage, NULL) > 0)
{
PageGDICalls (hdcPrn, xPage, yPage) ;
if (Escape (hdcPrn, NEWFRAME, 0, NULL, NULL) > 0)
Escape (hdcPrn, ENDDOC, 0, NULL, NULL) ;
else
bError = TRUE ;
}
else
bError = TRUE ;
if (!bError)
Escape (hdcPrn, ENDDOC, 0, NULL, NULL) ;
FreeProcInstance (lpfnAbortProc) ;
EnableWindow (hwnd, TRUE) ;
DeleteDC (hdcPrn) ;
return bError ;
}
PRINT2.DEF
;-----------------------------------
; PRINT2.DEF module definition file
;-----------------------------------
NAME PRINT2
DESCRIPTION 'Printing Program No. 2 (c) Charles Petzold, 1990'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
AbortProc