2-6
* BackPrnt.c
*
* Sample code for "Multithreading Applications in Win32"
* This is from Chapter 2, Listing 2-6
*
* Demonstrates background printing
*/
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include "resource.h"
#include "MtVerify.h"
//
// Macro definitions
//
#define WM_SHOWBITMAP WM_APP
#define MAX_PRINT_JOBS 64
//
// Structures
//
typedef struct
{ // Information passed to background thread for printing
HWND hDlg;
HWND hWndParent;
HDC hDc;
BOOL bPrint; // TRUE if printing;
char szText[256];
} ThreadPrintInfo;
//
// Global variables
//
HANDLE hInst;
HBITMAP gbmpDisplay;
RECT gDisplayRect;
int gNumPrinting = 0;
// Handle to each created thread
HANDLE gPrintJobs[64];
// Height of bitmap returned by DrawText
int iHeight;
// HWND of the dialog so other threads can find it.
HWND hDlgMain;
//
// Function declarations
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK MainWndProc(HWND hWnd, unsigned msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam);
void PrintDlg_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
void PrintDlg_OnPaint(HWND hwnd);
void PrintText(HWND hwndParent, char *pszText);
void PrintToDisplay(HWND hwndParent, char *pszText);
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
DWORD WINAPI BackgroundPrintThread(LPVOID pVoid);
///////////////////////////////////////////////////////////
//
// WinMain
//
// Main entry point of application. This will be a
// dialog based app, not a normal window, so this
// routine acts a little differently than "normal".
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
HWND hWnd;
WNDCLASS wc;
int index;
hInst = hInstance;
if (!hPrevInstance)
{
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = MainWndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, "GenIco");
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground= GetSysColorBrush(COLOR_BACKGROUND);
wc.lpszMenuName = "PRINTING_MENU";
wc.lpszClassName= "PrintDlgClass";
if (!RegisterClass(&wc))
return FALSE;
}
hWnd = CreateWindow(
"PrintDlgClass",
"Background Printing",
WS_OVERLAPPED|WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU,
CW_USEDEFAULT, // At this point we do not want to
0, // show the window until we know
0, // how big the Dialog Box is so
0, // that we can fit the main window
NULL, // around it.
NULL,
hInstance,
NULL);
hDlgMain = CreateDialog(hInst,
MAKEINTRESOURCE(IDD_PRINT),
hWnd, PrintDlgProc);
ShowWindow(hWnd, nCmdShow);
ShowWindow(hDlgMain, SW_SHOW);
while (GetMessage(&msg, NULL, 0, 0))
{ // Get Next message in queue
if(hDlgMain == NULL || !IsDialogMessage(hDlgMain,&msg))
{
TranslateMessage(&msg); /* Translate virtual key codes */
DispatchMessage(&msg); /* Dispatches message to window */
}
} // end while
// Wait for all threads to terminate. The Window will
// have already disappeared by this point.
for (index = 0; index < gNumPrinting; index++)
{
DWORD status;
do
{ // Wait for thread to terminate
GetExitCodeThread(gPrintJobs[index], &status);
Sleep(10);
} while (status == STILL_ACTIVE);
} // end for
return (msg.wParam); /* Returns the value from PostQuitMessage */
}
LRESULT CALLBACK MainWndProc(HWND hWnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
break;
case WM_COMMAND:
switch (wParam)
{
case IDM_ABOUT:
DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
case WM_SETFOCUS:
// ensure that the Dialog Box has the focus
SetFocus(hDlgMain);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hDlg);
hDlgMain = NULL;
break;
case WM_DESTROY:
return TRUE;
break;
case WM_SHOWBITMAP:
if (gbmpDisplay)
DeleteObject(gbmpDisplay);
gDisplayRect = *(RECT*)wParam;
gbmpDisplay = (HBITMAP) lParam;
InvalidateRect(hDlgMain, NULL, TRUE);
break;
HANDLE_MSG(hDlg, WM_INITDIALOG, PrintDlg_OnInitDialog);
HANDLE_MSG(hDlg, WM_COMMAND, PrintDlg_OnCommand);
HANDLE_MSG(hDlg, WM_PAINT, PrintDlg_OnPaint);
default:
return (FALSE);
}
return 0;
}
BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam)
{
RECT rect;
// Size parent to fit this dialog
GetWindowRect(hwndDlg, &rect);
SetWindowPos(GetParent(hwndDlg),NULL,
0,0,
rect.right-rect.left,
rect.bottom-rect.top+GetSystemMetrics(SM_CYMENU)
+GetSystemMetrics(SM_CYCAPTION),
SWP_NOMOVE | SWP_NOZORDER);
return TRUE;
}
void PrintDlg_OnCommand(HWND hDlg, int id,HWND hwndCtl, UINT codeNotify)
{
char szText[256];
switch (id)
{
case IDC_PRINT:
GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256);
PrintText(hDlg, szText);
break;
case IDC_DISPLAY:
GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256);
PrintToDisplay(hDlg, szText);
break;
case IDCANCEL:
case IDM_EXIT:
PostMessage(GetParent(hDlg),WM_DESTROY,
(WPARAM)0, (LPARAM)0);
DestroyWindow(hDlgMain);
hDlgMain = NULL;
break;
default:
break;
}
}
void PrintDlg_OnPaint( HWND hwnd )
{
PAINTSTRUCT paint;
HWND hwndCtrl;
HDC hdc;
HDC hDcMem;
HBITMAP bmpOld;
RECT rect;
POINT point;
if (!gbmpDisplay)
return;
hwndCtrl = GetDlgItem(hwnd, IDC_OUTPUT);
hdc = BeginPaint(hwnd, &paint);
GetWindowRect(hwndCtrl, &rect);
point = *((POINT *)&rect);
ScreenToClient(hwnd, &point);
hDcMem = CreateCompatibleDC(NULL);
bmpOld = SelectObject(hDcMem, gbmpDisplay);
// Copy bitmap to screen
MTVERIFY( BitBlt(hdc, point.x+10, point.y+40,
gDisplayRect.right-gDisplayRect.left, gDisplayRect.bottom-gDisplayRect.top,
hDcMem, iHeight, 0, SRCCOPY) );
SelectObject(hDcMem, bmpOld);
DeleteDC(hDcMem);
EndPaint(hwnd, &paint);
}
//
// Asks user which printer to use, then creates
// background printing thread.
//
void PrintText(HWND hwndParent, char *pszText)
{
ThreadPrintInfo *pInfo;
HANDLE hThread;
DWORD dwThreadId;
int result;
DOCINFO docInfo;
PRINTDLG dlgPrint;
// Put up Common Dialog for Printing and get hDC.
memset(&dlgPrint, 0, sizeof(PRINTDLG));
dlgPrint.lStructSize = sizeof(PRINTDLG);
dlgPrint.hwndOwner = hwndParent;
dlgPrint.Flags = PD_ALLPAGES | PD_USEDEVMODECOPIES
| PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
dlgPrint.hInstance = hInst;
if (!PrintDlg(&dlgPrint))
return;
// Initialize Printer device
docInfo.cbSize = sizeof(DOCINFO);
docInfo.lpszDocName = "Background Printing Example";
docInfo.lpszOutput = NULL;
docInfo.lpszDatatype = NULL;
docInfo.fwType = 0;
result = StartDoc(dlgPrint.hDC, &docInfo);
result = StartPage(dlgPrint.hDC);
pInfo = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(ThreadPrintInfo));
pInfo->hDlg = hwndParent;
pInfo->hWndParent = hwndParent;
pInfo->hDc = dlgPrint.hDC;
pInfo->bPrint = TRUE;
strcpy(pInfo->szText, pszText);
MTVERIFY( hThread = CreateThread(NULL, 0,
BackgroundPrintThread, (LPVOID)pInfo,
0, &dwThreadId ));
// keep track of all background printing threads
gPrintJobs[gNumPrinting++] = hThread;
}
//
// Shows output on the dialog box.
//
void PrintToDisplay(HWND hwndParent, char *pszText)
{
ThreadPrintInfo *pInfo;
DWORD dwThreadId;
HANDLE hThread;
pInfo = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(ThreadPrintInfo));
pInfo->hDlg = hwndParent;
pInfo->hWndParent = GetDlgItem(hwndParent, IDC_OUTPUT);
pInfo->hDc = GetDC(pInfo->hWndParent);
pInfo->bPrint = FALSE;
strcpy(pInfo->szText, pszText);
MTVERIFY( hThread = CreateThread(NULL, 0,
BackgroundPrintThread,
(LPVOID)pInfo,
0, &dwThreadId ));
// keep track of all background printing threads
gPrintJobs[gNumPrinting++] = hThread;
}
//---------------------------------------------------------
// About Box Handling
//---------------------------------------------------------
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_COMMAND:
if (LOWORD(wParam) == IDOK
|| LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, TRUE);
return (TRUE);
}
break;
default:
return (DefWindowProc(hDlg, message, wParam, lParam));
}
return FALSE;
}
//---------------------------------------------------------
// Background Printing Code
//---------------------------------------------------------
DWORD WINAPI BackgroundPrintThread(LPVOID pVoid)
{
ThreadPrintInfo *pInfo = (ThreadPrintInfo*) pVoid;
RECT rect;
RECT rectMem;
HDC hDcMem;
HBITMAP bmpMem;
HBITMAP bmpOld;
int x, y;
int counter = 0;
int nHeight;
HFONT hFont;
HFONT hFontOld;
// Get dimensions of paper into rect
rect.left = 0;
rect.top = 0;
rect.right = GetDeviceCaps(pInfo->hDc, HORZRES);
rect.bottom = GetDeviceCaps(pInfo->hDc, VERTRES);
nHeight = -MulDiv(36, GetDeviceCaps(pInfo->hDc, LOGPIXELSY), 72);
// Create Font
hFont = CreateFont(nHeight, 0,
0, 0, FW_DONTCARE,
FALSE, FALSE, FALSE,
ANSI_CHARSET,
OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS,
PROOF_QUALITY,
VARIABLE_PITCH,
NULL);
MTASSERT( hFont != 0);
// Draw into memory device context
hDcMem = CreateCompatibleDC(pInfo->hDc);
hFontOld = SelectObject(hDcMem, hFont);
iHeight = DrawText(hDcMem, pInfo->szText, -1, &rect, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
rectMem = rect;
rectMem.left = rect.left + iHeight;
rectMem.right = rect.right + (iHeight*2);
bmpMem = CreateCompatibleBitmap(hDcMem,
rectMem.right, rect.bottom);
bmpOld = SelectObject(hDcMem, bmpMem);
OffsetRect(&rect, iHeight, 0);
DrawText(hDcMem, pInfo->szText, -1, &rect,
DT_LEFT | DT_NOPREFIX | DT_WORDBREAK);
// Italicize bitmap. We use GetPixel and
// SetPixel because they are horribly inefficient,
// thereby causing the thread to run for awhile.
for (y = 0; y < iHeight; y++)
{ // Italicize line y
for (x = rectMem.right; x > iHeight; x--)
{ // Move specified pixel to the right.
COLORREF color;
int offset;
offset = y - iHeight;
color = GetPixel(hDcMem, x + offset, y);
if (color != 0)
counter++;
SetPixel(hDcMem, x, y, color);
} // end for x
} // end for y
MTASSERT( counter > 0);
// Copy bitmap of italicized text from memory to device
if (pInfo->bPrint)
{
BitBlt(pInfo->hDc, 50, 50, rectMem.right-rect.left, rectMem.bottom-rect.top,
hDcMem, iHeight, 0, SRCCOPY);
}
SelectObject(hDcMem, hFontOld);
SelectObject(hDcMem, bmpOld);
DeleteDC(hDcMem);
if (!pInfo->bPrint)
{
// We can't just write to the global variable where the
// bitmap is kept or we might overwrite the work of
// another thread, thereby "losing" a bitmap
// Also, if we used PostMessage instead of SendMessage, then
// the rectangle could have been deleted (it's on the stack)
// by the time the main message loop is reached.
SendMessage(pInfo->hDlg, WM_SHOWBITMAP, (WPARAM)&rectMem, (LPARAM) bmpMem);
}
if (pInfo->bPrint)
{ // Finish printing
int result;
result = EndPage(pInfo->hDc);
MTASSERT (result != SP_ERROR);
result = EndDoc(pInfo->hDc);
MTASSERT (result != SP_ERROR);
DeleteDC(pInfo->hDc);
// If we are printing, we are done with the bitmap.
DeleteObject(bmpMem);
}
else
{
ReleaseDC(pInfo->hWndParent, pInfo->hDc);
}
// free data structure passed in.
HeapFree(GetProcessHeap(), 0, pInfo);
return 0;
}
od
display
004014A0 /$ A1 20994000 mov eax, dword ptr [409920]
004014A5 |. 81EC 60020000 sub esp, 260
004014AB |. 85C0 test eax, eax
004014AD |. 55 push ebp
004014AE |. 56 push esi
004014AF |. 57 push edi
004014B0 |. 0F84 48010000 je 004015FE
004014B6 |. 8BBC24 700200>mov edi, dword ptr [esp+270]
004014BD |. 53 push ebx
004014BE |. 68 E9030000 push 3E9 ; /ControlID = 3E9 (1001.)
004014C3 |. 57 push edi ; |hWnd
004014C4 |. FF15 14614000 call dword ptr [<&USER32.GetDlgItem>] ; \GetDlgItem
004014CA |. 8BF0 mov esi, eax
004014CC |. 8D4424 2C lea eax, dword ptr [esp+2C]
004014D0 |. 50 push eax ; /pPaintstruct
004014D1 |. 57 push edi ; |hWnd
004014D2 |. FF15 18614000 call dword ptr [<&USER32.BeginPaint>] ; \BeginPaint
004014D8 |. 8D4C24 1C lea ecx, dword ptr [esp+1C]
004014DC |. 8BD8 mov ebx, eax
004014DE |. 51 push ecx ; /pRect
004014DF |. 56 push esi ; |hWnd
004014E0 |. FF15 34614000 call dword ptr [<&USER32.GetWindowRec>; \GetWindowRect
004014E6 |. 8B5424 1C mov edx, dword ptr [esp+1C]
004014EA |. 8B4424 20 mov eax, dword ptr [esp+20]
004014EE |. 8D4C24 14 lea ecx, dword ptr [esp+14]
004014F2 |. 895424 14 mov dword ptr [esp+14], edx
004014F6 |. 51 push ecx ; /pPoint
004014F7 |. 57 push edi ; |hWnd
004014F8 |. 894424 20 mov dword ptr [esp+20], eax ; |
004014FC |. FF15 1C614000 call dword ptr [<&USER32.ScreenToClie>; \ScreenToClient
00401502 |. 6A 00 push 0 ; /hDC = NULL
00401504 |. FF15 24604000 call dword ptr [<&GDI32.CreateCompati>; \CreateCompatibleDC
0040150A |. 8B15 20994000 mov edx, dword ptr [409920]
00401510 |. 8B2D 00604000 mov ebp, dword ptr [<&GDI32.SelectOb>; GDI32.SelectObject
00401516 |. 8BF0 mov esi, eax
00401518 |. 52 push edx ; /hObject => 7F050631
00401519 |. 56 push esi ; |hDC
0040151A |. FFD5 call ebp ; \SelectObject
0040151C |. 8B0D 1C984000 mov ecx, dword ptr [40981C]
00401522 |. 894424 10 mov dword ptr [esp+10], eax
00401526 |. A1 2C994000 mov eax, dword ptr [40992C]
0040152B |. 68 2000CC00 push 0CC0020 ; /ROP = SRCCOPY
00401530 |. 8B15 18984000 mov edx, dword ptr [409818] ; |
00401536 |. 6A 00 push 0 ; |YSrc = 0
00401538 |. 50 push eax ; |XSrc => 37 (55.)
00401539 |. A1 14984000 mov eax, dword ptr [409814] ; |
0040153E |. 2BC8 sub ecx, eax ; |
00401540 |. 8B4424 24 mov eax, dword ptr [esp+24] ; |
00401544 |. 56 push esi ; |hSrcDC
00401545 |. 51 push ecx ; |Height
00401546 |. 8B0D 10984000 mov ecx, dword ptr [409810] ; |
0040154C |. 83C0 28 add eax, 28 ; |
0040154F |. 2BD1 sub edx, ecx ; |
00401551 |. 8B4C24 28 mov ecx, dword ptr [esp+28] ; |
00401555 |. 52 push edx ; |Width
00401556 |. 83C1 0A add ecx, 0A ; |
00401559 |. 50 push eax ; |YDest
0040155A |. 51 push ecx ; |XDest
0040155B |. 53 push ebx ; |hDestDC
0040155C |. FF15 28604000 call dword ptr [<&GDI32.BitBlt>] ; \BitBlt
00401562 |. 85C0 test eax, eax
00401564 |. 5B pop ebx
00401565 |. 75 7C jnz short 004015E3
00401567 |. FF15 D8604000 call dword ptr [<&KERNEL32.GetLastErr>; [GetLastError
0040156D |. 6A 00 push 0 ; /Arguments = NULL
0040156F |. 8D5424 10 lea edx, dword ptr [esp+10] ; |
00401573 |. 6A 00 push 0 ; |BufSize = 0
00401575 |. 52 push edx ; |Buffer
00401576 |. 6A 00 push 0 ; |LanguageId = 0 (LANG_NEUTRAL)
00401578 |. 50 push eax ; |MessageId
00401579 |. 6A 00 push 0 ; |pSource = NULL
0040157B |. 68 00110000 push 1100 ; |Flags = ALLOCATE_BUFFER|FROM_SYSTEM|0
00401580 |. FF15 DC604000 call dword ptr [<&KERNEL32.FormatMess>; \FormatMessageA
00401586 |. 8B4424 0C mov eax, dword ptr [esp+C]
0040158A |. 8D4C24 68 lea ecx, dword ptr [esp+68]
0040158E |. 50 push eax ; /<%s>
0040158F |. 68 C8704000 push 004070C8 ; |<%s> = "BitBlt(hdc, point.x+10, point.y+40, gDisplayRect.right-gDisplayRect.left, gDisplayRect.bottom-gDisplayRect.top, hDcMem, iHeight, 0, SRCCOPY)"
00401594 |. 68 BC704000 push 004070BC ; |<%s> = "BackPrnt.c"
00401599 |. 68 25010000 push 125 ; |<%d> = 125 (293.)
0040159E |. 68 78704000 push 00407078 ; |Format = LF,"The following call failed at line %d in %s:",LF,LF," %s",LF,LF,"Reason: %s",LF,""
004015A3 |. 51 push ecx ; |s
004015A4 |. FF15 20614000 call dword ptr [<&USER32.wsprintfA>] ; \wsprintfA
004015AA |. 83C4 18 add esp, 18
004015AD |. 8D9424 680100>lea edx, dword ptr [esp+168]
004015B4 |. 68 04010000 push 104 ; /BufSize = 104 (260.)
004015B9 |. 52 push edx ; |PathBuffer
004015BA |. 6A 00 push 0 ; |hModule = NULL
004015BC |. FF15 E0604000 call dword ptr [<&KERNEL32.GetModuleF>; \GetModuleFileNameA
004015C2 |. 8D8424 680100>lea eax, dword ptr [esp+168]
004015C9 |. 68 30200100 push 12030 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL|10000
004015CE |. 8D4C24 6C lea ecx, dword ptr [esp+6C] ; |
004015D2 |. 50 push eax ; |Title
004015D3 |. 51 push ecx ; |Text
004015D4 |. 6A 00 push 0 ; |hOwner = NULL
004015D6 |. FF15 24614000 call dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
004015DC |. 6A 01 push 1
004015DE |. E8 A0050000 call 00401B83
004015E3 |> 8B5424 0C mov edx, dword ptr [esp+C]
004015E7 |. 52 push edx
004015E8 |. 56 push esi
004015E9 |. FFD5 call ebp
004015EB |. 56 push esi ; /hDC
004015EC |. FF15 2C604000 call dword ptr [<&GDI32.DeleteDC>] ; \DeleteDC
004015F2 |. 8D4424 28 lea eax, dword ptr [esp+28]
004015F6 |. 50 push eax ; /pPaintstruct
004015F7 |. 57 push edi ; |hWnd
004015F8 |. FF15 28614000 call dword ptr [<&USER32.EndPaint>] ; \EndPaint
004015FE |> 5F pop edi
004015FF |. 5E pop esi
00401600 |. 5D pop ebp
00401601 |. 81C4 60020000 add esp, 260
00401607 \. C3 retn