The Revised BLOWUP Program

The BLOWUP2 program is shown in Figure 16-1. You use it the same way as the BLOWUP1 program. First, click the mouse in BLOWUP1's client area. The cursor changes to a cross hair. Now place the cursor on one corner of the rectangle you want to capture, press the mouse button, drag the mouse to the opposite corner, and release the button.

BLOWUP2.MAK

#-----------------------

# BLOWUP2.MAK make file

#-----------------------

blowup2.exe : blowup2.obj blowup2.def

link blowup2, /align:16, NUL, /nod slibcew libw, blowup2

rc blowup2.exe

blowup2.obj : blowup2.c

cl -c -Gsw -Ow -W2 -Zp blowup2.c

BLOWUP2.C

/*------------------------------------------------

BLOWUP2.C -- Capture Screen Image to Clipboard

(c) Charles Petzold, 1990

------------------------------------------------*/

#include <windows.h>

#include <stdlib.h>

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,

LPSTR lpszCmdLine, int nCmdShow)

{

static char szAppName [] = "Blowup2" ;

HWND hwnd ;

MSG msg ;

WNDCLASS wndclass ;

if (!hPrevInstance)

{

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc = WndProc ;

wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = 0 ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = NULL ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName = NULL ;

wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass) ;

}

hwnd = CreateWindow (szAppName, szAppName,

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, nCmdShow) ;

UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}

void InvertBlock (HWND hwnd, POINT org, POINT len)

{

HDC hdc ;

hdc = CreateDC ("DISPLAY", NULL, NULL, NULL) ;

ClientToScreen (hwnd, &org) ;

PatBlt (hdc, org.x, org.y, len.x, len.y, DSTINVERT) ;

DeleteDC (hdc) ;

}

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static BOOL bCapturing, bBlocking ;

static POINT org, len ;

static short cxClient, cyClient ;

BITMAP bm ;

HDC hdc, hdcMem ;

HBITMAP hBitmap ;

PAINTSTRUCT ps ;

switch (message)

{

case WM_SIZE :

cxClient = LOWORD (lParam) ;

cyClient = HIWORD (lParam) ;

return 0 ;

case WM_LBUTTONDOWN :

if (!bCapturing)

{

bCapturing = TRUE ;

SetCapture (hwnd) ;

SetCursor (LoadCursor (NULL, IDC_CROSS)) ;

}

else if (!bBlocking)

{

bBlocking = TRUE ;

org = MAKEPOINT (lParam) ;

}

return 0 ;

case WM_MOUSEMOVE :

if (bCapturing)

SetCursor (LoadCursor (NULL, IDC_CROSS)) ;

if (bBlocking)

{

len = MAKEPOINT (lParam) ;

len.x -= org.x ;

len.y -= org.y ;

InvertBlock (hwnd, org, len) ;

InvertBlock (hwnd, org, len) ;

}

return 0 ;

case WM_LBUTTONUP :

if (!bBlocking)

break ;

bCapturing = bBlocking = FALSE ;

SetCursor (LoadCursor (NULL, IDC_ARROW)) ;

ReleaseCapture () ;

if (len.x == 0 || len.y == 0)

break ;

hdc = GetDC (hwnd) ;

hdcMem = CreateCompatibleDC (hdc) ;

hBitmap = CreateCompatibleBitmap (hdc,

abs (len.x), abs (len.y)) ;

if (hBitmap)

{

SelectObject (hdcMem, hBitmap) ;

StretchBlt (hdcMem, 0, 0, abs (len.x), abs (len.y),

hdc, org.x, org.y, len.x, len.y, SRCCOPY) ;

OpenClipboard (hwnd) ;

EmptyClipboard () ;

SetClipboardData (CF_BITMAP, hBitmap) ;

CloseClipboard () ;

InvalidateRect (hwnd, NULL, TRUE) ;

}

else

MessageBeep (0) ;

DeleteDC (hdcMem) ;

ReleaseDC (hwnd, hdc) ;

return 0 ;

case WM_PAINT :

InvalidateRect (hwnd, NULL, TRUE) ;

hdc = BeginPaint (hwnd, &ps) ;

OpenClipboard (hwnd) ;

if (hBitmap = GetClipboardData (CF_BITMAP))

{

SetCursor (LoadCursor (NULL, IDC_WAIT)) ;

hdcMem = CreateCompatibleDC (hdc) ;

SelectObject (hdcMem, hBitmap) ;

GetObject (hBitmap, sizeof (BITMAP), (LPSTR) &bm) ;

SetStretchBltMode (hdc, COLORONCOLOR) ;

StretchBlt (hdc, 0, 0, cxClient, cyClient,

hdcMem, 0, 0, bm.bmWidth, bm.bmHeight,

SRCCOPY) ;

SetCursor (LoadCursor (NULL, IDC_ARROW)) ;

DeleteDC (hdcMem) ;

}

CloseClipboard () ;

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_DESTROY :

PostQuitMessage (0) ;

return 0 ;

}

return DefWindowProc (hwnd, message, wParam, lParam) ;

}

BLOWUP2.DEF

;------------------------------------

; BLOWUP2.DEF module definition file

;------------------------------------

NAME BLOWUP2

DESCRIPTION 'Capture Screen Image to Clipboard (c) Charles Petzold, 1990'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS WndProc

In the earlier BLOWUP1 program, the blocked-out section of the display was copied to BLOWUP1's client area. In the new version, the area of the display is copied to a bitmap in a memory device context, and the bitmap is transferred to the clipboard.

When you're blocking out an area of the display with the mouse, BLOWUP2 retains two structures of type POINT with the initial corner (org, for ”origin“) and the width and height of the rectangle (len, for ”length“). If the org point isn't the upper left corner of the rectangle, then one or both of the values in len will be negative. When the mouse button is released (signaling to the program that the user has finished blocking out the rectangle), BLOWUP2 creates a memory device context and a bitmap using the absolute values of the lengths in the len point structure:

hdc = GetDC (hwnd) ;

hdcMem = CreateCompatibleDC (hdc) ;

hBitmap = CreateCompatibleBitmap (hdc,

abs (len.x), abs (len.y)) ;

If BLOWUP2 succeeds in creating this bitmap, the program selects the bitmap into the memory device context and uses StretchBlt to copy the blocked-out area of the display:

if (hBitmap)

{

SelectObject (hdcMem, hBitmap) ;

StretchBlt (hdcMem, 0, 0, abs (len.x), abs (len.y),

hdc, org.x, org.y, len.x, len.y, SRCCOPY) ;

Although we're using StretchBlt here, the image is not being stretched or compressed. However, if you block out the rectangle from right to left, then StretchBlt is needed to flip the image around a vertical axis. Similarly, if you block it out from bottom to top, then StretchBlt turns it upside down.

The program then opens and empties the clipboard, transfers the bitmap to the clipboard, and closes the clipboard:

OpenClipboard (hwnd) ;

EmptyClipboard () ;

SetClipboardData (CF_BITMAP, hBitmap) ;

CloseClipboard () ;

The bitmap is now the responsibility of the clipboard. Do not delete it! The clipboard will delete the bitmap itself the next time it gets an EmptyClipboard call.

Because the clipboard contains a new bitmap, BLOWUP2 invalidates its own client area, as follows:

InvalidateRect (hwnd, NULL, TRUE) ;

}

If BLOWUP2 wasn't successful in creating a bitmap, it beeps:

else

MessageBeep (0) ;

Finally, the memory device context is deleted, and the window's device context is released:

DeleteDC (hdcMem) ;

ReleaseDC (hwnd, hdc) ;

When BLOWUP2 gets a WM_PAINT message, it opens the clipboard and checks to see if a bitmap is available:

OpenClipboard (hwnd) ;

if (hBitmap = GetClipboardData (CF_BITMAP))

{

If the clipboard contains a bitmap, BLOWUP2 creates a memory device context and selects the bitmap from the clipboard into the device context:

hdcMem = CreateCompatibleDC (hdc) ;

SelectObject (hdcMem, hBitmap) ;

To copy the dimensions of this bitmap into a BITMAP structure, BLOWUP2 uses GetObject:

GetObject (hBitmap, sizeof (BITMAP), (LPSTR) &bm) ;

It can then copy the bitmap to the client area, stretching it to the larger or smaller dimensions:

SetStretchBltMode (hdc, COLORONCOLOR) ;

StretchBlt (hdc, 0, 0, xClient, yClient,

hdcMem, 0, 0, bm.bmWidth, bm.bmHeight,

SRCCOPY) ;

The only cleanup involved is deleting the memory device context:

DeleteDC (hdcMem) ;

}

and closing the clipboard:

CloseClipboard () ;

The bitmap isn't deleted, because it belongs to the clipboard.

If we wanted to make an exact copy of the bitmap, we could use GetObject to obtain the dimensions:

GetObject (hBitmap, sizeof (BITMAP), (LPSTR) &bm) ;

and create a new bitmap and another memory device context:

hBitmap2 = CreateBitmapIndirect (&bm) ;

hdcMem2 = CreateCompatibleDC (hdc) ;

SelectObject (hdcMem2, hBitmap2) ;

A simple BitBlt copies the bitmap:

BitBlt (hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight,

hdcMem, 0, 0, SRCCOPY) ;

You would then delete the two memory device contexts.

Although BLOWUP2 will display in its client area any bitmap that is currently in the clipboard, it checks the contents of the clipboard only when it gets a WM_PAINT message. For instance, if you draw something in the Windows PAINTBRUSH program, block it out, and copy it to the clipboard, BLOWUP2's client area won't show this new bitmap until BLOWUP2 gets a WM_PAINT message. For this reason, BLOWUP2 isn't a true clipboard viewer. We'll examine clipboard viewers later in this chapter.