现在的位置: 首页 > 综合 > 正文

[翻译]-WinCE 程序设计 (3rd 版)–3.1 键盘

2012年05月22日 ⁄ 综合 ⁄ 共 16165字 ⁄ 字号 评论关闭

键盘
虽然键盘在Windows CE中作用减少了,但键盘依然是录入大量信息的最好方法。即使像在Pocket PC这类没有物理键盘的系统上,用户使用最多的也还是软键盘--在触摸屏上模拟键盘的控件。基于此,除了极其特殊的Windows CE应用程序外,对键盘输入的适当操作是很重要的。虽然在本书后面章节我会详细讨论软键盘,但有一点应该先提一下。对于应用程序,软键盘的输入同传统硬件键盘的输入是没什么不同的。

输入焦点
在Windows操作系统下,同时只有一个窗口拥有输入焦点。有焦点的窗口接收所有键盘输入,直到焦点切换到另外一个窗口。虽然系统使用很多规则来分配键盘焦点,但通常有焦点的窗口就是当前活动窗口。活动窗口就是用户当前正在交互的顶层窗口。除了极少数例外,活动窗口通常位于Z坐标的顶部,也就是说,活动窗口是绘制在系统里其它窗口上面的。在资源浏览器(Explorer)里,用户可以按Alt-Esc键在程序间切换,以改变活动窗口,或者在任务栏里点另一个顶层窗口的按钮来切换活动窗口。焦点窗口是活动窗口或者其子窗口之一。

Windows下,程序可以通过调用GetFocus来判断哪个窗口拥有输入焦点,函数原型如下:HWND GetFocus (void);通过调用SetFocus可以把焦点切换到另外一个窗口,函数原型如下:HWND SetFocus (HWND hWnd);

在Windows CE下,对SetFocus的目标窗口有一些限制。通过SetFocus来获取焦点的窗口必须是调用SetFocus的线程创建的窗口。该规则的一个例外是:如果失去焦点的窗口和即将获得焦点的窗口是父子或兄弟关系,那即使这两个窗口是被不同线程创建的,也可以切换焦点。

当窗口失去焦点时,Windows会给该窗口发送WM_KILLFOCUS消息,通知窗口新的状态信息。wParam参数则包含即将获得焦点的窗口句柄。获得焦点的窗口会收到WM_SETFOCUS消息,消息的wParam参数包含了失去焦点的窗口的句柄。

还要再叮嘱一下。程序不应该在没有用户输入的情况下改变焦点窗口。否则,用户很容易变的迷惑。SetFocus的一个适当用途是给活动窗口里的子窗口(更多是控件)设置输入焦点。在这种情况下,程序让想接收键盘消息的窗口用其子窗口的句柄来调用SetFocus,以响应WM_SETFOCUS消息。

键盘消息
除了一些小的例外,Windows CE与桌面版的Windows具有相同的键盘消息处理过程,当键被按下,Windows给焦点窗口发送一系列消息,通常都是以WM_KEYDOWN消息开始的。如果被按下的键代表诸如字母或数字等字符,Windows会在WM_KEYDOWN之后发送一个WM_CHAR消息。(一些按键,例如功能键和光标键等,不代表字符,则不会发送WM_CHAR。对这些按键,程序必须翻译WM_KEYDOWN消息来了解这些按键是什么时候被按下的。)当按键被释放,Windows会发送一个WM_KEYUP消息。如果按键按的时间长一些,则自动重复功能就会开启,多条WM_KEYDOWN消息和WM_CHAR消息会被送出,直到最后键被释放,发出WM_KEYUP消息。当Alt键和另一个键一起被按下时,上面讨论的消息会被WM_SYSKEYDOWN、WM_SYSKEYCHAR和WM_SYSKEYUP消息替代。

对所有这些消息,几乎都按相同的方式使用参数wParam和lParam。对WM_KEYxx和WM_SYSKEYxx消息,wParam包含虚拟键值,用于指出当前被按下的键。所有版本的Windows都在键盘硬件和应用程序之间提供了一个中间层,用于把键盘返回的扫描码转换成虚拟键盘值。表3-1列出了VK_xx值及对应的键。虽然虚拟键表很大,但不是所有表中列的键都能用于Windows CE设备。例如,作为PC键盘上很主要的键并列在虚拟键表中的功能键,却并没有出现在大部分Windows CE键盘上。实际上,PC键盘上的许多键都从空间受限的Windows CE键盘上去除了。图3-1给出了通常很少用在Windows CE设备上的键列表,该表只是告诉你这些键在Windows CE键盘上可能不存在,但并不是说绝对不存在。
表3-1:虚拟键

图3-1:PC键盘中很少用于Windows CE键盘的键

对WM_CHAR和WM_SYSCHAR消息来说,wParam包含键对应的Unicode字符。多数情况下,应用程序只是处理WM_CHAR消息,而忽略WM_KEYDOWN和WM_KEYUP消息。WM_CHAR消息作为第2级抽象,使应用程序不必考虑键的释放或按下状态,而将精力集中在键盘输入的字符上。

这些键盘消息中的lParam值含有关于按下的键的进一步的信息。图3-2给出了lParam参数的格式。
从0到15的底字位,包含键的重复次数。有时Windows CE设备上的键被按的很快,超过了Windows CE发送消息到焦点应用程序的速度。在这种情况下,重复计数器包含键被按下的次数。第29比特位是上下文标志位。如果键被按下的同时,Alt键被按下,则设置该位。30位是键的先前状态。如果键先前是按下的,则该位被设置,否则该位是0。该标志可以用来判断键消息是否是自动重复的结果。31位指出转换状态。如果键从按下转成释放,该位被设置。16-28位用来指出键的扫描码。在许多情况下,Windows CE并不支持该域。然而,在一些扫描码作为必需品的新的Windows CE平台上,这个域包含扫描码。除非您知道在您的特定平台支持它,否则您不应该设想扫描码域是可用的。
图3-2:lParam用于键消息时的布局

一个额外的键盘消息,WM_DEADCHAR,有时可能会有用。当按下的键代表一个诸如元音变音的死字符,用它和一个字符组合起来创建一个不同的字符的时候,您需要发送该消息。在这种情况下,WM_DEADCHAR消息用来防止光标移动到下一格,直到第2个键被按下,这样您可以完成组合后的字符。

WM_DEADCHAR消息在Windows下总是有的,但在Windows CE下它的作用可能更大一点。随着运行Windows CE的小消费品设备国际化,程序员应该,也有必要使用在外语系统里经常需要的WM_DEADCHAR消息。

键盘函数
您会发现用于判断键盘状态的函数对Windows应用程序是很有用的。在键盘函数中,有两个密切相关但又经常混淆的函数:GetKeyState和GetAsyncKeyState。

GetKeyState的原型如下:
short GetKeyState (int nVirtKey);
它返回大小写字母键盘(换挡键)、Ctrl、Alt和Shift的按下/释放状态,指出这些键是否在转换状态。如果键盘有两个键具有同样的功能,例如,键盘两边的那两个Shift键,则该函数可以用来区分是哪个被按下。(大部分键盘有左右Shife键,一些有左右Ctrl和Alt键。)

nVirKey表示要查询的键的虚拟键码。如果返回值的高位被设置,则该键被按下。如果返回值的最低有效位被设置,则该键在转换状态;也就是说,自从系统启动开始,该键已被按下奇数次。返回的状态是消息从消息队列里读出时的状态,并不是键的实时状态。需要注意的是,Alt键的虚拟键是VK_MENU。

注意GetKeyState函数在Windows CE下查询换挡键的状态是受限制的。在Windows的其它版本里,GetKeyState可以用来判断键盘上的每个键的状态。

要判断键的实时状态,可以使用short GetAsyncKeyState(int vKey);
同GetKeyState一样,传入要查询的键的虚拟键码。GetAsyncKeyState函数返回值同GetKeyState的略微不同。和GetKeyState一样,当键被按下,返回值的高位被设置。然而,如果在上次调用GetAsyncKeyState后,键被按下,则最低有效位也被设置。同GetKeyState一样,GetAsyncKeyState可以区分左右Shift、Ctrl及Alt键。另外,通过传递虚拟值VK_LBUTTON,GetAsyncKeyState可以判断触摸笔是否正在触摸屏幕。

通过keybd_event函数,应用程序可以模拟击键动作。
void keybd_event(BYTE bVk, BYTE bScan, DWORD dwFlags, DWORD dwExtraInfo);
第一个参数是要模拟的键的虚拟键码。bScan码在Windows CE下应该设置为NULL。dwFlags参数可以取两个可能的值:KEYEVENTF_KEYUP,表示该调用是模拟键盘释放事件,KEYEVENTF_SILENT表示模拟的键盘按下动作不会产生通常的键盘滴答声。所以,要完全模拟键按下操作,keydb_event应该调用2次。一次不带KEYEVENTF_KEYUP,来模拟键按下,之后在用KEYEVENTF_KEYUP调用一次来模拟键盘释放。当模拟换挡键时,要指出具体的左右VK码,比如VK_LSHIF或VK_RCONTROL。

Windows CE特有的函数是
BOOL PostKeybdMessage (HWND hwnd, UINT VKey, KEY_STATE_FLAGS KeyStateFlags, UINT cCharacters, UINT *pShiftStateBuffer, UINT *pCharacterBuffer );

该函数发送一系列的键到指定的窗口。hwnd参数是目标窗口,该窗口必须是调用线程所拥有的。VKey参数为0。KeyStateFlags为所有即将发送的键规定键的状态。cCharacters参数指定要发送的键的数量。pShiftStateBuffer参数指向一个数组,包含了每个键的shift状态;pCharacterBuffer指向键的VK码。不像keybd_event,该函数不改变键盘的全局状态。

最后一个键盘函数MapVirtualKey把虚拟键码翻译成字符。Windows CE下的MapVirtualKey并不进行键盘扫描码和虚拟码之间的转换,虽然在Windows的其它版本里它会这么做。函数原型如下:
UINT MapVirtualKey (UINT uCode, UINT uMapType);

Windows CE下,第一个参数是待翻译的虚拟键码,第二个参数uMapType指出如何翻译键码。MapVirtualKey依赖于实现支持功能的键盘设备驱动程序。许多OEM设备并不实现这种支持函数,所以在这种系统上,MapVirtualKey会失败。

键盘测试
要判断系统里是否有键盘,可以调用DWORD GetKeyboardStatus (VOID);
如果系统里键盘,则该函数返回KBDI_KEYBOARD_PRESENT标志。如果键盘启用,则该函数返回KBDI_KEYBOARD_ENABLED标志。要让键盘失效,可以把bEnable设置为FALSE来调用BOOL EnableHardwareKeyboard (BOOL bEnable)。对键盘折叠在屏幕后面的系统,您可能需要使键盘失效,因为在这种系统里,当使用触摸笔时,用户可能会意外地按下键盘。

KeyTrac 示例程序
下面的示例程序KeyTrac显示了键盘消息的顺序。从程序上讲,KeyTrac与书中早期的程序差别不大。不同点在于所有键盘消息都被捕获和记录在数组里,并在WM_PAINT消息里被显示出来。对每个键盘消息,消息名称记录、wParam、lParam值以及指出转换键状态的标志集合都被记录下来。键盘消息之所以被记录到数组里是因为这些消息比重绘操作要产生的快。图3-3显示了一些键被按下后的KeyTrac窗口。
图3-3略:Shift-A键组合以及小写a键被按下后的KeyTrac窗口
了解键盘消息顺序的最好方式是运行KeyTrac,按一些键,之后观察消息滚动显示在屏幕上。按一个字符键,例如a,会产生三个消息:WM_KEYDOWN,WM_CHAR和WM_KEYUP。按a的同时按下Shift键,随后释放Shift键,会产生Shift键按下的消息,随后是为a键产生的三个消息,最后是为Shift键产生的键释放消息。因为Shift键本身不是字符键,所以没有发送WM_CHAR消息。然而,在a键对应的WM_CHAR消息中,现在wParam包含的是0x41,表示一个大写的A被输入,而不是小写的a。清单3-1列出了KeyTrac的源代码。
清单3-1:KeyTrac程序
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;
}
下面是KeyTrac中需要注意的一些特性。在每个键盘消息被记录后,一个InvalidateRect函数被调用,用来强制重绘窗口,并产生一个WM_PAINT消息。正如我在第二章中提到的,一个程序不应该尝试发送或者提交(send or post)WM_PAINT消息给窗口,因为在Windows用WM_PAINT消息调用一个窗口之前,它需要执行一些设置工作。

KeyTrac中使用的另一个设备描述表函数是
BOOL ScrollDC (HDC hDC, int dx, int dy, const RECT *lprcScroll,  const RECT *lprcClip, HRGN hrgnUpdate,
              LPRECT lprcUpdate);

该函数水平或者垂直滚动设备描述表的一个区域,但在Windows CE下,同时不会有两个方向。接下来的三个矩形参数定义了 即将被滚动的区域,滚动区域里将别裁减的区域和在滚动结束后将被绘制的区域。一个区域句柄可以传递给ScrollDC。由ScrollDC定义的这个区域包含了在滚动后需要绘制的区域。

最后,如果KeyTrac窗口被覆盖和重新显示出来,屏幕上显示的信息将丢失,原因是设备描述表没有存储显示屏的位信息。由应用程序负责存储用于恢复屏幕客户区域所需要的任何信息。因为KeyTrac没有存储这些信息,所以当窗口被覆盖的时候,这些信息就丢失了。

抱歉!评论已关闭.