The following code segments are drawn from a much larger system that I manage at work. The intent is to show how to provide a graphical print screen capability in a PM program. We install a hook to watch for the print screen key and then take a bit map snapshot of the screen. This bit map is then sent to the printer. Forgive me if I don't go into all the details about the non-related data structures -- it's late and my mind is a bit foggy.
Feel free to use anything here. Please add some kind of acknowledgement, if you use them as is, like:
"Print routines (c) 1990, 1991 Applied Signal Technology, Inc." Comment,
questions, ridicule should be directed to:
Jeff Hitchcock
CompuServe 71601,260
or U.S. Mail to
Applied Signal Technology, Inc.
470 Spring Park Place, Suite 700
Herndon, VA 22070
or phone to
703/478-5619
/*****************************************************************************/
1. During the WM_CREATE message processing, add the following:
// Set the print screen hook WinSetHook (hab, HMQ_CURRENT, HK_INPUT, (PFN) PrintScreenHook, (HMODULE) NULL);
/*****************************************************************************/
2. Somewhere, have this function:
BOOL CALLBACK PrintScreenHook (HAB hab, PQMSG pQmsg, USHORT fs) { if ( pQmsg->msg == WM_CHAR ) if ( ( SHORT1FROMMP(pQmsg->mp1) & KC_KEYUP) && ( SHORT1FROMMP(pQmsg->mp1) & KC_VIRTUALKEY ) && ( SHORT2FROMMP(pQmsg->mp2) == VK_PRINTSCRN) ) PrintScreen (hab); return FALSE; }
/*****************************************************************************/
3. Here's the "driver:"
VOID EXPENTRY PrintScreen (HAB hab) { HBITMAP hbm; hbm = ScreenToBitmap (hab); PrintBitMap(hbm); }
/*****************************************************************************/
4. Here's a general print-related structure we use. We often have many print threads running concurrently, but we only allow one to "run" at a time. We use a semaphore to show availability of the printer (so to speak), and only one thread at a time gets it. If we didn't do this, and more than a few print threads are running (especially graphical prints), even a 486/33 with 16 MB of RAM begins to C-R-A-W-L. So, for what it's worth, these are the structures that we use:
/************************************************************** * * * PRINTTHREADPARAMS structure * * * * Parameters that are used to manage separate print threads * * * * Item Contents/Use * * -------------- ---------------------------------------- * * * * sJobNumber Print job number, used for cancelling * * aiThreadStack Thread's stack * * hwndNotify Window to which notif. msgs are sent * * tidPrint System task id * * hssmPrinter... Semaphore for printer available * * fSemSet TRUE if semaphore was made and cleared * * szSummary Print summary (e.g., fax printout) * * fStart Can't start until TRUE (default FALSE) * * fContinue Quit if FALSE (default is TRUE) * * fHold Hold if TRUE (default is FALSE) * * sStartingPage For multipage, start here * * sEndingPage For multipage, end here * * usParam Module-dependent USHORT * * ulParam Module-dependent ULONG * * pPrintData PVOID to the print data * * * * PAGESETUP structure * * * * Parameters used to describe the appearance * * * * Item Contents/Use * * -------------- ---------------------------------------- * * * * szFont The name of the font to use * * sLinesPerPage Used to scale font * * sCharsPerLine Used to scale font * * sLeft Used to position on page, in chars * * sRight Used to position on page, in char * * sTop Used to position on page, in lines * * sBottom Used to position on page, in lines * * szHeader Text to place on top of each page * * fIncludeSummary If TRUE, include SRI summary on page 1 * * fHeaderEveryPage TRUE for every page, false for pg 1 * * fHeaderUnderline TRUE for underline * * szFooter Text to place at bottom of each page * * fFooterEveryPage TRUE for every page, false for pg 1 * * fOverlineFooter TRUE for overline * * * * HEADER AND FOOTER OPTIONS: * * * * Special Flags that should be supported in each module: * * * * &l Left justify * * &c Center * * &r Right justify * * &d Date * * &t Time * * &p Page number * * * **************************************************************/ typedef struct { CHAR szFont[FACESIZE] ; SHORT sLinesPerPage ; SHORT sCharsPerLine ; SHORT sLeft ; SHORT sRight ; SHORT sTop ; SHORT sBottom ; BOOL fIncludeSummary ; CHAR szHeader[HEADERFOOTERLENGTH] ; BOOL fHeaderEveryPage ; BOOL fUnderlineHeader ; CHAR szFooter[HEADERFOOTERLENGTH] ; BOOL fFooterEveryPage ; BOOL fOverlineFooter ; } PAGESETUP ; typedef PAGESETUP FAR *PPAGESETUP ; typedef struct { SHORT sJobNumber ; int aiThreadStack[STACKSIZE / sizeof (int)] ; HWND hwndNotify ; HSYSSEM hssmPrinterAvailable ; BOOL fSemSet ; CHAR szSummary[HEADERFOOTERLENGTH] ; BOOL fStart ; BOOL fRunning ; BOOL fContinue ; BOOL fHold ; SHORT sStartingPage ; SHORT sEndingPage ; PAGESETUP page ; USHORT usParam ; ULONG ulParam ; VOID huge *pPrintData ; } PRINTTHREADPARAMS ; typedef PRINTTHREADPARAMS FAR *PPRINTTHREADPARAMS ;
/*****************************************************************************/
5. This function saves the screen display to a bitmap.
HBITMAP ScreenToBitmap (HAB hab) { BITMAPINFOHEADER bmp ; HBITMAP hbm ; HDC hdcMemory ; HPS hpsScreen, hpsMemory ; LONG alBitmapFormats [2] ; POINTL aptl[3] ; SIZEL sizl ; SHORT cxScreen; SHORT cyScreen; BOOL fMonochrome = FALSE; // Create memory DC and PS cxScreen = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN); cyScreen = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN); hdcMemory = DevOpenDC (hab, OD_MEMORY, "*", 0L, NULL, NULL) ; sizl.cx = sizl.cy = 0 ; hpsMemory = GpiCreatePS (hab, hdcMemory, &sizl, PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC) ; // Create bitmap for destination bmp.cbFix = sizeof bmp ; if (fMonochrome) { bmp.cPlanes = 1 ; bmp.cBitCount = 1 ; } else { GpiQueryDeviceBitmapFormats (hpsMemory, 2L, alBitmapFormats) ; bmp.cPlanes = (USHORT) alBitmapFormats[0] ; bmp.cBitCount = (USHORT) alBitmapFormats[1] ; } bmp.cx = cxScreen ; bmp.cy = cyScreen ; hbm = GpiCreateBitmap (hpsMemory, &bmp, 0L, NULL, NULL) ; // Copy from screen to bitmap if (hbm != NULL) { GpiSetBitmap (hpsMemory, hbm) ; hpsScreen = WinGetScreenPS (HWND_DESKTOP) ; aptl[0].x = 0 ; aptl[0].y = 0 ; aptl[1].x = cxScreen ; aptl[1].y = cyScreen ; aptl[2].x = 0 ; aptl[2].y = 0 ; WinLockVisRegions (HWND_DESKTOP, TRUE) ; GpiBitBlt (hpsMemory, hpsScreen, 3L, aptl, fMonochrome ? ROP_NOTSRCCOPY : ROP_SRCCOPY, BBO_IGNORE) ; WinLockVisRegions (HWND_DESKTOP, FALSE) ; WinReleasePS (hpsScreen) ; GpiDestroyPS (hpsMemory) ; DevCloseDC (hdcMemory) ; } return hbm ; }
/*****************************************************************************
6. The "core" function:
This function prints a bitmap to the printer. The bitmap is scaled according to the size of the printer. No distortion is allowed of the bitmap image.
Returns False : if an error occurrs
Returns True : no Error occurred
Known bug(s):
Areas on the screen that have a black foreground and a gray background are completely black when printed. For example, when a window does not have the focus, it's title bar becomes black lettering on a gray background. When this window is printed, the entire title bar is black and no title can be read. This is using the Hewlett Packard LaserJet Series II printer.
According to MicroSoft online help this is a known bug with the printer device driver. To fix the bug you must go to the control panel and change the colors of the inactive window.
************************************************************************/ SHORT sBitmapToPrinter(PPRINTTHREADPARAMS pptp, HPS hpsPrinter, HDC hdcPrinter, HAB habPrinter, SIZEL *psizlPage, SIZEL *psizlChar) { HDC hdcPrinterMemory; HPS hpsPrinterMemory; POINTL ptl; SHORT sPage = 1; RECTL rcl; // Coordinates of region long lCapsHRes; long lCapsVRes; float fYAspectRatio; float fXAspectRatio; SIZEL sizl; HBITMAP hbm; POINTL aptl [4] ; SHORT cxScreen; SHORT cyScreen; float fltScale; // Skip down top margin, ... ptl.x = pptp->page.sLeft * psizlChar->cx ; ptl.y = psizlPage->cy - (pptp->page.sTop * psizlChar->cy) ; // Print header, if requested if (pptp->page.szHeader[0] != '\0') { PrintHeaderFooter (hpsPrinter, &ptl, pptp, psizlPage, psizlChar, pptp->page.szHeader, sPage, PRINT_HEADER) ; } hbm = pptp->pPrintData; // Find the aspect ratio of the printer DevQueryCaps(hdcPrinter,CAPS_HORIZONTAL_RESOLUTION,1L,&lCapsHRes); DevQueryCaps(hdcPrinter,CAPS_VERTICAL_RESOLUTION,1L,&lCapsVRes); if ( (lCapsVRes == 0) || (lCapsHRes == 0) ) { fXAspectRatio = (float) 1; fYAspectRatio = (float) 1; } else { fXAspectRatio = (float) ((float) lCapsVRes / (float) lCapsHRes); fYAspectRatio = (float) ((float) lCapsHRes / (float) lCapsVRes); } // determine coordinates to print on printer rcl.xLeft = pptp->page.sLeft * psizlChar->cx; // Printer left rcl.xRight = psizlPage->cx - (pptp->page.sRight * psizlChar->cx); // Printer right rcl.yBottom = (pptp->page.sBottom + 1) * psizlChar->cy; // Printer bottom rcl.yTop = psizlPage->cy - ( (pptp->page.sTop + 1) * psizlChar->cy); // Printer top cxScreen = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXSCREEN); cyScreen = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CYSCREEN); ScaleToWindowSize ((SHORT) (rcl.xRight - rcl.xLeft), // sXtarget (SHORT) (rcl.yTop - rcl.yBottom), // sYTarget cxScreen, // sXSource cyScreen, // sYSource &fltScale) ; // Create a memory device context // Memory device contexts are used to contain bitmaps hdcPrinterMemory = DevOpenDC (habPrinter, OD_MEMORY, "*", 0L, NULL, hdcPrinter); if ( hdcPrinterMemory == DEV_ERROR ) return FALSE; sizl.cx = 0; sizl.cy = 0; // Create a presentation space and associate it the memory device context hpsPrinterMemory = GpiCreatePS (habPrinter, hdcPrinterMemory, &sizl, PU_PELS | GPIF_DEFAULT | GPIT_NORMAL | GPIA_ASSOC); if( ! hpsPrinterMemory) { DevCloseDC (hdcPrinterMemory); return FALSE; } GpiSetBitmap(hpsPrinterMemory,hbm); aptl [0].x = rcl.xRight - (long) ((float) cxScreen * fltScale); aptl [0].y = rcl.yTop - (long) ((float) cyScreen * fltScale * fYAspectRatio); aptl [1].x = rcl.xRight; aptl [1].y = rcl.yTop; aptl [2].x = 0; aptl [2].y = 0; aptl [3].x = cxScreen; aptl [3].y = cyScreen; GpiBitBlt(hpsPrinter,hpsPrinterMemory,4L,aptl,ROP_SRCCOPY,BBO_IGNORE); GpiAssociate (hpsPrinterMemory, NULL) ; GpiDestroyPS (hpsPrinterMemory); DevCloseDC (hdcPrinterMemory); // If a footer is defined, ... if (pptp->page.szFooter[0] != '\0') { // ... compute its position ... ptl.x = pptp->page.sLeft * psizlChar->cx ; ptl.y = pptp->page.sBottom * psizlChar->cy ; // ... and print it. PrintHeaderFooter (hpsPrinter, &ptl, pptp, psizlPage, psizlChar, pptp->page.szFooter, sPage, PRINT_FOOTER) ; } return( TRUE); }