PRINT.C
//******************************************************************** 
// 
//  print.c 
// 
//  Source file for Device-Independent Bitmap (DIB) API.  Provides 
//  the following functions: 
// 
//  PrintWindow()       - Prints all or part of a window 
//  PrintScreen()       - Prints the entire screen 
//  PrintDIB()          - Prints the specified DIB 
// 
// Written by Microsoft Product Support Services, Developer Support. 
// Copyright 1991-1998 Microsoft Corporation. All rights reserved. 
//******************************************************************** 
 
#define     STRICT      // enable strict type checking 
 
#include <windows.h> 
#include <string.h> 
#include "dibdll.h"     // Header for printing dialog & DLL instance handle 
#include "dibapi.h"     // Header for DIB functions 
#include "dibutil.h"    // Auxiliary functions 
 
 
extern HANDLE   ghDLLInst;      // Global handle to DLL's instance 
 
/*************************************************************** 
 * Typedefs 
 **************************************************************/ 
 
// Structure used for Banding 
 
typedef struct 
{ 
   BOOL bGraphics; 
   BOOL bText; 
   RECT GraphicsRect; 
} BANDINFOSTRUCT; 
 
 
/**************************************************************** 
 * Variables 
 ***************************************************************/ 
 
HWND hDlgAbort;                    // Handle to Abort Dialog 
char szPrintDlg[] = "PRINTING";    // Name of Print dialog from .RC 
BOOL bAbort = FALSE;               // Abort a print operation? 
char gszDevice[50];                // Keeps track out device (e.g. "HP LaserJet") 
char gszOutput[50];                // Output device (e.g. "LPT1:") 
 
/*************************************************************** 
 * Function prototypes for functions local to this module 
 **************************************************************/ 
 
BOOL CALLBACK PrintAbortProc(HDC, int); 
int CALLBACK PrintAbortDlg(HWND, UINT, WPARAM, LPARAM); 
WORD PrintBand(HDC, LPRECT, LPRECT, BOOL, BOOL, LPBITMAPINFOHEADER, LPSTR); 
HDC GetPrinterDC(void); 
void CalculatePrintRect(HDC, LPRECT, WORD, DWORD, DWORD); 
 
 
/********************************************************************** 
 * 
 * PrintWindow() 
 * 
 * 
 * Description: 
 * 
 * This function prints the specified window on the default 
 * printer. 
 * 
 * Parameters: 
 * 
 * HWND hWnd       - Specifies the window to print.  The window must 
 *                   not be iconic and must be topmost on the display. 
 * 
 * WORD fPrintArea - Specifies the area of the window to print.  Must be 
 *                   one of PW_ALL, PW_CLIENT, PW_CAPTION,  or PW_MENUBAR 
 * 
 * WORD fPrintOpt  - Print options (one of PW_BESTFIT, PW_STRETCHTOPAGE, or 
 *                   PW_SCALE) 
 * 
 * WORD wXScale, wYScale - X and Y scaling factors if PW_SCALE is specified 
 * 
 * LPSTR szJobName - Name that you would like to give to this print job (this 
 *                   name shows up in the Print Manager as well as the 
 *                   "Now Printing..." dialog box). 
 * Return Value: 
 *      ERR_DIBFUNCTION or any return value from PrintDIB 
 * 
 **********************************************************************/ 
 
 
WORD PrintWindow(HWND hWnd, WORD fPrintArea, WORD fPrintOpt, WORD wXScale, 
        WORD wYScale, LPSTR szJobName) 
{ 
    HDIB    hDib;          // Handle to the DIB 
    WORD    wReturn;       // our return value 
 
    // Parameter validation 
 
    if (!hWnd) 
        return ERR_INVALIDHANDLE;  // Invalid Window 
 
    // Copy the Window to a DIB and print it. 
 
    hDib = CopyWindowToDIB(hWnd, fPrintArea); 
    if (!hDib) 
        return ERR_DIBFUNCTION; // CopyWindowToDIB failed! 
 
    wReturn = PrintDIB(hDib, fPrintOpt, wXScale, wYScale, szJobName); 
 
    // Call DestroyDIB to free the memory the dib takes up. 
 
    DestroyDIB(hDib); 
    return wReturn;   // return the value from PrintDIB 
} 
 
 
/********************************************************************** 
 * 
 * PrintScreen() 
 * 
 * 
 * Description: 
 * 
 * This function prints the specified portion of the display screen on the 
 * default printer using the print options specified.  The print 
 * options are listed in dibapi.h. 
 * 
 * Parameters: 
 * 
 * LPRECT rRegion  - Specifies the region of the screen (in screen 
 *                   coordinates) to print 
 * 
 * WORD fPrintOpt  - Print options  (PW_BESTFIT, PW_STRETCHTOPAGE, or PW_SCALE) 
 * 
 * WORD wXScale, wYScale - X and Y scaling factors if PW_SCALE is specified 
 * 
 * LPSTR szJobName - Name that you would like to give to this print job (this 
 *                   name shows up in the Print Manager as well as the 
 *                   "Now Printing..." dialog box). 
 * 
 * Return Value: 
 *      ERR_DIBFUNCTION or any return value from PrintDIB 
 * 
 **********************************************************************/ 
 
WORD PrintScreen(LPRECT rRegion, WORD fPrintOpt, WORD wXScale, WORD wYScale, 
        LPSTR szJobName) 
{ 
    HDIB     hDib;          // A Handle to our DIB 
    WORD     wReturn;       // Return value 
 
    // Copy the screen contained in the specified rectangle to a DIB 
 
    hDib = CopyScreenToDIB(rRegion); 
 
    if (!hDib) 
        return ERR_DIBFUNCTION;   // CopyScreenToDIB failed! 
 
    wReturn = PrintDIB(hDib, fPrintOpt, wXScale, wYScale, szJobName); 
    DestroyDIB(hDib); 
    return wReturn; // Return the value that PrintDIB returned 
} 
 
 
/********************************************************************** 
 * 
 * PrintDIB() 
 * 
 * Description: 
 * 
 * This routine prints the specified DIB.  The actual printing is done 
 * in the PrintBand() routine (see below), this procedure drives the 
 * printing operation.  PrintDIB() has the code to handle both banding 
 * and non-banding printers.  A banding printer can be distinguished by 
 * the GetDeviceCaps() API (see the code below).  On banding devices, 
 * must repeatedly call the NEXTBAND escape to get the next banding 
 * rectangle to print into.  If the device supports the BANDINFO escape, 
 * it should be used to determine whether the band "wants" text or 
 * graphics (or both).  On non-banding devices, we can ignore all this 
 * and call PrintBand() on the entire page. 
 * 
 * Parameters: 
 * 
 * HDIB hDib       - Handle to dib to be printed 
 * 
 * WORD fPrintOpt  - tells which print option to use (PW_BESTFIT, 
 *                   PW_STRETCHTOPAGE, OR PW_SCALE) 
 * 
 * WORD wXScale, wYScale - X and Y scaling factors (integers) for 
 *                   printed output if the PW_SCALE option is used. 
 * 
 * LPSTR szJobName - Name that you would like to give to this print job (this 
 *                   name shows up in the Print Manager as well as the 
 *                   "Now Printing..." dialog box). 
 * 
 * Return Value:  (see errors.h for description) 
 * 
 * One of: ERR_INVALIDHANDLE 
 *         ERR_LOCK 
 *         ERR_SETABORTPROC 
 *         ERR_STARTDOC 
 *         ERR_NEWFRAME 
 *         ERR_ENDDOC 
 *         ERR_GETDC 
 *         ERR_STRETCHDIBITS 
 * 
 ********************************************************************/ 
 
WORD PrintDIB(HDIB hDib, WORD fPrintOpt, WORD wXScale, WORD wYScale, 
        LPSTR szJobName) 
{ 
    HDC                 hPrnDC;         // DC to the printer 
    RECT                rect;           // Rect structure used for banding 
    LPSTR               lpBits;         // pointer to the DIB bits 
    LPBITMAPINFOHEADER  lpDIBHdr;       // Pointer to DIB header 
    int                 nBandCount = 0; // used for print dialog box to count bands 
    WORD                wErrorCode = 0; // Error code to return 
    RECT                rPrintRect;     // specifies the area on the printer 
                                        // (in printer coordinates) which we 
                                        // want the DIB to go to 
    char                szBuffer[70];   // Buffer to hold message for "Printing" dlg box 
    char                szJobNameTrunc[35];     // szJobName truncated to 31 
                                        // characters, since STARTDOC can't 
                                        // accept a string longer than 31 
    DOCINFO             DocInfo;        // structure for StartDoc 
    int                 nTemp;          // used to check banding capability 
    CHAR                lpBuffer[128];  // Buffer for strings retrieved from resources 
 
 
    // Paramter validation 
 
    if (!hDib) 
        return ERR_INVALIDHANDLE; 
 
    // Get pointer to DIB header 
 
    lpDIBHdr = (LPBITMAPINFOHEADER)GlobalLock(hDib); 
    if (!lpDIBHdr) // Check that we have a valid pointer 
        return ERR_LOCK; 
    lpBits = FindDIBBits((LPSTR)lpDIBHdr); // Find pointer to DIB bits 
 
    if (hPrnDC = GetPrinterDC()) 
    { 
        SetStretchBltMode(hPrnDC, COLORONCOLOR); 
 
        // Determine rPrintRect (printer area to print to) from the 
        // fPrintOpt.  Fill in rPrintRect.left and .top from wXScale and 
        // wYScale just in case we use PW_SCALE (see the function 
        // CalculatePrintRect). 
 
        rPrintRect.left = wXScale; 
        rPrintRect.top = wYScale; 
        CalculatePrintRect(hPrnDC, &rPrintRect, fPrintOpt, lpDIBHdr->biWidth, 
                         lpDIBHdr->biHeight); 
 
        // Initialize the abort procedure. 
 
        hDlgAbort = CreateDialog(ghDLLInst, szPrintDlg, GetFocus(), 
                (DLGPROC)PrintAbortDlg); 
 
        // ISet the text inside the dialog to the name of our print job 
 
        lstrcpy(szJobNameTrunc, szJobName); 
        szJobNameTrunc[31] = '\0';           // Truncate string to 31 chars 
        LoadString(ghDLLInst, IDS_PRINTMSG, lpBuffer, sizeof(lpBuffer)); 
        wsprintf(szBuffer, lpBuffer, (LPSTR)szJobNameTrunc); 
        SetDlgItemText(hDlgAbort, IDC_PRINTTEXT1, (LPSTR)szBuffer); 
 
        // Set global variable bAbort to FALSE.  This will get set to TRUE 
        // in our PrintAbortDlg() procedure if the user selects the 
        // CANCEL button in our dialog box 
 
        bAbort = FALSE; 
 
        // set up the Abort Procedure 
 
        if (SetAbortProc(hPrnDC, (ABORTPROC)PrintAbortProc) < 0) 
                return ERR_SETABORTPROC; 
 
        // start print job 
 
        ZeroMemory(&DocInfo, sizeof(DOCINFO)); 
        DocInfo.cbSize = sizeof(DOCINFO); 
        DocInfo.lpszDocName = (LPTSTR)szJobNameTrunc; 
        DocInfo.lpszOutput = NULL; 
 
        if (StartDoc(hPrnDC, &DocInfo) <= 0) 
        { 
            // Oops, something happened, let's clean up here and return 
 
             DestroyWindow(hDlgAbort);   // Remove abort dialog box 
             DeleteDC(hPrnDC); 
             GlobalUnlock(hDib); 
             return ERR_STARTDOC; 
        } 
 
// Note: the following banding code applies to Windows 3.1.  With the new 
//       printing architecture of Win32, send out both the graphics and 
//       text in one band (like a non-banding device).  This code is used 
//       for Win32s since Win32s depends on Windows 3.1 printing architecture. 
// 
        // Check if need to do banding.  If we do, loop through 
        // each band in the page, calling NEXTBAND and BANDINFO 
        // (if supported) calling PrintBand() on the band.  Else, 
        // call PrintBand() with the entire page as our clipping 
        // rectangle! 
 
        // If Wincap32 is running on Win32s, then use banding 
 
        nTemp = NEXTBAND; 
        if (Escape(hPrnDC, QUERYESCSUPPORT, sizeof(int), (LPSTR)&nTemp, NULL) && 
                (GetVersion() & 0x80000000) && (LOWORD(GetVersion()) == 3)) 
        { 
            BOOL                bBandInfoDevice; 
            BANDINFOSTRUCT      biBandInfo;         // Used for banding 
 
            // Fill in initial values for our BandInfo Structure to 
            // tell driver we can want to do graphics and text, and 
            // also which area we want the graphics to go in. 
 
            biBandInfo.bGraphics = TRUE; 
            biBandInfo.bText = TRUE; 
            biBandInfo.GraphicsRect = rPrintRect; 
 
            // Check if device supports the BANDINFO escape. 
 
            nTemp = BANDINFO; 
            bBandInfoDevice = Escape(hPrnDC, QUERYESCSUPPORT, sizeof(int), 
                    (LPSTR)&nTemp, NULL); 
 
            // Do each band -- Call Escape() with NEXTBAND, then the 
            // rect structure returned is the area where we are to 
            // print in.  This loop exits when the rect area is empty. 
 
            while (Escape(hPrnDC, NEXTBAND, 0, NULL, (LPSTR)&rect) && ! 
                IsRectEmpty(&rect)) 
            { 
                char szTmpBuf[100]; 
 
 
                // Do the BANDINFO, if needed. 
 
                if (bBandInfoDevice) 
                    Escape(hPrnDC, BANDINFO, sizeof(BANDINFOSTRUCT), (LPSTR)& 
                            biBandInfo, (LPSTR)&biBandInfo); 
                LoadString(ghDLLInst, IDS_BANDNMBR, lpBuffer, sizeof(lpBuffer)); 
                wsprintf(szTmpBuf, lpBuffer, ++nBandCount); 
                SetDlgItemText(hDlgAbort, IDC_PERCENTAGE, (LPSTR)szTmpBuf); 
 
                // Call PrintBand() to do actual output into band. 
                // Pass in our band-info flags to tell what sort 
                // of data to output into the band.  Note that on 
                // non-banding devices, we pass in the default bandinfo 
                // stuff set above (i.e. bText=TRUE, bGraphics=TRUE). 
 
                wErrorCode = PrintBand(hPrnDC, &rPrintRect, &rect, 
                        biBandInfo.bText, biBandInfo.bGraphics, lpDIBHdr, 
                        lpBits); 
            } 
        } 
        else 
        { 
            // Print the whole page -- non-banding device. 
 
            if (StartPage(hPrnDC) <= 0) 
                return ERR_STARTPAGE; 
 
            rect = rPrintRect; 
            LoadString(ghDLLInst, IDS_SENDINGBAND, lpBuffer, sizeof(lpBuffer)); 
            SetDlgItemText(hDlgAbort, IDC_PERCENTAGE, lpBuffer); 
            wErrorCode = PrintBand(hPrnDC, &rPrintRect, &rect, TRUE, TRUE, 
                    lpDIBHdr, lpBits); 
 
            // Non-banding devices need a NEWFRAME 
 
            if (EndPage(hPrnDC) <= 0) 
                return ERR_ENDPAGE; 
        } 
 
 
        // End the print operation.  Only send the ENDDOC if 
        //  we didn't abort or error. 
 
        if (!bAbort) 
        { 
            // We errored out on ENDDOC, but don't return here - we still 
            // need to close the dialog box, free proc instances, etc. 
 
            if (EndDoc(hPrnDC) <= 0) 
                wErrorCode = ERR_ENDDOC; 
 
            DestroyWindow(hDlgAbort); 
        } 
 
        // All done, clean up. 
 
        DeleteDC(hPrnDC); 
    } 
    else 
        wErrorCode = ERR_GETDC;   // Couldn't get Printer DC! 
 
    GlobalUnlock(hDib); 
    return wErrorCode; 
} 
 
 
 
 
// ******************************************************************* 
// Auxiliary Functions 
//     -- Local to this module only 
// ******************************************************************* 
 
 
/********************************************************************* 
 * 
 * CalculatePrintRect() 
 * 
 * Given fPrintOpt and a size of the DIB, return the area on the 
 * printer where the image should go (in printer coordinates).  If 
 * fPrintOpt is PW_SCALE, then lpPrintRect.left and .top should 
 * contain WORDs which specify the scaling factor for the X and 
 * Y directions, respecively. 
 * 
 ********************************************************************/ 
 
void CalculatePrintRect(HDC hDC, LPRECT lpPrintRect, WORD fPrintOpt, 
        DWORD cxDIB, DWORD cyDIB) 
{ 
    int  cxPage, cyPage, cxInch, cyInch; 
 
    if (!hDC) 
        return; 
 
    // Get some info from printer driver 
 
    cxPage = GetDeviceCaps(hDC, HORZRES);   // Width of printr page - pixels 
    cyPage = GetDeviceCaps(hDC, VERTRES);   // Height of printr page - pixels 
    cxInch = GetDeviceCaps(hDC, LOGPIXELSX);// Printer pixels per inch - X 
    cyInch = GetDeviceCaps(hDC, LOGPIXELSY);// Printer pixels per inch - Y 
 
    switch (fPrintOpt) 
    { 
 
        // Best Fit case -- create a rectangle which preserves 
        // the DIB's aspect ratio, and fills the page horizontally. 
 
        // The formula in the "->bottom" field below calculates the Y 
        // position of the printed bitmap, based on the size of the 
        // bitmap, the width of the page, and the relative size of 
        // a printed pixel (cyInch / cxInch). 
 
        case PW_BESTFIT: 
            lpPrintRect->top = 0; 
            lpPrintRect->left = 0; 
            lpPrintRect->bottom = (int)(((double)cyDIB * cxPage * cyInch) / 
                    ((double)cxDIB * cxInch)); 
            lpPrintRect->right = cxPage; 
            break; 
 
        // Scaling option -- lpPrintRect's top/left contain 
        // multipliers to multiply the DIB's height/width by. 
 
        case PW_SCALE: 
        { 
            int     cxMult, cyMult; 
 
            cxMult = lpPrintRect->left; 
            cyMult = lpPrintRect->top; 
            lpPrintRect->top = 0; 
            lpPrintRect->left = 0; 
            lpPrintRect->bottom = (int)(cyDIB * cyMult); 
            lpPrintRect->right = (int)(cxDIB * cxMult); 
            break; 
        } 
 
        // Stretch To Page case -- create a rectangle 
        // which covers the entire printing page (note that this 
        // is also the default). 
 
        case PW_STRETCHTOPAGE: 
 
        default: 
            lpPrintRect->top = 0; 
            lpPrintRect->left = 0; 
            lpPrintRect->bottom = cyPage; 
            lpPrintRect->right = cxPage; 
            break; 
    } 
} 
 
 
/********************************************************************* 
 * 
 * PrintBand() 
 * 
 * This routine does ALL output to the printer.  It is called from 
 * the PrintDIB() routine.  It is called for both banding and non- 
 * banding printing devices.  lpRectClip contains the rectangular 
 * area we should do our output into (i.e. we should clip our output 
 * to this area).  The flags fDoText and fDoGraphics should be set 
 * appropriately (if we want any text output to the rectangle, set 
 * fDoText to true).  Normally these flags are returned on banding 
 * devices which support the BANDINFO escape. 
 * 
 ********************************************************************/ 
 
WORD PrintBand(HDC hDC, LPRECT lpRectOut, LPRECT lpRectClip, BOOL fDoText, 
        BOOL fDoGraphics, LPBITMAPINFOHEADER lpDIBHdr, LPSTR lpDIBBits) 
{ 
    RECT    rect;           // Temporary rectangle 
    double  dblXScaling,    // X and Y scaling factors 
            dblYScaling; 
    WORD    wReturn = 0;    // Return code 
 
    if (fDoGraphics) 
    { 
        dblXScaling = ((double)lpRectOut->right - lpRectOut->left) / (double) 
                lpDIBHdr->biWidth; 
        dblYScaling = ((double)lpRectOut->bottom - lpRectOut->top) / (double) 
                lpDIBHdr->biHeight; 
 
        // Now we set up a temporary rectangle -- this rectangle 
        // holds the coordinates on the paper where our bitmap 
        // WILL be output.  We can intersect this rectangle with 
        // the lpClipRect to see what we NEED to output to this 
        // band.  Then, we determine the coordinates in the DIB 
        // to which this rectangle corresponds (using dbl?Scaling). 
 
        IntersectRect(&rect, lpRectOut, lpRectClip); 
        if (!IsRectEmpty(&rect)) 
        { 
            RECT    rectIn; 
 
            rectIn.left = (int)((rect.left - lpRectOut->left) / dblXScaling + 
                    0.5 
                       ); 
            rectIn.top = (int)((rect.top - lpRectOut->top) / dblYScaling + 0.5); 
            rectIn.right = (int)(rectIn.left + (rect.right - rect.left) / 
                    dblXScaling + 0.5); 
            rectIn.bottom = (int)(rectIn.top + (rect.bottom - rect.top) / 
                    dblYScaling + 0.5); 
            if (!StretchDIBits(hDC, rect.left, rect.top, 
                    rect.right - rect.left, rect.bottom - rect.top, 
                    rectIn.left, (int)(lpDIBHdr->biHeight) - 
                    rectIn.top - (rectIn.bottom - rectIn.top), 
                    rectIn.right - rectIn.left, rectIn.bottom - rectIn.top, 
                    lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS, 
                    SRCCOPY)) 
                wReturn = ERR_STRETCHDIBITS; // StretchDIBits() failed! 
        } 
    } 
 
    return wReturn; 
} 
 
 
/*********************************************************************** 
 * 
 * GetPrinterDC() 
 * 
 * Uses PrinDlg common dialog for printer selection and creates a dc. 
 * Returns NULL on error. 
 * 
 ***********************************************************************/ 
 
HDC GetPrinterDC() { 
 
    PRINTDLG pd; 
 
    ZeroMemory(&pd, sizeof(pd)); 
 
    pd.lStructSize = sizeof(PRINTDLG); 
    pd.Flags = PD_RETURNDC; 
 
    if (PrintDlg(&pd) == TRUE) 
    { 
        DEVNAMES    *pDevNames = GlobalLock(pd.hDevNames); 
 
        lstrcpy((LPSTR)gszDevice, 
                (LPSTR)((char *)pDevNames+pDevNames->wDeviceOffset)); 
 
        if(!lstrcmpi((LPSTR)((char *)pDevNames+pDevNames->wDeviceOffset), 
                (LPSTR)((char *)pDevNames+pDevNames->wOutputOffset))) 
            lstrcpy((LPSTR)gszOutput, "net:"); 
        else 
            lstrcpy((LPSTR)gszOutput, 
                    (LPSTR)((char *)pDevNames+pDevNames->wOutputOffset)); 
 
        GlobalUnlock(pd.hDevNames); 
        return pd.hDC; 
    } 
 
    else 
        return NULL; 
} 
 
 
/********************************************************************** 
 * PrintAbortProc() 
 * 
 * Abort procedure - contains the message loop while printing is 
 * in progress.  By using a PeekMessage() loop, multitasking 
 * can occur during printing. 
 * 
 **********************************************************************/ 
 
BOOL CALLBACK PrintAbortProc(HDC hDC, int code) 
{ 
    MSG  msg; 
 
    while (!bAbort && PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) 
        if (!IsDialogMessage(hDlgAbort, &msg)) 
        { 
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        } 
 
    return !bAbort; 
} 
 
/*********************************************************************** 
 * 
 * PrintAbortDlg() 
 * 
 * 
 * This is the Dialog Procedure which will handle the "Now Printing" 
 * dialog box.  When the user presses the "Cancel" button, the 
 * global variable bAbort is set to TRUE, which causes the 
 * PrintAbortProc to exit, which in turn causes the printing 
 * operation to terminate. 
 * 
 ***********************************************************************/ 
 
int CALLBACK PrintAbortDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{ 
    switch (msg) 
    { 
        case WM_INITDIALOG: 
        { 
            char szBuffer[100]; 
            CHAR msgBuffer[128]; 
 
 
            // Fill in the text which specifies where this bitmap 
            // is going ("on HP LaserJet on LPT1", for example) 
            LoadString(ghDLLInst, IDS_ABORTSTRING, msgBuffer, sizeof(msgBuffer)); 
            wsprintf(szBuffer, msgBuffer, (LPSTR)gszDevice, 
                    (LPSTR)gszOutput); 
            SetDlgItemText(hWnd, IDC_PRINTTEXT2, (LPSTR)szBuffer); 
            SetFocus(GetDlgItem(hWnd, IDCANCEL)); 
            return TRUE;     // Return TRUE because we called SetFocus() 
        } 
 
        case WM_COMMAND: 
            bAbort = TRUE; 
            DestroyWindow(hWnd); 
            return TRUE; 
            break; 
    } 
 
    return FALSE; 
}