Wouldn't it be better to simply translate each of these WM_KEYDOWN messages into an equivalent WM_VSCROLL and WM_HSCROLL message and then perhaps fool WndProc into thinking that it's getting a WM_VSCROLL or WM_HSCROLL message, perhaps by sending a phony scroll bar message to the window procedure? Windows lets you do this. The function is called SendMessage, and it takes the same parameters as those passed to the window procedure:
SendMessage (hwnd, message, wParam, lParam) ;
When you call SendMessage, Windows calls the window procedure whose window handle is hwnd, passing to it these four parameters. When the window procedure has completed processing the message, Windows returns control to the next statement following the SendMessage call. The window procedure to which you send the message could be the same window procedure, another window procedure in the same program, or a window procedure in another application.
Here's how we might use SendMessage for processing WM_KEYDOWN codes in the SYSMETS program:
case WM_KEYDOWN :
switch (wParam)
{
case VK_HOME :
SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0L) ;
break ;
case VK_END :
SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0L) ;
break ;
case VK_PRIOR :
SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0L) ;
break ;
[other program lines]
OK, you get the general idea. Our goal was to add a keyboard interface to the scroll bars, and that's exactly what we've done. We've made the cursor movement keys duplicate scroll bar logic by actually sending the window procedure a scroll bar message. Now you see why I included SB_TOP and SB_BOTTOM processing for WM_VSCROLL messages in the SYSMETS3 program. It wasn't used then, but it's used now for processing the Home and End keys. The final SYSMETS program, shown in Figure 3-2, incorporates these changes. You'll also need the SYSMETS.H file from Chapter 2 (Figure 2-4) to compile this program.
Remember: To send a message to a window procedure, use the SendMessage function. Do not try to call the window procedure directly like this:
WndProc (hwnd, WM_VSCROLL, SB_PAGEUP, 0L) ; // WRONG !!!
This statement will cause ”unpredictable results“ (if you call a system crash ”unpredictable“). You may define and call other subroutines within a Windows program, but you must not call a window procedure directly. You'll find out why in Chapter 7.
SYSMETS.MAK
#-----------------------
# SYSMETS.MAK make file
#-----------------------
sysmets.exe : sysmets.obj sysmets.def
link sysmets, /align:16, NUL, /nod slibcew libw, sysmets
rc sysmets.exe
sysmets.obj : sysmets.c sysmets.h
cl -c -Gsw -Ow -W2 -Zp sysmets.c
SYSMETS.C
/*-----------------------------------------------------
SYSMETS.C -- System Metrics Display Program (Final)
(c) Charles Petzold, 1990
-----------------------------------------------------*/
#include <windows.h>
#include "sysmets.h"
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
static char szAppName[] = "SysMets" ;
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 = GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
RegisterClass (&wndclass) ;
}
hwnd = CreateWindow (szAppName, "System Metrics",
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
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 ;
}
long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
{
static short cxChar, cxCaps, cyChar, cxClient, cyClient, nMaxWidth,
nVscrollPos, nVscrollMax, nHscrollPos, nHscrollMax ;
char szBuffer[10] ;
HDC hdc ;
short i, x, y, nPaintBeg, nPaintEnd, nVscrollInc, nHscrollInc ;
PAINTSTRUCT ps ;
TEXTMETRIC tm ;
switch (message)
{
case WM_CREATE :
hdc = GetDC (hwnd) ;
GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
cyChar = tm.tmHeight + tm.tmExternalLeading ;
ReleaseDC (hwnd, hdc) ;
nMaxWidth = 40 * cxChar + 18 * cxCaps ;
return 0 ;
case WM_SIZE :
cyClient = HIWORD (lParam) ;
cxClient = LOWORD (lParam) ;
nVscrollMax = max (0, NUMLINES + 2 - cyClient / cyChar) ;
nVscrollPos = min (nVscrollPos, nVscrollMax) ;
SetScrollRange (hwnd, SB_VERT, 0, nVscrollMax, FALSE) ;
SetScrollPos (hwnd, SB_VERT, nVscrollPos, TRUE) ;
nHscrollMax = max (0, 2 + (nMaxWidth - cxClient) / cxChar) ;
nHscrollPos = min (nHscrollPos, nHscrollMax) ;
SetScrollRange (hwnd, SB_HORZ, 0, nHscrollMax, FALSE) ;
SetScrollPos (hwnd, SB_HORZ, nHscrollPos, TRUE) ;
return 0 ;
case WM_VSCROLL :
switch (wParam)
{
case SB_TOP :
nVscrollInc = -nVscrollPos ;
break ;
case SB_BOTTOM :
nVscrollInc = nVscrollMax - nVscrollPos ;
break ;
case SB_LINEUP :
nVscrollInc = -1 ;
break ;
case SB_LINEDOWN :
nVscrollInc = 1 ;
break ;
case SB_PAGEUP :
nVscrollInc = min (-1, -cyClient / cyChar) ;
break ;
case SB_PAGEDOWN :
nVscrollInc = max (1, cyClient / cyChar) ;
break ;
case SB_THUMBTRACK :
nVscrollInc = LOWORD (lParam) - nVscrollPos ;
break ;
default :
nVscrollInc = 0 ;
}
if (nVscrollInc = max (-nVscrollPos,
min (nVscrollInc, nVscrollMax - nVscrollPos)))
{
nVscrollPos += nVscrollInc ;
ScrollWindow (hwnd, 0, -cyChar * nVscrollInc, NULL, NULL) ;
SetScrollPos (hwnd, SB_VERT, nVscrollPos, TRUE) ;
UpdateWindow (hwnd) ;
}
return 0 ;
case WM_HSCROLL :
switch (wParam)
{
case SB_LINEUP :
nHscrollInc = -1 ;
break ;
case SB_LINEDOWN :
nHscrollInc = 1 ;
break ;
case SB_PAGEUP :
nHscrollInc = -8 ;
break ;
case SB_PAGEDOWN :
nHscrollInc = 8 ;
break ;
case SB_THUMBPOSITION :
nHscrollInc = LOWORD (lParam) - nHscrollPos ;
break ;
default :
nHscrollInc = 0 ;
}
if (nHscrollInc = max (-nHscrollPos,
min (nHscrollInc, nHscrollMax - nHscrollPos)))
{
nHscrollPos += nHscrollInc ;
ScrollWindow (hwnd, -cxChar * nHscrollInc, 0, NULL, NULL) ;
SetScrollPos (hwnd, SB_HORZ, nHscrollPos, TRUE) ;
}
return 0 ;
case WM_KEYDOWN :
switch (wParam)
{
case VK_HOME :
SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0L) ;
break ;
case VK_END :
SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0L) ;
break ;
case VK_PRIOR :
SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0L) ;
break ;
case VK_NEXT :
SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L) ;
break ;
case VK_UP :
SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0L) ;
break ;
case VK_DOWN :
SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0L) ;
break ;
case VK_LEFT :
SendMessage (hwnd, WM_HSCROLL, SB_PAGEUP, 0L) ;
break ;
case VK_RIGHT :
SendMessage (hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L) ;
break ;
}
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
nPaintBeg = max (0, nVscrollPos + ps.rcPaint.top / cyChar - 1) ;
nPaintEnd = min (NUMLINES,
nVscrollPos + ps.rcPaint.bottom / cyChar) ;
for (i = nPaintBeg ; i < nPaintEnd ; i++)
{
x = cxChar * (1 - nHscrollPos) ;
y = cyChar * (1 - nVscrollPos + i) ;
TextOut (hdc, x, y,
sysmetrics[i].szLabel,
lstrlen (sysmetrics[i].szLabel)) ;
TextOut (hdc, x + 18 * cxCaps, y,
sysmetrics[i].szDesc,
lstrlen (sysmetrics[i].szDesc)) ;
SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
TextOut (hdc, x + 18 * cxCaps + 40 * cxChar, y,
szBuffer,
wsprintf (szBuffer, "%5d",
GetSystemMetrics (sysmetrics[i].nIndex))) ;
SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
SYSMETS.DEF
;------------------------------------
; SYSMETS.DEF module definition file
;------------------------------------
NAME SYSMETS
DESCRIPTION 'System Metrics Display (c) Charles Petzold, 1990'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc