If you'd like to see how Windows sends keyboard messages to a program, KEYLOOK, shown in Figure 3-3, will help. This program displays in its client area all the information that Windows sends the window procedure for the eight different keyboard messages.
KEYLOOK.MAK
#-----------------------
# KEYLOOK.MAK make file
#-----------------------
keylook.exe : keylook.obj keylook.def
link keylook, /align:16, NUL, /nod slibcew libw, keylook
rc keylook.exe
keylook.obj : keylook.c
cl -c -Gsw -Ow -W2 -Zp keylook.c
KEYLOOK.C
/*-------------------------------------------------------
KEYLOOK.C -- Displays Keyboard and Character Messages
(c) Charles Petzold, 1990
-------------------------------------------------------*/
#include <windows.h>
#include <stdio.h>
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
RECT rect ;
short cxChar, cyChar ;
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
static char szAppName[] = "KeyLook" ;
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 = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
RegisterClass (&wndclass) ;
}
hwnd = CreateWindow (szAppName, "Keyboard Message Looker",
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 ShowKey (HWND hwnd, int iType, char *szMessage, WORD wParam, LONG lParam)
{
static char *szFormat[2] = { "%-14s %3d %c %6u %4d %3s %3s %4s %4s",
"%-14s %3d %c %6u %4d %3s %3s %4s %4s" } ;
char szBuffer[80] ;
HDC hdc ;
ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ;
hdc = GetDC (hwnd) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
TextOut (hdc, cxChar, rect.bottom - cyChar, szBuffer,
wsprintf (szBuffer, szFormat [iType],
(LPSTR) szMessage, wParam,
(BYTE) (iType ? wParam : ' '),
LOWORD (lParam),
HIWORD (lParam) & 0xFF,
(LPSTR) (0x01000000 & lParam ? "Yes" : "No"),
(LPSTR) (0x20000000 & lParam ? "Yes" : "No"),
(LPSTR) (0x40000000 & lParam ? "Down" : "Up"),
(LPSTR) (0x80000000 & lParam ? "Up" : "Down"))) ;
ReleaseDC (hwnd, hdc) ;
ValidateRect (hwnd, NULL) ;
}
long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
{
static char szTop[] =
"Message Key Char Repeat Scan Ext ALT Prev Tran";
static char szUnd[]=
"_______ ___ ____ ______ ____ ___ ___ ____ ____";
HDC hdc ;
PAINTSTRUCT ps ;
TEXTMETRIC tm ;
switch (message)
{
case WM_CREATE :
hdc = GetDC (hwnd) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cyChar = tm.tmHeight ;
ReleaseDC (hwnd, hdc) ;
rect.top = 3 * cyChar / 2 ;
return 0 ;
case WM_SIZE :
rect.right = LOWORD (lParam) ;
rect.bottom = HIWORD (lParam) ;
UpdateWindow (hwnd) ;
return 0 ;
case WM_PAINT :
InvalidateRect (hwnd, NULL, TRUE) ;
hdc = BeginPaint (hwnd, &ps) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
SetBkMode (hdc, TRANSPARENT) ;
TextOut (hdc, cxChar, cyChar / 2, szTop, (sizeof szTop) - 1) ;
TextOut (hdc, cxChar, cyChar / 2, szUnd, (sizeof szUnd) - 1) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_KEYDOWN :
ShowKey (hwnd, 0, "WM_KEYDOWN", wParam, lParam) ;
return 0 ;
case WM_KEYUP :
ShowKey (hwnd, 0, "WM_KEYUP", wParam, lParam) ;
return 0 ;
case WM_CHAR :
ShowKey (hwnd, 1, "WM_CHAR", wParam, lParam) ;
return 0 ;
case WM_DEADCHAR :
ShowKey (hwnd, 1, "WM_DEADCHAR", wParam, lParam) ;
return 0 ;
case WM_SYSKEYDOWN :
ShowKey (hwnd, 0, "WM_SYSKEYDOWN", wParam, lParam) ;
break ; // i.e., call DefWindowProc
case WM_SYSKEYUP :
ShowKey (hwnd, 0, "WM_SYSKEYUP", wParam, lParam) ;
break ; // i.e., call DefWindowProc
case WM_SYSCHAR :
ShowKey (hwnd, 1, "WM_SYSCHAR", wParam, lParam) ;
break ; // i.e., call DefWindowProc
case WM_SYSDEADCHAR :
ShowKey (hwnd, 1, "WM_SYSDEADCHAR", wParam, lParam) ;
break ; // i.e., call DefWindowProc
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
KEYLOOK.DEF
;------------------------------------
; KEYLOOK.DEF module definition file
;------------------------------------
NAME KEYLOOK
DESCRIPTION 'Key Look Program (c) Charles Petzold, 1990'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
KEYLOOK uses the display like an old-fashioned teletype output device. When KEYLOOK receives a keystroke message, it calls ScrollWindow to scroll the contents of the entire client area of the window so that the contents move up the height of one character. TextOut is used to display the line of new information beginning one character height from the bottom. This is about as simple as a teletype output can get. Figure 3-4 shows what the KEYLOOK display looks like when you type the word ”Windows.“ The first column shows the keyboard message, the second shows the virtual key code for keystroke messages, the
third shows the character code (and the character itself) for character messages, and the other six columns show the states of the six fields in the lParam message parameter.
Most of KEYLOOK.C uses features of Windows that have already been covered in the various SYSMETS programs, but a few new functions are used here.
The column formatting of KEYLOOK would be difficult with the default proportional font. The code to display each line would need to be broken into nine sections to get everything lined up. For something like this, a much easier approach is to simply switch to a fixed-pitch font. This requires two functions in a single statement:
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
KEYLOOK calls these two functions whenever it obtains a device context. This occurs in three places: the ShowKey function, while processing the WM_CREATE message in WndProc, and while processing the WM_PAINT message. The GetStockObject function obtains a handle to a ”stock“ graphics object, which is a predefined graphics object that Windows makes available to programs. In this case, GetStockObject obtains a handle to a font known as SYSTEM_FIXED_FONT, which is the fixed-pitch font that was used in versions of Windows prior to Windows 3. The SelectObject call places that object into the device context. Following this call, all text that is displayed will use the fixed-pitch font. It is possible to switch back to the default proportional font by calling:
SelectObject (hdc, GetStockObject (SYSTEM_FONT)) ;
I'll discuss these functions in more depth in Chapter 12.
The ShowKey function calls ScrollWindow to scroll the previous lines of keystrokes up before displaying a new line. Normally this would cause part of the window to become invalid and hence generate a WM_PAINT message. The ShowKey function concludes with a call to ValidateRect to prevent this.
Notice the use of the Windows wsprintf function in the ShowKey function. The character strings must be explicitly cast to far pointers using the LPSTR data type (defined in WINDOWS.H as a far pointer to a character string). The wsprintf function is one of the very few functions in Windows that explicitly requires casting of its parameters.
KEYLOOK does not save the keystrokes it receives, so on receipt of a WM_PAINT message it cannot re-create the window. For this reason, KEYLOOK simply displays the header at the top of the client area during the WM_PAINT message. Before calling BeginPaint during the WM_PAINT message, KEYLOOK invalidates the entire window. This allows the whole window to be erased rather than just the invalid rectangle.
(That KEYLOOK does not save the keystrokes and hence cannot redraw the window during a WM_PAINT message is certainly a flaw. The TYPE program shown later in this chapter corrects this flaw.)
KEYLOOK draws a header at the top of the client area identifying the nine columns. Although it's possible to create an underlined font, I took a slightly different approach here. I defined two character string variables named szTop (which has the text) and szUnd (which has the underlining) and displayed both of them at the same position at the top of the window during the WM_PAINT message. Normally, Windows displays text in an ”opaque“ mode, meaning that Windows erases the character background area while displaying a character. This would cause the second character string (szUnd) to erase the first (szTop). To prevent this, switch the device context into the ”transparent“ mode:
SetBkMode (hdc, TRANSPARENT) ;