The Keyboard
While keyboards play a lesser role in Windows CE, they're still the best means of entering large volumes of information. Even on systems without a physical keyboard such as the Pocket PC, soft keyboards—controls that simulate keyboards on a touch screen—will most likely be available to the user. Given this, proper handling of keyboard input is critical to all but the most specialized of Windows CE applications. While I'll talk at length about soft keyboards later in the book, one point should be made here. To the application, input from a soft keyboard is no different from input from a traditional "hard" keyboard.
Input Focus
Under Windows operating systems, only one window at a time has the input focus. The focus window receives all keyboard input until it loses focus to another window. The system assigns the keyboard focus using a number of rules, but most often the focus window is the current active window. The active window, you'll recall, is the top-level window, the one with which the user is currently interacting. With rare exceptions, the active window also sits at the top of the Z-order; that is, it's drawn on top of all other windows in the system. In the Explorer, the user can change the active window by pressing Alt-Esc to switch between programs or by tapping on another top-level window's button on the task bar. The focus window is either the active window or one of its child windows.Under Windows, a program can determine which window has the input focus by calling
HWND GetFocus (void);
The focus can be changed to another window by calling
HWND SetFocus (HWND hWnd);
Under Windows CE, the target window of SetFocus is limited. The window being given the focus by SetFocus must have been created by the thread calling SetFocus. An exception to this rule occurs if the window losing focus is related to the window gaining focus by a parent/child or sibling relationship; in this case, the focus can be changed even if the windows were created by different threads.When a window loses focus, Windows sends a WM_KILLFOCUS message to that window informing it of its new state. The wParam parameter contains the handle of the window that will be gaining the focus. The window gaining focus receives a WM_SETFOCUS message. The wParam parameter of the WM_SETFOCUS message contains the handle of the window losing focus.Now for a bit of motherhood. Programs shouldn't change the focus window without some input from the user. Otherwise, the user can easily become confused. A proper use of SetFocus is to set the input focus to a child window (more than likely a control) contained in the active window. In this case, a window would respond to the WM_SETFOCUS message by calling SetFocus with the handle of a child window contained in the window to which the program wants to direct keyboard messages.
Keyboard Messages
Windows CE practices the same keyboard message processing as its larger desktop relations with a few small exceptions, which I cover shortly. When a key is pressed, Windows sends a series of messages to the focus window, typically beginning with a WM_KEYDOWN message. If the key pressed represents a character such as a letter or number, Windows follows the WM_KEYDOWN with a WM_CHAR message. (Some keys, such as function keys and cursor keys, don't represent characters, so WM_CHAR messages aren't sent in response to those keys. For those keys, a program must interpret the WM_KEYDOWN message to know when the keys are pressed.) When the key is released, Windows sends a WM_KEYUP message. If a key is held down long enough for the auto-repeat feature to kick in, multiple WM_KEYDOWN and WM_CHAR messages are sent for each auto-repeat until the key is released when the final WM_KEYUP message is sent. I used the word typically to qualify this process because if the Alt key is being held when another key is pressed, the messages I've just described are replaced by WM_SYSKEYDOWN, WM_SYSCHAR, and WM_SYSKEYUP messages.For all of these messages, the generic parameters wParam and lParam are used in mostly the same manner. For WM_KEYxx and WM_SYSKEYxx messages, the wParam value contains the virtual key value, indicating the key being pressed. All versions of Windows provide a level of indirection between the keyboard hardware and applications by translating the scan codes returned by the keyboard into virtual key values. You see a list of the VK_xx values and their associated keys in Table 3-1. While the table of virtual keys is extensive, not all keys listed in the table are present on Windows CE devices. For example, function keys, a mainstay on PC keyboards and listed in the virtual key table, aren't present on most Windows CE keyboards. In fact, a number of keys on a PC keyboard are left off the space-constrained Windows CE keyboards. A short list of the keys not typically used on Windows CE devices is presented in Figure 3-1. This list is meant to inform you that these keys might not exist, not to indicate that the keys never exist on Windows CE keyboards.
Constant | Value | Keyboard Equivalent |
---|---|---|
VK_LBUTTON | 01 | Stylus tap |
VK_RBUTTON | 02 | Mouse right button[*] |
VK_CANCEL | 03 | Control-break processing |
VK_RBUTTON | 04 | Mouse middle button[*] |
-- | 05–07 | Undefined |
VK_BACK | 08 | Backspace key |
VK_TAB | 09 | Tab key |
-- | 0A–0B | Undefined |
VK_CLEAR | 0C | Clear key |
Constant | Value | Keyboard Equivalent |
VK_RETURN | 0D | Enter key |
-- | 0E–0F | Undefined |
VK_SHIFT | 10 | Shift key |
VK_CONTROL | 11 | Ctrl key |
VK_MENU | 12 | Alt key |
VK_CAPITAL | 14 | Caps Lock key |
-- | 15–19 | Reserved for Kanji systems |
-- | 1A | Undefined |
VK_ESCAPE | 1B | Escape key |
-- | 1C–1F | Reserved for Kanji systems |
VK_SPACE | 20 | Spacebar |
VK_PRIOR | 21 | Page Up key |
VK_NEXT | 22 | Page Down key |
VK_END | 23 | End key |
VK_HOME | 24 | Home key |
VK_LEFT | 25 | Left Arrow key |
VK_UP | 26 | Up Arrow key |
VK_RIGHT | 27 | Right Arrow key |
VK_DOWN | 28 | Down Arrow key |
VK_SELECT | 29 | Select key |
-- | 2A | Original equipment manufacturer (OEM)–specific |
VK_EXECUTE | 2B | Execute key |
VK_SNAPSHOT | 2C | Print Screen key for Windows 3.0 and later |
[*]Mouse right and middle buttons are defined but are relevant only on a Windows CE system equipped with a mouse. [†]On some Windows CE systems, Delete is simulated with Shift-Backspace[‡]Many Windows CE Systems don’t have this key[\]These constants can be used only with GetKeyState and GetAsyncKeyState.[]These codes are used by the application launch keys on systems that have them. | ||
Constant | Value | Keyboard Equivalent |
VK_INSERT | 2D | Insert[‡] |
VK_DELETE | 2E | Delete[†] |
VK_HELP | 2F | Help key |
VK_0–VK_9 | 30–39 | 0–9 keys |
-- | 3A–40 | Undefined |
VK_A–VK_Z | 41–5A | A through Z keys |
VK_LWIN | 5B | Windows key |
VK_RWIN | 5C | Windows key[‡] |
VK_APPS | 5D | |
-- | 5E–5F | Undefined |
VK_NUMPAD0–9 | 60–69 | Numeric keypad 0–9 keys |
VK_MULTIPLY | 6A | Numeric keypad Asterisk (*) key |
VK_ADD | 6B | Numeric keypad Plus sign (+) key |
VK_SEPARATOR | 6C | Separator key |
VK_SUBTRACT | 6D | Numeric keypad Minus sign (-) key |
VK_DECIMAL | 6E | Numeric keypad Period (.) key |
VK_DIVIDE | 6F | Numeric keypad Slash mark (/) key |
VK_F1–VK_F24 | 70–87 | F1–F24[‡] |
-- | 88–8F | Unassigned |
VK_NUMLOCK | 90 | Num Lock[‡] |
VK_SCROLL | 91 | Scroll Lock[‡] |
-- | 92–9F | Unassigned |
VK_LSHIFT | A0 | Left Shift[\] |
VK_RSHIFT | A1 | Right Shift[\] |
VK_LCONTROL | A2 | Left Control[\] |
VK_RCONTROL | A3 | Right Control[\] |
VK_LMENU | A4 | Left Alt[\] |
VK_RMENU | A5 | Right Alt[\] |
-- | A6–B9 | Unassigned |
VK_SEMICOLON | BA | ; key |
VK_EQUAL | BB | = key |
VK_COMMA | BC | , key |
VK_HYPHEN | BD | - key |
VK_PERIOD | BE | . key |
VK_SLASH | BF | / key |
[*]Mouse right and middle buttons are defined but are relevant only on a Windows CE system equipped with a mouse. [†]On some Windows CE systems, Delete is simulated with Shift-Backspace[‡]Many Windows CE Systems don’t have this key[\]These constants can be used only with GetKeyState and GetAsyncKeyState.[]These codes are used by the application launch keys on systems that have them. | ||
Constant | Value | Keyboard Equivalent |
VK_BACKQUOTE | C0 | ` key |
-- | C1–DA | Unassigned[] |
VK_LBRACKET | DB | [ key |
VK_BACKSLASH | DC | \ key |
VK_RBRACKET | DD | ] key |
VK_APOSTROPHE | DE | ' key |
VK_OFF | DF | Power button |
-- | E5 | Unassigned |
-- | E6 | OEM-specific |
-- | E7–E8 | Unassigned |
-- | E9–F5 | OEM-specific |
VK_ATTN | F6 | |
VK_CRSEL | F7 | |
VK_EXSEL | F8 | |
VK_EREOF | F9 | |
VK_PLAY | FA | |
VK_ZOOM | FB | |
VK_NONAME | FC | |
VK_PA1 | FD | |
VK_OEM_CLEAR | FE | |
[*]Mouse right and middle buttons are defined but are relevant only on a Windows CE system equipped with a mouse. [†]On some Windows CE systems, Delete is simulated with Shift-Backspace[‡]Many Windows CE Systems don’t have this key[\]These constants can be used only with GetKeyState and GetAsyncKeyState.[]These codes are used by the application launch keys on systems that have them. |
For the WM_CHAR and WM_SYSCHAR messages, the wParam value contains the Unicode character represented by the key. Most often an application can simply look for WM_CHAR messages and ignore WM_KEYDOWN and WM_ KEYUP. The WM_CHAR message allows for a second level of abstraction so that the application doesn't have to worry about the up or down state of the keys and can concentrate on the characters being entered by means of the keyboard.The lParam value of any of these keyboard messages contains further information about the pressed key. The format of the lParam parameter is shown in Figure 3-2.

Figure 3-1: Keys on a PC keyboard that are rarely on a Windows CE keyboard
The low word, bits 0 through 15, contains the repeat count of the key. Sometimes keys on a Windows CE device can be pressed faster than Windows CE can send messages to the focus application. In these cases, the repeat count contains the number of times the key has been pressed. Bit 29 contains the context flag. If the Alt key was being held down when the key was pressed, this bit will be set. Bit 30 contains the previous key state. If the key was previously down, this bit is set; otherwise, it's 0. Bit 30 can be used to determine whether the key message is the result of an auto-repeat sequence. Bit 31 indicates the transition state. If the key is in transition from down to up, Bit 31 is set. Bits 16 through 28 are used to indicate the key scan code. In many cases, Windows CE doesn't support this field. However, on some of the newer Windows CE platforms where scan codes are necessary, this field does contain the scan code. You shouldn't plan on the scan code field being available unless you know it's supported on your specific platform.

Figure 3-2: The layout of the lParam value for key messages
One additional keyboard message, WM_DEADCHAR, can sometimes come into play. You send it when the pressed key represents a dead character, such as an umlaut, that you want to combine with a character to create a different character. In this case, the WM_DEADCHAR message can be used to prevent the text entry point (the caret) from advancing to the next space until the second key is pressed so that you can complete the combined character.The WM_DEADCHAR message has always been present under Windows, but under Windows CE it takes on a somewhat larger role. With the internationalization of small consumer devices that run Windows CE, programmers should plan for, and if necessary use, the WM_DEADCHAR message that is so often necessary in foreign language systems.
Keyboard Functions
You'll find useful a few other keyboard state–determining functions for Windows applications. Among the keyboard functions, two are closely related but often confused: GetKeyState and GetAsyncKeyState.GetKeyState, prototyped as
SHORT GetKeyState (int nVirtKey);
returns the up/down state of the shift keys, Ctrl, Alt, and Shift, and indicates whether any of these keys is in a toggled state. If the keyboard has two keys with the same function—for example, two Shift keys, one on each side of the keyboard—this function can also be used to differentiate which of them is being pressed. (Most keyboards have left and right Shift keys, and some include left and right Ctrl and Alt keys.)You pass to the function the virtual key code for the key being queried. If the high bit of the return value is set, the key is down. If the least significant bit of the return value is set, the key is in a toggled state; that is, it has been pressed an odd number of times since the system was started. The state returned is the state at the time the most recent message was read from the message queue, which isn't necessarily the real-time state of the key. An interesting aside: notice that the virtual key label for the Alt key is VK_MENU, which relates to the windows convention that the Alt-Shift key combination works in concert with other keys to access various menus from the keyboard.Note that the GetKeyState function is limited under Windows CE to querying the state of the shift keys. Under other versions of Windows, GetKeyState can determine the state of every key on the keyboard.To determine the real-time state of a key, use
SHORT GetAsyncKeyState (int vKey);
As with GetKeyState, you pass to this function the virtual key code for the key being queried. The GetAsyncKeyState function returns a value subtly different from the one returned by GetKeyState. As with the GetKeyState function, the high bit of the return value is set while the key is being pressed. However, the least significant bit is then set if the key was pressed after a previous call to GetAsyncKeyState. Like GetKeyState, the GetAsyncKeyState function can distinguish the left and right Shift, Ctrl, and Alt keys. In addition, by passing the VK_LBUTTON virtual key value, GetAsyncKeyState determines whether the stylus is currently touching the screen.An application can simulate a keystroke using the keybd_event function:
VOID keybd_event (BYTE bVk, BYTE bScan, DWORD dwFlags,
DWORD dwExtraInfo);
The first parameter is the virtual key code of the key to simulate. The bScan code should be set to NULL under Windows CE. The dwFlags parameter can have two possible flags: KEYEVENTF_KEYUP indicates that the call is to emulate a key up event, while KEYEVENTF_SILENT indicates that the simulated key press won't cause the standard keyboard click that you normally hear when you press a key. So to fully simulate a key press, keybd_event should be called twice, once without KEYEVENTF_KEYUP to simulate a key down, and then once again, this time with KEYEVENTF_KEYUP to simulate the key release. When simulating a shift key, specify the specific left or right VK code, as in VK_LSHIFT or VF_RCONTROL.A function unique to Windows CE is
BOOL PostKeybdMessage (HWND hwnd, UINT VKey,
KEY_STATE_FLAGS KeyStateFlags,
UINT cCharacters, UINT *pShiftStateBuffer,
UINT *pCharacterBuffer );
This function sends a series of keys to the specified window. The hwnd parameter is the target window. This window must be owned by the calling thread. The VKey parameter should be zero. KeyStateFlags specifies the key state for all the keys being sent. The cCharacters parameter specifies the number of keys being sent. The pShiftStateBuffer parameter points to an array that contains a shift state for each key sent, while pCharacterBuffer points to the VK codes of the keys being sent. Unlike keybd_event, this function doesn't change the global state of the keyboard.One final keyboard function, MapVirtualKey, translates virtual key codes to characters. MapVirtualKey in Windows CE doesn't translate keyboard scan codes to and from virtual key codes, although it does so in other versions of Windows. The prototype of the function is the top of the following page.
UINT MapVirtualKey (UINT uCode, UINT uMapType);
Under Windows CE, the first parameter is the virtual key code to be translated, while the second parameter, uMapType, indicates how the key code is translated. MapVirtualKey is dependent on the keyboard device driver implementing a supporting function. Many OEMs don't implement this supporting function, so on their systems, MapVirtualKey fails.
Testing for the Keyboard
To determine whether a keyboard is even present in the system, you can call
DWORD GetKeyboardStatus (VOID);
This function returns the KBDI_KEYBOARD_PRESENT flag if a hardware keyboard is present in the system. This function also returns a KBDI_KEYBOARD_ENABLED flag if the keyboard is enabled. To disable the keyboard, a call can be made to
BOOL EnableHardwareKeyboard (BOOL bEnable);
with the bEnable flag set to FALSE. You might want to disable the keyboard in a system for which the keyboard folds around behind the screen; in such a system, a user could accidentally hit keys while using the stylus.
The KeyTrac Example Program
The following example program, KeyTrac, displays the sequence of keyboard messages. Programmatically, KeyTrac isn't much of a departure from the earlier programs in the book. The difference is that the keyboard messages I've been describing are all trapped and recorded in an array that's then displayed during the WM_PAINT message. For each keyboard message, the message name is recorded along with the wParam and lParam values and a set of flags indicating the state of the shift keys. The key messages are recorded in an array because these messages can occur faster than the redraw can occur. Figure 3-3 shows the KeyTrac window after a few keys have been pressed.

Figure 3-3: The KeyTrac window after a Shift-A key combination followed by a lowercase a key press
The best way to learn about the sequence of the keyboard messages is to run KeyTrac, press a few keys, and watch the messages scroll down the screen. Pressing a character key such as the a results in three messages: WM_KEYDOWN, WM_CHAR, and WM_KEYUP. Holding down the Shift key while pressing the a and then releasing the Shift key produces a key-down message for the Shift key followed by the three messages for the a key followed by a key-up message for the Shift key. Because the Shift key itself isn't a character key, no WM_CHAR message is sent in response to it. However, the WM_CHAR message for the a key now contains a 0x41 in the wParam value, indicating that an uppercase A was entered instead of a lowercase a.Listing 3-1 shows the source code for the KeyTrac program.Listing 3-1: The KeyTrac program
KeyTrac.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Program-specific defines and structures
//
typedef struct {
UINT wKeyMsg;
INT wParam;
INT lParam;
LPCTSTR pszMsgTxt;
TCHAR szShift[20];
} MYKEYARRAY, *PMYKEYARRAY;
// Structure to associate messages with text name of message
typedef struct {
UINT wMsg;
LPCTSTR pName;
} KEYNAMESTRUCT;
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoCreateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoPaintMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoKeysMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);
KeyTrac.cpp
//======================================================================
// KeyTrac - displays keyboard messages
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <commctrl.h> // Command bar includes
#include "keytrac.h" // Program-specific stuff
// The include and lib files for the Pocket PC are conditionally
// included so that this example can share the same project file. This
// is necessary since this example must have a menu bar on the Pocket
// PC to have a SIP button.
#if defined(WIN32_PLATFORM_PSPC)
#include <aygshell.h> // Add Pocket PC includes.
#pragma comment( lib, "aygshell" ) // Link Pocket PC lib for menu bar.
#endif
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("KeyTrac");
HINSTANCE hInst; // Program instance handle
// Program-specific global data
MYKEYARRAY ka[16];
int nKeyCnt = 0;
int nFontHeight;
// Array associates key messages with text tags
KEYNAMESTRUCT knArray[] = {{WM_KEYDOWN, TEXT ("WM_KEYDOWN")},
{WM_KEYUP, TEXT ("WM_KEYUP")},
{WM_CHAR, TEXT ("WM_CHAR")},
{WM_SYSCHAR, TEXT ("WM_SYSCHAR")},
{WM_SYSKEYUP, TEXT ("WM_SYSKEYUP")},
{WM_SYSKEYDOWN, TEXT ("WM_SYSKEYDOWN")},
{WM_DEADCHAR, TEXT ("WM_DEADCHAR")},
{WM_SYSDEADCHAR, TEXT ("WM_SYSDEADCHAR")}};
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_CREATE, DoCreateMain,
WM_PAINT, DoPaintMain,
WM_KEYUP, DoKeysMain,
WM_KEYDOWN, DoKeysMain,
WM_CHAR, DoKeysMain,
WM_DEADCHAR, DoKeysMain,
WM_SYSCHAR, DoKeysMain,
WM_SYSDEADCHAR, DoKeysMain,
WM_SYSKEYDOWN, DoKeysMain,
WM_SYSKEYUP, DoKeysMain,
WM_DESTROY, DoDestroyMain,
};
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
MSG msg;
int rc = 0;
HWND hwndMain;
// Initialize this instance.
hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndMain == 0)
return 0x10;
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
//----------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc;
HWND hWnd;
#if defined(WIN32_PLATFORM_PSPC)
// If Pocket PC, allow only one instance of the application
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
#endif
hInst = hInstance; // Save program instance handle
// Register application main window class.
wc.style = 0; // Window style
wc.lpfnWndProc = MainWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass(&wc) == 0) return 0;
// Create main window.
hWnd = CreateWindowEx (WS_EX_NODRAG, szAppName, TEXT ("KeyTrac"),
WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
// Fail if window not created
if (!IsWindow (hWnd)) return 0;
// Standard show and update calls
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Message handling procedures for MainWindow
//
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateMain - Process WM_CREATE message for window.
//
LRESULT DoCreateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
HDC hdc;
TEXTMETRIC tm;
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
SHMENUBARINFO mbi; // For Pocket PC, create
memset(&mbi, 0, sizeof(SHMENUBARINFO)); // menu bar so that we
mbi.cbSize = sizeof(SHMENUBARINFO); // have a sip button
mbi.hwndParent = hWnd;
mbi.dwFlags = SHCMBF_EMPTYBAR; // No menu
SHCreateMenuBar(&mbi);
#endif
// Get the height of the default font.
hdc = GetDC (hWnd);
GetTextMetrics (hdc, &tm);
nFontHeight = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC (hWnd, hdc);
return 0;
}
//----------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PAINTSTRUCT ps;
RECT rect, rectOut;
TCHAR szOut[256];
HDC hdc;
INT i, j;
LPCTSTR pKeyText;
GetClientRect (hWnd, &rect);
// Create a drawing rectangle for the top line of the window.
rectOut = rect;
rectOut.bottom = rectOut.top + nFontHeight;
hdc = BeginPaint (hWnd, &ps);
if (nKeyCnt) {
for (i = 0; i < nKeyCnt; i++) {
// Create string containing wParam, lParam, and shift data.
wsprintf (szOut, TEXT ("wP:%08x lP:%08x shift: %s"),
ka[i].wParam, ka[i].lParam, ka[i].szShift);
// Look up name of key message.
for (j = 0; j < dim (knArray); j++)
if (knArray[j].wMsg == ka[i].wKeyMsg)
break;
// See if we found the message.
if (j < dim (knArray))
pKeyText = knArray[j].pName;
else
pKeyText = TEXT ("Unknown");
// Scroll the window one line.
ScrollDC (hdc, 0, nFontHeight, &rect, &rect, NULL, NULL);
// See if wide or narrow screen.
if (GetSystemMetrics (SM_CXSCREEN) < 480) {
// If Pocket PC, display info on 2 lines
ExtTextOut (hdc, 10, rect.top, ETO_OPAQUE, &rectOut,
szOut, lstrlen (szOut), NULL);
// Scroll the window another line.
ScrollDC(hdc, 0, nFontHeight, &rect, &rect, NULL, NULL);
ExtTextOut (hdc, 5, rect.top, ETO_OPAQUE, &rectOut,
pKeyText, lstrlen (pKeyText), NULL);
} else {
// Wide screen, print all on one line.
ExtTextOut (hdc, 5, rect.top, ETO_OPAQUE, &rectOut,
pKeyText, lstrlen (pKeyText), NULL);
ExtTextOut (hdc, 100, rect.top, 0, NULL,
szOut, lstrlen (szOut), NULL);
}
}
nKeyCnt = 0;
}
EndPaint (hWnd, &ps);
return 0;
}
//----------------------------------------------------------------------
// DoKeysMain - Process all keyboard messages for window.
//
LRESULT DoKeysMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
if (nKeyCnt >= 16) return 0;
ka[nKeyCnt].wKeyMsg = wMsg;
ka[nKeyCnt].wParam = wParam;
ka[nKeyCnt].lParam = lParam;
// Capture the state of the shift flags.
ka[nKeyCnt].szShift[0] = TEXT ('\0');
if (GetKeyState (VK_LMENU))
lstrcat (ka[nKeyCnt].szShift, TEXT ("lA "));
if (GetKeyState (VK_RMENU))
lstrcat (ka[nKeyCnt].szShift, TEXT ("rA "));
if (GetKeyState (VK_MENU))
lstrcat (ka[nKeyCnt].szShift, TEXT ("A "));
if (GetKeyState (VK_LCONTROL))
lstrcat (ka[nKeyCnt].szShift, TEXT ("lC "));
if (GetKeyState (VK_RCONTROL))
lstrcat (ka[nKeyCnt].szShift, TEXT ("rC "));
if (GetKeyState (VK_CONTROL))
lstrcat (ka[nKeyCnt].szShift, TEXT ("C "));
if (GetKeyState (VK_LSHIFT))
lstrcat (ka[nKeyCnt].szShift, TEXT ("lS "));
if (GetKeyState (VK_RSHIFT))
lstrcat (ka[nKeyCnt].szShift, TEXT ("rS "));
if (GetKeyState (VK_SHIFT))
lstrcat (ka[nKeyCnt].szShift, TEXT ("S "));
nKeyCnt++;
InvalidateRect (hWnd, NULL, FALSE);
return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}
Here are a few more characteristics of KeyTrac to notice. After each keyboard message is recorded, an InvalidateRect functionChapter 2, a program should never attempt to send or post a WM_PAINT message to a window because Windows needs to perform some setup before it calls a window with a WM_PAINT message.Another device context function used in KeyTrac is
BOOL ScrollDC (HDC hDC, int dx, int dy, const RECT *lprcScroll,
const RECT *lprcClip, HRGN hrgnUpdate,
LPRECT lprcUpdate);
which scrolls an area of the device context either horizontally or vertically, but under Windows CE, not both directions at the same time. The three rectangle parameters define the area to be scrolled, the area within the scrolling area to be clipped, and the area to be painted after the scrolling ends. Alternatively, a handle to a region can be passed to ScrollDC. That region is defined by ScrollDC to encompass the region that needs painting after the scroll.Finally, if the KeyTrac window is covered up for any reason and then reexposed, the message information on the display is lost. This behavior occurs because a device context doesn't store the bit information of the display. The application is responsible for saving any information necessary to completely restore the client area of the screen. Since KeyTrac doesn't save this information, it's lost when the window is covered up.