//***********************************************************************
//
// Wincap.c
//
// Windows Screen Capture Utility
// Version 3.20
//
// Description:
// ------------
//
// Captures portions of the screen, specific windows, or the entire screen
// and saves it to a file or prints it. Uses DIBAPI functions to do most
// of the capture/printing/saving work. See the file DIBAPI.TXT for a
// description of the DIB api functions.
//
// Changes from first version:
//
// - Updated to use 3.1 Common Dialogs and 3.1 hook functions
// - New user interface displays window which was captured in client
// area of Wincap, complete with scroll bars
// - Hot new hotkeys allow more versatile capturing of windows (e.g. you
// can now capture windows with menu items pulled down)
// - New APIs to draw bitmaps and DIBs on the screen
// - Nifty startup bitmap
// - All DIB API functions are now in a DLL, which can be used
// by any application
//
// Changes from version 3.1 to 3.2:
// - Window selection changed due to how SetCapture works under Win32
// - The window to be selected for capture is highlighted as the cursor is moved
// - Option to copy DIB, DDB, and Palette to the clipboard
//
// Written by Microsoft Product Support Services, Developer Support.
// Copyright 1991-1998 Microsoft Corporation. All rights reserved.
//***********************************************************************
#define STRICT
#include <windows.h>
#include <string.h>
#include "commdlg.h"
#include "wincap.h"
#include "resource.h"
#include "dialogs.h"
#include "dibapi.h"
char szAppName[20]; // Name of application - used in dialog boxes
// Global variables
HINSTANCE ghInst; // Handle to instance
HWND ghWndMain; // Handle to main window
HOOKPROC lpfnKeyHook; // Used in keyboard hook
HOOKPROC lpfnOldHook; // Used for keyboard hook
HWND hModelessDlg; // Handle to modeless "Saving to file..."
// dialog box
BOOL bStartup=TRUE; // Startup flag for WM_PAINT/logo
BOOL bViewFull=FALSE; // Full view flag
HBITMAP ghbmLogo; // Handle to logo bitmap
HBITMAP ghBitmap=NULL; // Handle to captured bitmap
HPALETTE ghPal=NULL; // Handle to our bitmap's palette
char gszWindowText[100]; // Text which tells what we captured
BOOL gbLButtonDown=FALSE;
BOOL gbSave = FALSE;
HWND ghWndCapture=NULL;
BOOL gbNowCapturing = FALSE;
BOOL gbCaptRect = FALSE;
HWND hSelectDlg; // help dialog for window selection
HWND hRectangleDlg; // help dialog for rectangle selection
UINT guiFileOKMsg; // for common dialog FILEOKSTRING
#define WM_DOCAPTURE WM_USER+101 // Used for screen capture messages
// Macro to swap two values
#define SWAP(x,y) ((x)^=(y)^=(x)^=(y))
#define SCROLL_RATIO 4
BOOL CenterWindow (HWND hwndChild, HWND hwndParent);
//************************************************************************
//
// WinMain()
//
// Entry point of the Application.
//
//************************************************************************
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine,
int nCmdShow)
{
MSG msg;
WNDCLASS wndclass;
HWND hWnd;
CHAR lpBuffer[256];
strcpy(szAppName, "WinCap"); // Name of our App
hModelessDlg = NULL; // Set handle to modeless dialog to NULL
// because we haven't created it yet
hSelectDlg = hRectangleDlg = NULL;
LoadString(ghInst, IDS_MAINWINDOWTITLE, lpBuffer, sizeof(lpBuffer));
if (!FindWindow(szAppName, lpBuffer))
{
wndclass.style = 0;
wndclass.lpfnWndProc = (WNDPROC)WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, "WINCAP");
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
// Use black for background for better contrast
wndclass.hbrBackground = GetStockObject(GRAY_BRUSH);
wndclass.lpszMenuName = (LPSTR)"MAINMENU";
wndclass.lpszClassName = (LPSTR)szAppName;
if (!RegisterClass(&wndclass))
return FALSE;
ghInst = hInstance; // Set Global variable
// Create a main window for this application instance.
hWnd = CreateWindow(szAppName, lpBuffer, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 450, 345, NULL, NULL, hInstance, // This instance owns this window
NULL);
ghWndMain = hWnd; // Set global variable
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// Set up the Keyboard hook for our hotkeys
InstallHook(hWnd, TRUE); // Function resides in DIBAPI32.DLL
// Create our full-screen view class
wndclass.style = 0;
wndclass.lpfnWndProc = (WNDPROC)FullViewWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = NULL;
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = GetStockObject(GRAY_BRUSH);
wndclass.lpszMenuName = (LPSTR)NULL;
wndclass.lpszClassName = (LPSTR)"ViewClass";
if (!RegisterClass(&wndclass))
return FALSE;
}
// Let's make this a single-instance app -- we can get into hotkey
// conflicts (e.g. windows won't know which instance of WINCAP to
// send the message to).
else
{
LoadString(ghInst, IDS_WINCAPRUNNING, lpBuffer, sizeof(lpBuffer));
MessageBeep(0);
MessageBox(NULL, lpBuffer, szAppName, MB_OK | MB_ICONHAND);
return FALSE;
}
// Polling messages from event queue -- we have a modeless dialog
// box, so we have to take care of the messages here also
while (GetMessage(&msg, NULL, 0, 0))
{
if (hModelessDlg == NULL || !IsDialogMessage(hModelessDlg, &msg) ||
hSelectDlg || !IsDialogMessage(hSelectDlg, &msg) ||
hRectangleDlg || !IsDialogMessage(hRectangleDlg, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//**********************************************************************
//
// WndProc()
//
// This is our main window procedure. It receives all the messages destined
// for our application's main window.
//
// When we capture a window, we capture it into a Device-Dependent bitmap,
// and at the same time, we get a copy of the current system palette. This
// makes displaying the bitmap on the screen very fast. And when we want
// to print or save the captured window, we need to use this palette to
// convert the DDB to a DIB.
//
//*********************************************************************
long WINAPI WndProc(HWND hWnd, UINT wMessage, WPARAM wParam, LPARAM lParam)
{
// The gbNowCapturing variable is set to TRUE if we are in the middle of
// printing. This takes care of the case when the user presses the hotkey
// during capturing
static BOOL bCapturedYet = FALSE; // TRUE if window contains captured screen
HWND hViewWnd; // Handle to our view window
static WORD wCaptureCommand;
static HCURSOR hOldCursor;
static HWND hBrowseWnd;
CHAR lpBuffer[128];
CHAR lpBuffer2[128];
switch (wMessage)
{
case WM_CREATE:
if (!hSelectDlg)
hSelectDlg = CreateDialog(ghInst, "Select", hWnd,
SelectDlgProc);
if (!hRectangleDlg)
hRectangleDlg = CreateDialog(ghInst, "Rectangle", hWnd,
RectangleDlgProc);
ghbmLogo = LoadBitmap(ghInst, "STARTBMP");
guiFileOKMsg = RegisterWindowMessage( (LPCSTR)FILEOKSTRING );
break;
// Gray out menu items if we haven't captured anything yet.
case WM_INITMENU:
EnableMenuItem(GetMenu(hWnd), IDM_SAVE, MF_BYCOMMAND |
(bCapturedYet ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
EnableMenuItem(GetMenu(hWnd), IDM_PRINT, MF_BYCOMMAND |
(bCapturedYet ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
EnableMenuItem(GetMenu(hWnd), IDM_EDITCOPY, MF_BYCOMMAND |
(bCapturedYet ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
return 0;
// The WM_PALETTECHANGED message informs all windows that the window
// with input focus has realized its logical palette, thereby changing
// the system palette. This message allows a window without input focus
// that uses a color palette to realize its logical palettes and update
// its client area.
//
// This message is sent to all windows, including the one that changed
// the system palette and caused this message to be sent. The wParam of
// this message contains the handle of the window that caused the system
// palette to change. To avoid an infinite loop, care must be taken to
// check that the wParam of this message does not match the window's
// handle.
case WM_PALETTECHANGED:
{
HDC hDC; // Handle to device context
HPALETTE hOldPal; // Handle to previous logical palette
// Before processing this message, make sure we
// are indeed using a palette
if (ghPal)
{
// If this application did not change the palette, select
// and realize this application's palette
if (wParam != (WPARAM)hWnd)
{
// Need the window's DC for SelectPalette/RealizePalette
hDC = GetDC(hWnd);
// Select and realize our palette
hOldPal = SelectPalette(hDC, ghPal, FALSE);
RealizePalette(hDC);
// WHen updating the colors for an inactive window,
// UpdateColors can be called because it is faster than
// redrawing the client area (even though the results are
// not as good)
UpdateColors(hDC);
// Clean up
if (hOldPal)
SelectPalette(hDC, hOldPal, FALSE);
ReleaseDC(hWnd, hDC);
}
}
break;
}
// The WM_QUERYNEWPALETTE message informs a window that it is about to
// receive input focus. In response, the window receiving focus should
// realize its palette as a foreground palette and update its client
// area. If the window realizes its palette, it should return TRUE;
// otherwise, it should return FALSE.
case WM_QUERYNEWPALETTE:
{
HDC hDC; // Handle to device context
HPALETTE hOldPal; // Handle to previous logical palette
// Before processing this message, make sure we
// are indeed using a palette
if (ghPal)
{
// Need the window's DC for SelectPalette/RealizePalette
hDC = GetDC(hWnd);
// Select and realize our palette
hOldPal = SelectPalette(hDC, ghPal, FALSE);
RealizePalette(hDC);
// Redraw the entire client area
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
// Clean up
if (hOldPal)
SelectPalette(hDC, hOldPal, FALSE);
ReleaseDC(hWnd, hDC);
// Message processed, return TRUE
return TRUE;
}
// Message not processed, return FALSE
return FALSE;
}
case WM_MOVE:
{
if (gbNowCapturing)
{
if (gbCaptRect)
CenterWindow(hRectangleDlg, hWnd);
else
CenterWindow(hSelectDlg, hWnd);
}
return 0;
}
case WM_SIZE:
{
static BOOL bSizing=FALSE;
if (gbNowCapturing)
{
if (gbCaptRect)
CenterWindow(hRectangleDlg, hWnd);
else
CenterWindow(hSelectDlg, hWnd);
}
// Check if we are already sizing
if (bSizing)
return 0l;
bSizing = TRUE;
DoSize(hWnd);
bSizing = FALSE;
break;
}
case WM_HSCROLL:
case WM_VSCROLL:
DoScroll(hWnd, wMessage, (int)HIWORD(wParam), (int)LOWORD(wParam));
break;
case WM_PAINT:
DoPaint(hWnd);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_ABOUT:
DialogBox(ghInst, (LPSTR)"About", hWnd, AboutDlgProc);
break;
case IDM_SAVE:
SaveMe();
break;
case IDM_PRINT:
PrintMe();
break;
case IDM_VIEWFULL:
if (!bViewFull && (ghBitmap || bStartup))
{
HDC hDC;
int ScreenX, ScreenY;
hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
ScreenX = GetDeviceCaps(hDC, HORZRES);
ScreenY = GetDeviceCaps(hDC, VERTRES);
DeleteDC(hDC);
hViewWnd = CreateWindow((LPSTR)"ViewClass", (LPSTR)NULL,
WS_POPUP | WS_VISIBLE, 0, 0, ScreenX, ScreenY,
hWnd, NULL, ghInst, NULL);
ShowWindow(hViewWnd, SW_SHOW);
UpdateWindow(hViewWnd);
}
else {
LoadString(ghInst, IDS_NOIMAGE, lpBuffer, sizeof(lpBuffer));
LoadString(ghInst, IDS_VWFULLSCRN, lpBuffer2, sizeof(lpBuffer2));
MessageBox(hWnd, lpBuffer, lpBuffer2, MB_OK);
}
break;
case IDM_EDITCOPY: // Copy DIB, bitmap, & palette to clipboard
if (OpenClipboard(hWnd))
{
HDIB hDib;
HBITMAP hBitmap;
HPALETTE hPal;
EmptyClipboard();
if (ghBitmap)
{
// Once these are added to the clipboard, the
// clipboard owns the data.
if (hDib = BitmapToDIB(ghBitmap, ghPal))
SetClipboardData(CF_DIB, hDib);
// Make a copy of the bitmap so the clipboard can
// own it.
if (hBitmap = DIBToBitmap(hDib, ghPal))
SetClipboardData(CF_BITMAP, hBitmap);
// Make a copy of the palette so the clipboard can
// own it.
if (hPal = GetStockObject(DEFAULT_PALETTE))
SetClipboardData(CF_PALETTE, hPal);
}
CloseClipboard();
}
break;
case IDM_VIEWCLEAR:
{
WORD wRet;
if (gbSave)
{
LoadString(ghInst, IDS_SAVEBMP, lpBuffer, sizeof(lpBuffer));
wRet = MessageBox(hWnd, lpBuffer,
szAppName, MB_ICONEXCLAMATION | MB_YESNOCANCEL);
if (wRet == IDYES)
SaveMe();
else if( wRet == IDCANCEL)
break;
}
gbSave = FALSE;
// If we are just displaying logo, don't
// display it anymore
if (bStartup)
bStartup = FALSE;
// Delete captured bitmap if we have one
if (ghBitmap)
{
DeleteObject(ghBitmap);
ghBitmap = NULL;
}
// Delete our captured bitmap's palette if we have one
if (ghPal)
{
DeleteObject(ghPal);
ghPal = NULL;
}
// Now update display to reflect fact that we
// nuked the captured bitmap or don't want to
// look at the cool logo
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
bCapturedYet = FALSE; // Gray out "File.Save" menu item
break;
}
case IDM_CAPTRECT:
gbCaptRect = TRUE;
case IDM_CAPTWINDOW: // make sure lParam == IDM_CURSORSELECT
case IDM_CAPTCLIENT: // make sure lParam == IDM_CURSORSELECT
lParam = IDM_CURSORSELECT;
case IDM_ACTIVEWINDOW:
case IDM_DESKTOP:
// User selected one of the window capture items
// Check to see that we aren't already in the middle of
// capturing. This could happen if the user presses our hotkey
// in the middle of one of our dialog boxes.
if (gbNowCapturing)
{
LoadString(ghInst, IDS_CAPTEDALREADY, lpBuffer, sizeof(lpBuffer));
MessageBox(NULL, lpBuffer,
szAppName, MB_OK | MB_ICONEXCLAMATION);
}
else
{
// User wants to capture screen. One problem we may
// run into here is that we might have a popup menu pulled
// down in our own application when we get to this point
// (because of the hotkey feature of this app).
//
// Normally, we'd just enter a message loop after calling
// ShowWindow(SW_HIDE) to take care of any menu messages
// which may have been posted as we hide our application's
// window, and *then* call our screen capture routine.
// But unfortunately, we can't do that here and be 100% safe.
//
// If we *have* been sent here on a hotkey, and a menu of
// our own app is currently down, then we are currently running
// inside of a PeekMessage() loop in the Windows Menu
// manager. We should *not* enter another PeekMessage loop,
// but should return from this message case right away.
// The Windows Menu manager code relies on checking messages
// in it's queue via it's own PeekMessage loop, and if we
// entered one, it would confuse the menu manager.
//
// So what we do instead is just post ourselves a private
// message, and when we get this message (see below), then
// do the screen capture.
//
// Commence screen capture!
gbNowCapturing = TRUE;
if (wParam == IDM_DESKTOP)
ShowWindow(hWnd, SW_HIDE);
// Allow this message case to return right away. We'll
// capture screen down below.
if (lParam != IDM_CURSORSELECT)
PostMessage(hWnd, WM_DOCAPTURE, wParam, 0L);
else if (gbCaptRect)
{
CenterWindow(hRectangleDlg, hWnd);
ShowWindow(hRectangleDlg, SW_SHOW);
}
else
{
CenterWindow(hSelectDlg, hWnd);
ShowWindow(hSelectDlg, SW_SHOW);
}
wCaptureCommand = (WORD)wParam;
}
break;
case IDM_EXIT:
PostMessage(hWnd, WM_CLOSE, 0, 0L);
break;
default:
return DefWindowProc(hWnd, wMessage, wParam, lParam);
break;
} //switch (LOWORD(wParam))
break; // case WM_COMMAND:
// Message case for doing screen capture. This message is posted
// to us from the IDM_CAPT* code above. wParam should be equal to
// the ID of the message which we got sent here for (it is used in
// the call to DoCapture()).
case WM_DOCAPTURE:
{
// We're going to capture a new screen, get rid of
// previous captured image
if (ghBitmap)
{
DeleteObject(ghBitmap);
ghBitmap = NULL;
}
if (ghPal)
{
DeleteObject(ghPal);
ghPal = NULL;
}
SetForegroundWindow(ghWndCapture);
// Save captured screen as bitmap
DoCapture(hWnd, (WORD)wParam);
SetForegroundWindow(hWnd);
// Un-hide Window
ShowWindow(hWnd, SW_SHOW);
if (IsIconic(hWnd))
PostMessage(hWnd, WM_SYSCOMMAND, SC_RESTORE, 0L);
if (!gbCaptRect)
gbNowCapturing = FALSE;
bCapturedYet = TRUE; // Enable "File.Save" menu item
gbSave = TRUE;
break;
}
case WM_LBUTTONDOWN:
{
if (!gbNowCapturing)
break;
ShowWindow(hWnd, SW_HIDE); // Hide main app's window
if (gbCaptRect)
ShowWindow(hRectangleDlg, SW_HIDE);
else
ShowWindow(hSelectDlg, SW_HIDE);
if (gbCaptRect)
{
hOldCursor = SetCursor(LoadCursor(NULL, IDC_CROSS));
PostMessage(hWnd, WM_DOCAPTURE, wCaptureCommand, 0L);
}
else
hOldCursor = SetCursor(LoadCursor(ghInst, "SELECT"));
gbLButtonDown = TRUE;
SetCapture(ghWndMain);
break;
}
case WM_LBUTTONUP:
{
POINTS pts;
POINT pt;
if (!(gbNowCapturing && gbLButtonDown))
break;
ReleaseCapture();
gbLButtonDown = FALSE;
SetCursor(hOldCursor);
if (gbCaptRect)
{
gbCaptRect = FALSE;
gbNowCapturing = FALSE;
break;
}
FrameWindow(hBrowseWnd);
hBrowseWnd = NULL;
pts = MAKEPOINTS(lParam);
pt.x = pts.x;
pt.y = pts.y;
// Convert to screen coordinates
ClientToScreen(ghWndMain, &pt);
// Get Window that we clicked on
ghWndCapture = WindowFromPoint(pt);
// If it's not a valid window, just return NULL
if (!ghWndCapture)
{
SetCursor(hOldCursor);
gbLButtonDown = gbNowCapturing = FALSE;
break;
}
PostMessage(hWnd, WM_DOCAPTURE, wCaptureCommand, 0L);
break;
}
case WM_MOUSEMOVE:
{
POINT pt;
HWND hWndCurrent;
if (!(gbNowCapturing && gbLButtonDown))
break;
if (gbCaptRect)
break;
// get the current mouse position
GetCursorPos(&pt);
// Get Window that we clicked on
hWndCurrent = WindowFromPoint(pt);
// if the cursor is still moving in the current window, no need
// to highlight it again
if (hWndCurrent != hBrowseWnd)
{
FrameWindow(hBrowseWnd);
FrameWindow(hWndCurrent);
hBrowseWnd = hWndCurrent;
if (hWndCurrent)
UpdateWindow(hWndCurrent);
}
break;
}
case WM_CLOSE:
{
WORD wRet;
if (gbSave)
{
LoadString(ghInst, IDS_SAVEBMP, lpBuffer, sizeof(lpBuffer));
wRet = MessageBox(hWnd, lpBuffer,
szAppName, MB_ICONEXCLAMATION | MB_YESNOCANCEL);
if (wRet == IDYES)
SaveMe();
else if( wRet == IDCANCEL)
break;
}
if (IsWindow(hSelectDlg))
DestroyWindow(hSelectDlg);
if (IsWindow(hRectangleDlg))
DestroyWindow(hRectangleDlg);
return DefWindowProc(hWnd, wMessage, wParam, lParam);
}
case WM_DESTROY: // Clean up
InstallHook(hWnd, FALSE); // Remove keyboard hook
DeleteObject(ghbmLogo);
if (ghBitmap)
DeleteObject(ghBitmap);
if (ghPal)
DeleteObject(ghPal);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, wMessage, wParam, lParam);
}
return 0L;
}
//**********************************************************************
//
// FullViewWndProc()
//
// This is our full-screen popup window procedure. It is used to display an
// image using the entire screen. Clicking the left mouse button restores
// to the main app window.
//
//
//*********************************************************************
long APIENTRY FullViewWndProc(HWND hWnd, UINT wMessage, WPARAM wParam,
LPARAM lParam)
{
switch (wMessage)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hMemDC;
BITMAP bm;
HBITMAP hOldBm;
RECT rect, rectClient;
int x, y;
BeginPaint(hWnd, &ps);
// Check to see if we are displaying a bitmap
if (!ghBitmap)
{
// No bitmap yet, are we in start mode?
if (bStartup)
{
GetClientRect(hWnd, &rectClient);
hMemDC = CreateCompatibleDC(ps.hdc);
// Select our logo bitmap
hOldBm = SelectObject(hMemDC, ghbmLogo);
GetObject(ghbmLogo, sizeof(BITMAP), (VOID *)&bm);
x = (rectClient.right - bm.bmWidth) / 2;
y = (rectClient.bottom - bm.bmHeight) / 2;
// Now bitblt our logo to client area
BitBlt(ps.hdc, x, y, bm.bmWidth, bm.bmHeight,
hMemDC, 0, 0, SRCCOPY);
// Clean up
SelectObject(hMemDC,hOldBm);
DeleteDC(hMemDC);
}
}
else
{
// Get info for captured bitmap
GetObject(ghBitmap, sizeof(BITMAP), (LPSTR)&bm);
// Fill in src/dest rectangle with width and height
// of captured bitmap
rect.left = 0;
rect.top = 0;
rect.right = bm.bmWidth;
rect.bottom = bm.bmHeight;
// Paint the captured bitmap in the client area
PaintBitmap(ps.hdc, &rect, ghBitmap, &rect, ghPal);
}
EndPaint(hWnd, &ps);
break;
}
case WM_KEYDOWN:
case WM_LBUTTONDOWN:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, wMessage, wParam, lParam);
}
}
//**********************************************************************
//
// SaveMe()
//
// This procedure calls up the common dialog "File.Save" box, then
// saves the current hBitmap as a DIB in the specified file, in the
// file format specified by the user in the dialog.
//
//*********************************************************************
void SaveMe()
{
char szFileBuf[255]; // Buffer to hold returned file name
DWORD dwFlags; // used to pass in / get out file type
HDC hDC; // HDC for getting info
int iBits; // Bits per pixel of the display adapter
DWORD dCompression; // Compression that the user specifies
WORD wBitCount; // Bits per pixel that the user specifies
WORD wCompression; // Compression
HDIB hDIB; // A handle to our dib
BOOL bResult; // Result of dialog box - TRUE if OK was pressed
CHAR lpBuffer[128]; // Buffer to hold string message retrieved from resources
CHAR lpBuffer2[128]; // Buffer to hold string message retrieved from resources
// Set up default compression to display in dialog box
wCompression = IDD_RGB;
// Depending on bits/pixel type for current display,
// set the appropriate bit in the fFileOptions flag
//
// NOTE that we don't just assign wBitCount to iBits. The reason
// for this is some displays aren't 1,4,8 or 24 bits. Some are
// 15 bits, which isn't valid for a DIB, so in this case, we would
// set the bits to 24.
hDC = CreateDC("DISPLAY",NULL,NULL,NULL);
iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
DeleteDC(hDC);
if (iBits <= 1)
wBitCount = 1;
else if (iBits <= 4)
wBitCount = 4;
else if (iBits <= 8)
wBitCount = 8;
else if (iBits <= 24)
wBitCount = 24;
// Our dwFlags parameter to GetFileName is made up of the
// bits per pixel in the HIWORD (1, 2, 4, or 24), and the compression
// type in the LOWORD (IDD_RGB, IDD_RLE4, or IDD_RLE8).
dwFlags = MAKELONG(wCompression, wBitCount);
// Bring up File/Save... dialog and get info. about filename,
// compression, and bits/pix. of DIB to be written.
bResult = GetFileName(ghWndMain, (LPSTR)szFileBuf, &dwFlags);
// Extract DIB specs and save to file (if the user did not
// press cancel)
if (bResult)
{
switch(LOWORD(dwFlags))
{
case IDD_RLE4:
dCompression = BI_RLE4;
break;
case IDD_RLE8:
dCompression = BI_RLE8;
break;
case IDD_RGB:
default:
dCompression = BI_RGB;
break;
}
// First, call up a modeless dialog box which tells that we are
// saving this to a file...
if (!hModelessDlg)
{
hModelessDlg = CreateDialogParam(ghInst, (LPSTR)"Saving",
ghWndMain,SavingDlgProc, (DWORD)(LPSTR)szFileBuf);
}
// Now, write out the DIB in the proper format. The following
// API ChangeBitmapFormat() will convert the specified bitmap
// to a DIB with the specified Bit Count, Compression and
// Palette. Remember that the HIWORD of dwFlags specifies the
// bits per pixel.
hDIB = ChangeBitmapFormat(ghBitmap, HIWORD(dwFlags),
dCompression, ghPal);
if (hDIB)
{
if (SaveDIB(hDIB, szFileBuf))
{
LoadString(ghInst, IDS_CANTSAVE, lpBuffer, sizeof(lpBuffer));
LoadString(ghInst, IDS_SAVEERROR, lpBuffer2, sizeof(lpBuffer2));
MessageBox(NULL, lpBuffer, lpBuffer2, MB_ICONEXCLAMATION);
gbSave = TRUE;
}
else
gbSave = FALSE;
DestroyDIB(hDIB);
}
DestroyWindow(hModelessDlg);
hModelessDlg = NULL;
}
}
//*********************************************************************
//
// PrintMe()
//
// This procedure calls up the "File.Print" dialog, then prints the
// current hBitmap as a DIB on the default printer.
//
//*********************************************************************
void PrintMe()
{
static OPTIONSTRUCT opts;
int iReturn;
HDIB hDIB;
WORD wReturn;
// Display "Print Options" Box
iReturn = DialogBoxParam(ghInst, (LPSTR)"Print", ghWndMain, PrintDlgProc,
(LONG)(LPSTR)&opts);
if (iReturn)
{
// User pressed "OK" -- do the printing
hDIB = BitmapToDIB(ghBitmap, ghPal);
if (hDIB)
{
// Print the dib using PrintDIB() API
if (opts.iOption == IDC_STRETCHTOPAGE)
wReturn = PrintDIB(hDIB, PW_STRETCHTOPAGE, 0, 0,
(LPSTR)gszWindowText);
else if (opts.iOption == IDC_SCALE)
wReturn = PrintDIB(hDIB, (WORD)PW_SCALE, (WORD)opts.iXScale,
(WORD)opts.iYScale, (LPSTR)gszWindowText);
else
wReturn = PrintDIB(hDIB, PW_BESTFIT, 0, 0,
(LPSTR)gszWindowText);
if (wReturn)
DIBError(wReturn);
DestroyDIB(hDIB);
}
}
}
//*********************************************************************
//
// DoCapture()
//
// This procedure gets called when the user wants to capture the
// screen. The wCommand parameter tells us which capture operation
// we want to perform.
//
//*********************************************************************
void DoCapture(HWND hWnd, WORD wCommand)
{
HBITMAP hBitmap; // Handle to our temporary bitmap
HPALETTE hPal; // Handle to our palette
CHAR lpBuffer[128]; // Buffer for string retrieved from resources
switch (wCommand)
{
// Copy Entire screen to DIB
case IDM_DESKTOP:
{
RECT rScreen; // Rect containing entire screen coordinates
HDC hDC; // DC to screen
MSG msg; // Message for the PeekMessage()
CHAR lpBuffer[128]; // Buffer for string retrieved from resources
hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
rScreen.left = rScreen.top = 0;
rScreen.right = GetDeviceCaps(hDC, HORZRES);
rScreen.bottom = GetDeviceCaps(hDC, VERTRES);
// Delete our DC
DeleteDC(hDC);
LoadString(ghInst, IDS_ENTIRESCRN, lpBuffer, sizeof(lpBuffer));
strcpy(gszWindowText, lpBuffer);
// Wait until everybody repaints themselves
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
hBitmap = CopyScreenToBitmap(&rScreen);
if (hBitmap)
hPal = GetSystemPalette();
else
hPal = NULL;
}
break;
// copy user-selected portion of screen to DIB
case IDM_CAPTRECT:
{
RECT rRubberBand; // Region to capture (screen coordinates)
// Allow user to "rubberband" a section of the screen for
// us to capture
RubberBandScreen(&rRubberBand);
LoadString(ghInst, IDS_USERSELECT, lpBuffer, sizeof(lpBuffer));
strcpy(gszWindowText, lpBuffer);
hBitmap = CopyScreenToBitmap(&rRubberBand);
if (hBitmap)
hPal = GetSystemPalette();
else
hPal = NULL;
}
break;
case IDM_CAPTWINDOW:
case IDM_CAPTCLIENT:
case IDM_ACTIVEWINDOW:
{
HWND hWndSelect; // The current active window
HWND hWndDesktop; // Window to desktop
MSG msg; // For our peekmessage loop
// Just capture the current active window, whatever it is
if (wCommand == IDM_ACTIVEWINDOW)
hWndSelect = GetForegroundWindow();
// Allow the user to click on a single window to capture
else
hWndSelect = ghWndCapture;
// If they try to capture the desktop window, then just
// capture the entire screen.
hWndDesktop = GetDesktopWindow();
if (hWndSelect == hWndDesktop)
{
RECT rScreen; // contains entire screen coordinates
HDC hDC; // DC to screen
MSG msg; // Message for the PeekMessage()
hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
rScreen.left = rScreen.top = 0;
rScreen.right = GetDeviceCaps(hDC, HORZRES);
rScreen.bottom = GetDeviceCaps(hDC, VERTRES);
/* Delete our DC */
DeleteDC(hDC);
LoadString(ghInst, IDS_ENTIRESCRN, lpBuffer, sizeof(lpBuffer));
strcpy(gszWindowText, lpBuffer);
// Wait until everybody repaints themselves
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
ghBitmap = CopyScreenToBitmap(&rScreen);
if (ghBitmap)
ghPal = GetSystemPalette();
else
ghPal = NULL;
ghWndCapture = NULL;
return;
}
// Check to see that the hWnd is not NULL
if (!hWndSelect)
{
LoadString(ghInst, IDS_CANTCAPWNDW, lpBuffer, sizeof(lpBuffer));
MessageBox(NULL, lpBuffer, szAppName,
MB_ICONEXCLAMATION | MB_OK);
hBitmap = NULL;
hPal = NULL;
break;
}
// Make sure it's not a hidden window. Hmm, capturing a hidden
// window would certainly be a cool trick, wouldn't it?
if (!IsWindowVisible(hWndSelect))
{
LoadString(ghInst, IDS_WNDWNOTVIS, lpBuffer, sizeof(lpBuffer));
MessageBox(NULL, lpBuffer,
szAppName, MB_ICONEXCLAMATION | MB_OK);
ghWndCapture = NULL;
hBitmap = NULL;
hPal = NULL;
break;
}
// Move window which was selected to top of Z-order for
// the capture, and make it redraw itself
SetWindowPos(hWndSelect, NULL, 0, 0, 0, 0,
SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOMOVE);
UpdateWindow(hWndSelect);
// Wait until everybody repaints themselves
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Get the caption
GetWindowText(hWndSelect, gszWindowText, 100);
// Capture the screen -- if we selected CLIENT only capture, then
// select that open when calling our API. Otherwise, get the
// entire window.
hBitmap = CopyWindowToBitmap(hWndSelect,
(WORD)((wCommand == IDM_CAPTCLIENT) ? (PW_CLIENT) :
(PW_WINDOW)));
if (hBitmap)
hPal = GetSystemPalette();
else
hPal = NULL;
break;
}
//Oops, something went wrong
default:
LoadString(ghInst, IDS_INTERROR, lpBuffer, sizeof(lpBuffer));
MessageBox(NULL, lpBuffer, szAppName,
MB_ICONHAND | MB_OK);
break;
}
if (hBitmap)
{
ghBitmap = hBitmap;
hBitmap = NULL;
}
if (hPal)
{
ghPal = hPal;
hPal = NULL;
}
// Now, paint our bitmap in the client area
if (bStartup)
bStartup = FALSE;
InvalidateRect(hWnd, NULL, FALSE);
UpdateWindow(hWnd);
ghWndCapture = NULL;
}
//**********************************************************************
//
// RubberBandScreen()
//
// This function allows the user to rubber-band a portion of the screen.
// When the left button is released, the rect that the user selected
// (in screen coordinates) is returned in lpRect.
//
//*********************************************************************
void RubberBandScreen(LPRECT lpRect)
{
POINT pt; // Temporary POINT
MSG msg; // Used in our PeekMessage() loop
POINT ptOrigin; // Point where the user pressed left mouse button down
RECT rcClip; // Current selection
HDC hScreenDC; // DC to the screen (so we can draw on it)
BOOL bCapturing = FALSE; // TRUE if we are rubber-banding
POINTS pts;
BOOL bLButtonUp = FALSE;
hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
// Eat mouse messages until a WM_LBUTTONUP is encountered. Meanwhile
// continue to draw a rubberbanding rectangle and display it's dimensions
for (;;)
{
WaitMessage();
if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
{
// If the message is a WM_LBUTTONDOWN, begin drawing the
// rubber-band box.
if (msg.message == WM_RBUTTONDOWN)
{
// User pressed left button, initialize selection
// Set origin to current mouse position (in window coords)
pts = MAKEPOINTS(msg.lParam);
ptOrigin.x = pts.x;
ptOrigin.y = pts.y;
// Convert to screen coordinates
ClientToScreen(ghWndMain, &ptOrigin);
// rcClip is the current rectangle that the user
// has selected. Since user just pressed left button down,
// initialize this rect very small
rcClip.left = rcClip.right = ptOrigin.x;
rcClip.top = rcClip.bottom = ptOrigin.y;
NormalizeRect(&rcClip); // Make sure it is a normal rect
DrawSelect(hScreenDC, TRUE, &rcClip); // Draw the rubber-band box
bCapturing = TRUE;
}
else if (msg.message == WM_LBUTTONUP)
{
if (!bCapturing)
break;
else
{
bLButtonUp = TRUE;
PostMessage(ghWndMain, WM_RBUTTONUP, msg.wParam, msg.lParam);
}
}
// Any messages that make it into the next statement are mouse
// messages, and we are capturing, so let's update the rubber-band
// box
if (bCapturing)
{
DrawSelect(hScreenDC, FALSE, &rcClip); // erase old rubber-band
rcClip.left = ptOrigin.x; // Update rect with new mouse info
rcClip.top = ptOrigin.y;
pts = MAKEPOINTS(msg.lParam);
pt.x = pts.x;
pt.y = pts.y;
// Convert to screen coordinates
ClientToScreen(ghWndMain, &pt);
rcClip.right = pt.x;
rcClip.bottom = pt.y;
NormalizeRect(&rcClip);
DrawSelect(hScreenDC, TRUE, &rcClip); // new rubber-band
}
// If the message is WM_LBUTTONUP, then we stop the selection
// process.
if (msg.message == WM_RBUTTONUP)
{
DrawSelect(hScreenDC, FALSE, &rcClip); // erase rubber-band
if (bLButtonUp)
PostMessage(ghWndMain, WM_LBUTTONUP, msg.wParam, msg.lParam);
break;
}
}
else
continue;
}
DeleteDC(hScreenDC);
// Assign rect user selected to lpRect parameter
if (!IsRectEmpty(&rcClip))
CopyRect(lpRect, &rcClip);
PostMessage(ghWndMain, WM_LBUTTONUP, msg.wParam, msg.lParam);
}
//***************************************************************************
//
// DrawSelect
//
// Draws the selected clip rectangle with its dimensions on the DC
//
//***************************************************************************
void DrawSelect(HDC hdc, BOOL fDraw, LPRECT lprClip)
{
char sz[80];
DWORD dw;
int x, y, len, dx, dy;
HDC hdcBits;
HBITMAP hbm;
RECT rcClip;
SIZE sExtent;
rcClip = *lprClip;
if (!IsRectEmpty(&rcClip))
{
// If a rectangular clip region has been selected, draw it
PatBlt(hdc, rcClip.left, rcClip.top, rcClip.right - rcClip.left, 1,
DSTINVERT);
PatBlt(hdc, rcClip.left, rcClip.bottom, 1, -(rcClip.bottom-rcClip.top),
DSTINVERT);
PatBlt(hdc, rcClip.right - 1, rcClip.top, 1, rcClip.bottom - rcClip.top,
DSTINVERT);
PatBlt(hdc, rcClip.right, rcClip.bottom - 1, -(rcClip.right -
rcClip.left), 1, DSTINVERT);
// Format the dimensions string ...
wsprintf(sz, "%dx%d", rcClip.right - rcClip.left, rcClip.bottom -
rcClip.top);
len = lstrlen(sz);
// ... and center it in the rectangle
dw = GetTextExtentPoint(hdc, sz, len, &sExtent);
dx = sExtent.cx;
dy = sExtent.cy;
x = (rcClip.right + rcClip.left - dx) / 2;
y = (rcClip.bottom + rcClip.top - dy) / 2;
hdcBits = CreateCompatibleDC(hdc);
SetTextColor(hdcBits, 0xFFFFFFL);
SetBkColor(hdcBits, 0x000000L);
// Output the text to the DC
if (hbm = CreateBitmap(dx, dy, 1, 1, NULL))
{
hbm = SelectObject(hdcBits, hbm);
ExtTextOut(hdcBits, 0, 0, 0, NULL, sz, len, NULL);
BitBlt(hdc, x, y, dx, dy, hdcBits, 0, 0, SRCINVERT);
hbm = SelectObject(hdcBits, hbm);
DeleteObject(hbm);
}
DeleteDC(hdcBits);
}
}
//*************************************************************************
//
// FUNCTION : NormalizeRect(RECT *prc)
//
// PURPOSE : If the rectangle coordinates are reversed, swaps them.
// This is used to make sure that the first coordinate of
// our rect is the upper left, and the second is lower right.
//
//*************************************************************************
void WINAPI NormalizeRect(LPRECT prc)
{
if (prc->right < prc->left) SWAP(prc->right, prc->left);
if (prc->bottom < prc->top) SWAP(prc->bottom, prc->top);
}
//***************************************************************************
//
// Function: DoSize
//
// Purpose: Called by WndProc() on WM_SIZE
//
// When the window is sized -- set up the scroll bars.
//
// The window will be repainted if the new size, combined
// with the current scroll bar positions would create blank
// space at the left or bottom of the window.
//
//***************************************************************************
void DoSize(HWND hWnd)
{
BITMAP bm; // Bitmap info structure
int cxBitmap=0, cyBitmap=0; // Bitmap width and height
int cxScroll, cyScroll; // Scroll positions
RECT rect; // Client rectangle
// repaint if displaying bitmap
if (ghBitmap)
{
// Get info about bitmap
GetObject(ghBitmap, sizeof(BITMAP), (LPSTR)&bm);
// Get the width and height of the bitmap
cxBitmap = bm.bmWidth;
cyBitmap = bm.bmHeight;
// Find out the dimensions of the window, and the current thumb
// positions
GetClientRect(hWnd, &rect);
cxScroll = GetScrollPos (hWnd, SB_HORZ);
cyScroll = GetScrollPos (hWnd, SB_VERT);
// If current thumb positions would cause blank space
// at right or bottom of client area, repaint
if (cxScroll + rect.right > cxBitmap ||
cyScroll + rect.bottom > cyBitmap)
InvalidateRect(hWnd, NULL, FALSE);
// Make sure scroll bars are updated
SetupScrollBars(hWnd, (WORD)cxBitmap, (WORD)cyBitmap);
}
else if (bStartup)
InvalidateRect(hWnd, NULL, TRUE);
}
//***************************************************************************
//
// Function: ReallyGetClientRect
//
// Purpose: Gets the rectangular area of the client rect including
// the area underneath visible scroll bars. Stolen from
// ShowDIB.
//
//***************************************************************************
void ReallyGetClientRect(HWND hWnd, LPRECT lpRect)
{
DWORD dwWinStyle;
dwWinStyle = GetWindowLong (hWnd, GWL_STYLE);
GetClientRect (hWnd, lpRect);
if (dwWinStyle & WS_HSCROLL)
lpRect->bottom += (GetSystemMetrics (SM_CYHSCROLL) - 1);
if (dwWinStyle & WS_VSCROLL)
lpRect->right += (GetSystemMetrics (SM_CXVSCROLL) - 1);
}
//***************************************************************************
//
// Function: SetupScrollBars
//
// Purpose: Sets up scroll bars.
//
//***************************************************************************
void SetupScrollBars(HWND hWnd, WORD cxBitmap, WORD cyBitmap)
{
RECT rect; // Client Rectangle
BOOL bNeedScrollBars=FALSE; // Need Scroll bars?
unsigned cxWindow, cyWindow; // Width and height of client area
int cxRange=0, cyRange=0; // Range needed for horz and vert
// Do some initialization
ReallyGetClientRect(hWnd, &rect);
cxWindow = rect.right - rect.left;
cyWindow = rect.bottom - rect.top;
// Now determine if we need the scroll bars
if ((cxWindow < (unsigned)cxBitmap) || (cyWindow < (unsigned)cyBitmap))
bNeedScrollBars = TRUE;
// Setup the scroll bar ranges. We want to be able to
// scroll the window so that all the bitmap can appear
// within the client area. Take into account that
// if the opposite scroll bar is activated, it eats
// up some client area.
if (bNeedScrollBars)
{
cyRange = (unsigned)cyBitmap - cyWindow - 1 +
GetSystemMetrics (SM_CYHSCROLL);
cxRange = (unsigned)cxBitmap - cxWindow - 1 +
GetSystemMetrics (SM_CXVSCROLL);
}
// Set the ranges we've calculated (0 to 0 means invisible scrollbar)
SetScrollRange(hWnd, SB_VERT, 0, cyRange, TRUE);
SetScrollRange(hWnd, SB_HORZ, 0, cxRange, TRUE);
}
//**********************************************************************
//
// Function: DoScroll()
//
// Purpose: Called by ChildWndProc() on WM_HSCROLL and WM_VSCROLL.
// Window needs to be scrolled (user has clicked on one
// of the scroll bars.
//
// Does scrolling in both horiziontal and vertical directions.
// Note that the variables are all named as if we were
// doing a horizontal scroll. However, if we're doing a
// vertical scroll, they are initialized to the appropriate
// values for a vertical scroll.
//
// If we scroll by one (i.e. user clicks on one of the
// scrolling arrows), we scroll the window by 1/SCROLL_RATIO
// of the client area. In other words, if SCROLL_RATION==4,
// then we move the client area over a 1/4 of the width/height
// of the screen.
//
// If the user is paging up/down we move a full client area's
// worth.
//
// If the user moves the thumb to an absolute position, we
// just move there.
//
// ScrollWindow/re-painting do the actual work of scrolling.
//
//**********************************************************************
void DoScroll(HWND hWnd, int message, int wPos, int wScrollType)
{
int xBar; // Where scrollbar is now.
int nMin; // Minumum scroll bar value.
int nMax; // Maximum scroll bar value.
int dx; // How much to move.
int nOneUnit; // # of pixels for LINEUP/LINEDOWN
int cxClient; // Width of client area.
int nHorzOrVert; // Doing the horizontal or vertical?
RECT rect; // Client area.
GetClientRect (hWnd, &rect);
if (message == WM_HSCROLL)
{
nHorzOrVert = SB_HORZ;
cxClient = rect.right - rect.left;
}
else
{
nHorzOrVert = SB_VERT;
cxClient = rect.bottom - rect.top;
}
// On a SB_LINEUP/SB_LINEDOWN we will move the DIB by
// 1/SCROLL_RATIO of the client area (i.e. if SCROLL_RATIO
// is 4, it will scroll the DIB a quarter of the client
// area's height or width.
nOneUnit = cxClient / SCROLL_RATIO;
if (!nOneUnit)
nOneUnit = 1;
xBar = GetScrollPos (hWnd, nHorzOrVert);
GetScrollRange (hWnd, nHorzOrVert, &nMin, &nMax);
switch (wScrollType)
{
case SB_LINEDOWN: // One line right.
dx = nOneUnit;
break;
case SB_LINEUP: // One line left.
dx = -nOneUnit;
break;
case SB_PAGEDOWN: // One page right.
dx = cxClient;
break;
case SB_PAGEUP: // One page left.
dx = -cxClient;
break;
case SB_THUMBPOSITION: // Absolute position.
dx = wPos - xBar;
break;
default: // No change.
dx = 0;
break;
}
if (dx)
{
xBar += dx;
if (xBar < nMin)
{
dx -= xBar - nMin;
xBar = nMin;
}
if (xBar > nMax)
{
dx -= xBar - nMax;
xBar = nMax;
}
if (dx)
{
SetScrollPos (hWnd, nHorzOrVert, xBar, TRUE);
if (nHorzOrVert == SB_HORZ)
ScrollWindow (hWnd, -dx, 0, NULL, NULL);
else
ScrollWindow (hWnd, 0, -dx, NULL, NULL);
UpdateWindow (hWnd);
}
}
}
//****************************************************************************
//
// Function: DoPaint()
//
// Purpose: Called by WndProc. Does painting for client area.
//
//
//***************************************************************************
void DoPaint(HWND hWnd)
{
HDC hDC, hMemDC; // Handle to DC, memory DC
PAINTSTRUCT ps; // Painting structure
BITMAP bm; // BITMAP structure
HBITMAP hOldBm; // Handle to previous bitmap
RECT rectClient, rectDDB; // Client and bitmap rectangles
int xScroll, yScroll; // Scroll positions
int x, y; // Logo origin
// Begin painting
hDC = BeginPaint(hWnd, &ps);
// Check to see if we are displaying a bitmap
if (!ghBitmap)
{
// No bitmap yet, are we in start mode?
if (bStartup)
{
GetClientRect(hWnd, &rectClient);
hMemDC = CreateCompatibleDC(ps.hdc);
// Select our logo bitmap
hOldBm = SelectObject(hMemDC, ghbmLogo);
GetObject(ghbmLogo, sizeof(BITMAP), (VOID *)&bm);
x = (rectClient.right - bm.bmWidth) / 2;
y = (rectClient.bottom - bm.bmHeight) / 2;
// Now bitblt our logo to client area
BitBlt(ps.hdc, x, y, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0,
SRCCOPY);
// Clean up
SelectObject(hMemDC,hOldBm);
DeleteDC(hMemDC);
}
else
{
// Turn off scroll bars in case they were on
SetScrollRange (hWnd, SB_VERT, 0, 0, TRUE);
SetScrollRange (hWnd, SB_HORZ, 0, 0, TRUE);
}
}
else // We are displaying a bitmap
{
// Get bitmap info
GetObject(ghBitmap, sizeof(BITMAP), (LPSTR)&bm);
// Get scroll bar positions
xScroll = GetScrollPos (hWnd, SB_HORZ);
yScroll = GetScrollPos (hWnd, SB_VERT);
// Set up the scroll bars appropriately.
SetupScrollBars(hWnd, (WORD)bm.bmWidth, (WORD)bm.bmHeight);
// Set up the necessary rectangles -- i.e. the rectangle
// we're rendering into, and the rectangle in the bitmap
GetClientRect (hWnd, &rectClient);
rectDDB.left = xScroll;
rectDDB.top = yScroll;
rectDDB.right = xScroll + rectClient.right - rectClient.left;
rectDDB.bottom = yScroll + rectClient.bottom - rectClient.top;
if (rectDDB.right > bm.bmWidth)
{
int dx;
dx = bm.bmWidth - rectDDB.right;
rectDDB.right += dx;
rectClient.right += dx;
}
if (rectDDB.bottom > bm.bmHeight)
{
int dy;
dy = bm.bmHeight - rectDDB.bottom;
rectDDB.bottom += dy;
rectClient.bottom += dy;
}
// Go do the actual painting.
PaintBitmap(hDC, &rectClient, ghBitmap, &rectDDB, ghPal);
}
EndPaint(hWnd, &ps);
}
//****************************************************************************
//
// Function: FrameWindow()
//
// Purpose: Highlight the window frame
//
//
//***************************************************************************
void FrameWindow(HWND hWnd)
{
HDC hdc;
RECT rc;
#define DINV 3
if (!IsWindow(hWnd))
return;
hdc = GetWindowDC(hWnd);
GetWindowRect(hWnd, &rc);
OffsetRect(&rc, -rc.left, -rc.top);
if (!IsRectEmpty(&rc))
{
PatBlt(hdc, rc.left, rc.top, rc.right-rc.left, DINV, DSTINVERT);
PatBlt(hdc, rc.left, rc.bottom-DINV, DINV, -(rc.bottom-rc.top-2*DINV),
DSTINVERT);
PatBlt(hdc, rc.right-DINV, rc.top+DINV, DINV, rc.bottom-rc.top-2*DINV,
DSTINVERT);
PatBlt(hdc, rc.right, rc.bottom-DINV, -(rc.right-rc.left), DINV,
DSTINVERT);
}
ReleaseDC(hWnd, hdc);
}
//****************************************************************************
//
// FUNCTION: CenterWindow (HWND, HWND)
//
// PURPOSE: Center one window over another
//
// COMMENTS:
//
// Dialog boxes take on the screen position that they were designed at,
// which is not always appropriate. Centering the dialog over a particular
// window usually results in a better position.
//
//***************************************************************************
BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
{
RECT rChild, rParent;
int wChild, hChild, wParent, hParent;
int wScreen, hScreen, xNew, yNew;
HDC hdc;
// Get the Height and Width of the child window
GetWindowRect (hwndChild, &rChild);
wChild = rChild.right - rChild.left;
hChild = rChild.bottom - rChild.top;
// Get the Height and Width of the parent window
GetWindowRect (hwndParent, &rParent);
wParent = rParent.right - rParent.left;
hParent = rParent.bottom - rParent.top;
// Get the display limits
hdc = GetDC (hwndChild);
wScreen = GetDeviceCaps (hdc, HORZRES);
hScreen = GetDeviceCaps (hdc, VERTRES);
ReleaseDC (hwndChild, hdc);
// Calculate new X position, then adjust for screen
xNew = rParent.left + ((wParent - wChild) /2);
if (xNew < 0)
xNew = 0;
else if ((xNew+wChild) > wScreen)
xNew = wScreen - wChild;
// Calculate new Y position, then adjust for screen
yNew = rParent.top + ((hParent - hChild) /2);
if (yNew < 0)
yNew = 0;
else if ((yNew+hChild) > hScreen)
yNew = hScreen - hChild;
// Set it, and return
return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
}