Reputation: 620
I am writing a program for displaying all the information that the windows sends the window procedure for 8 different keyboard messages. Here is the code
/*--------------------------------------------------------
KEYVIEW1.C -- Displays Keyboard and Character Messages
(c) Charles Petzold, 1998
--------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("KeyView1") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
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 ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Keyboard Message Viewer #1"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxClientMax, cyClientMax, cxClient, cyClient, cxChar, cyChar ;
static int cLinesMax, cLines ;
static PMSG pmsg ;
static RECT rectScroll ;
static TCHAR szTop[] = TEXT ("Message Key Char ")
TEXT ("Repeat Scan Ext ALT Prev Tran") ;
static TCHAR szUnd[] = TEXT ("_______ ___ ____ ")
TEXT ("______ ____ ___ ___ ____ ____") ;
static TCHAR * szFormat[2] = {
TEXT ("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"),
TEXT ("%-13s 0x%04X%1s%c %6u %4d %3s %3s %4s %4s") } ;
static TCHAR * szYes = TEXT ("Yes") ;
static TCHAR * szNo = TEXT ("No") ;
static TCHAR * szDown = TEXT ("Down") ;
static TCHAR * szUp = TEXT ("Up") ;
static TCHAR * szMessage [] = {
TEXT ("WM_KEYDOWN"), TEXT ("WM_KEYUP"),
TEXT ("WM_CHAR"), TEXT ("WM_DEADCHAR"),
TEXT ("WM_SYSKEYDOWN"), TEXT ("WM_SYSKEYUP"),
TEXT ("WM_SYSCHAR"), TEXT ("WM_SYSDEADCHAR") } ;
HDC hdc ;
int i, iType ;
PAINTSTRUCT ps ;
TCHAR szBuffer[128], szKeyName [32] ;
TEXTMETRIC tm ;
switch (message)
{
case WM_CREATE:
case WM_DISPLAYCHANGE:
// Get maximum size of client area
cxClientMax = GetSystemMetrics (SM_CXMAXIMIZED) ;
cyClientMax = GetSystemMetrics (SM_CYMAXIMIZED) ;
// Get character size for fixed-pitch font
hdc = GetDC (hwnd) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cyChar = tm.tmHeight ;
ReleaseDC (hwnd, hdc) ;
// Allocate memory for display lines
if (pmsg)
free (pmsg) ;
cLinesMax = cyClientMax / cyChar ;
pmsg = (PMSG)malloc (cLinesMax * sizeof (MSG)) ;
cLines = 0 ;
// fall through
case WM_SIZE:
if (message == WM_SIZE)
{
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
}
// Calculate scrolling rectangle
rectScroll.left = 0 ;
rectScroll.right = cxClient ;
rectScroll.top = cyChar ;
rectScroll.bottom = cyChar * (cyClient / cyChar) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case WM_KEYDOWN:
case WM_KEYUP:
case WM_CHAR:
case WM_DEADCHAR:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_SYSCHAR:
case WM_SYSDEADCHAR:
// Rearrange storage array
for (i = cLinesMax - 1 ; i > 0 ; i--)
{
pmsg[i] = pmsg[i - 1] ;
}
// Store new message
pmsg[0].hwnd = hwnd ;
pmsg[0].message = message ;
pmsg[0].wParam = wParam ;
pmsg[0].lParam = lParam ;
cLines = min (cLines + 1, cLinesMax) ;
// Scroll up the display
ScrollWindow (hwnd, 0, -cyChar, &rectScroll, &rectScroll) ;
break ; // ie, call DefWindowProc so Sys messages work
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
SetBkMode (hdc, TRANSPARENT) ;
TextOut (hdc, 0, 0, szTop, lstrlen (szTop)) ;
TextOut (hdc, 0, 0, szUnd, lstrlen (szUnd)) ;
for (i = 1 ; i < max (cLines, cyClient / cyChar - 1) ; i++)
{
iType = pmsg[i].message == WM_CHAR ||
pmsg[i].message == WM_SYSCHAR ||
pmsg[i].message == WM_DEADCHAR ||
pmsg[i].message == WM_SYSDEADCHAR ;
GetKeyNameText (pmsg[i].lParam, szKeyName,
sizeof (szKeyName) / sizeof (TCHAR)) ;
TextOut (hdc, 0, (cyClient / cyChar - i) * cyChar, szBuffer,
wsprintf (szBuffer, szFormat [iType],
szMessage [pmsg[i].message - WM_KEYFIRST],
pmsg[i].wParam,
(PTSTR) (iType!=0 ? TEXT (" ") : szKeyName),
(TCHAR) (iType ? pmsg[i].wParam : ' '),
LOWORD (pmsg[i].lParam),
HIWORD (pmsg[i].lParam) & 0xFF,
0x01000000 & pmsg[i].lParam ? szYes : szNo,,// for extended key flag
0x20000000 & pmsg[i].lParam ? szYes : szNo,
0x40000000 & pmsg[i].lParam ? szDown : szUp,//Previous key state
0x80000000 & pmsg[i].lParam ? szUp : szDown)) ;//transition state
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
My problem is that I can't understand the code.I tried my best .
In the line
static TCHAR * szFormat[2] = {
TEXT ("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"), TEXT ("%-13s 0x%04X%1s%c %6u %4d %3s %3s %4s %4s") } ;
what is happening ?I understood the first line but not the second one.
in TextOut function
what does szMessage [pmsg[i].message - WM_KEYFIRST],
means
and what the codes used
0x01000000
and why we used specifically &0XFF with HIWORD (pmsg[i].lParam) & 0xFF,
I know these are lot of questions but none of them were explained in the book .Please could anyone answer atleast one of the questions. Any help would be appreciated
Upvotes: 0
Views: 379
Reputation: 244772
In the line
static TCHAR * szFormat[2] = { TEXT ("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"), TEXT ("%-13s 0x%04X%1s%c %6u %4d %3s %3s %4s %4s") } ;
what is happening? I understood the first line but not the second one.
This is not really Windows-specific. It is just an array of C-style strings, which are themselves pointers to a nul-terminated array of characters.
It is declared static
so that it is only initialized once when the function is first called, and maintains its value across subsequent calls.
TCHAR
is a Windows typedef for either char
or wchar_t
, depending on whether or not you're building a Unicode application. So TCHAR *
is equivalent to either char *
or wchar_t *
.
The name szFormat
is systems hungarian notation, common in Win32 programming and used extensively by Petzold. The sz
means it is a nul (zero)-terminated string.
The [2]
part just makes it an array that contains two elements. An array that itself contains arrays.
TEXT
is a function-like macro that is used to declare string literals appropriately, depending on the build configuration. In Unicode builds, an L
is appended to the beginning of the string literal to make it a wide string. In non-Unicode builds, nothing is appended.
The strings themselves are just format strings, like you might use with the printf
function in standard C. Except that Petzold's code is using it with wsprintf
, the Windows version of this C function. Basically, all of the %
parts are placeholders that are filled in with the values of variables you specify. They have special decorations to provide more extensive control over the formatting of the output string.
case WM_SIZE: if (message == WM_SIZE) { cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; }
What does
message == WM_SIZE
mean?
I have no idea. That looks like a bug to me, actually. Inside of the WM_SIZE
case, you already know that message
is WM_SIZE
. It is therefore pointless to check it again. Just pretend it isn't there.
what does
szMessage [pmsg[i].message - WM_KEYFIRST],
mean?
From the name and the variable declaration at the top, you know that szMessage
is an array of C-style strings, just like we saw with szFormat
. It contains the names of the window messages he's interested in, like WM_KEYDOWN
and WM_KEYUP
.
The stuff in brackets is an indexer into the array. It is just as if he had written szMessage[1]
to access the second element in the array, except he's calculating the index of the item to be accessed dynamically using an expression.
The expression is built so that it matches the message name in the array up with the message ID known at runtime. You get the message ID from pmsg[i].message
, and then subtract it from WM_KEYFIRST
because that is the ID of the first keyboard message. That makes the message ID sync up the order of the message names in the string array.
and what the codes used
0x01000000
and why we used specifically &0XFF withHIWORD (pmsg[i].lParam) & 0xFF,
Those are just bit masks. Combining them with a bit value using the &
operator (bitwise AND) masks off the specified bits.
As for their meaning, that is all covered in the documentation for the particular window message. For example, in the WM_KEYDOWN
docs, it tells you the meaning of specific bits in the lParam
value. By masking off those bits, you can isolate them to get at the individual pieces of data.
Petzold is taking advantage of the fact that all of the keyboard-related messages he's monitoring (listed in the szMessage
array we just talked about) all use the same formatting for the lParam
value. This is verifiable by looking at the respective documentation for each of those messages. For example, WM_CHAR
is the same as we just saw for WM_KEYDOWN
. That allows the code to be vastly simplified.
The purpose for this, of course, is to display this information about keyboard events to the user each time the window gets painted.
Upvotes: 5