Win32 SDK 创建加速键表。
加速键与菜单密切相关——都向用户提供一个应用程序的命令集的操作。通常情况下,用户依靠菜单来了解命令集合,在熟悉了应用程序后转而使用加速键。加速键提供比菜单更快、更直接的命令操作。虽然加速键通常产生菜单中存在的命令,但也可以产生菜单中不存在的命令。 加速键表 加速键表由一个ACCEL结构数组构成。每个结构定义一个独立的加速键,由以下信息组成: 1. 击键组合 2. 标识符 3. 一些标记。 要处理加速键,开发人员必须在线程消息队列相关的消息循环中调用TranslateAccelerator函数。此函数监控消息队列中的键盘输入,检查与加速键表中条目匹配的击键组合。若匹配,则将键盘输入(WM_KEYUP和WM_KEYDOWN消息)转换为WM_COMMAND或WM_SYSCOMMAND消息并发送给指定窗口的窗口过程。 WM_COMMAND消息包含加速键的标识符。窗口过程检查此标识符以确定消息源然后进行相应地消息处理。 加速键表存在两种不同的等级:系统级(单一的、系统范围的应用于所有程序)和应用程序级。系统加速键表不允许修改。 应用程序可定义多个加速键表。每个表用一个32位句柄标识。不过,对一个特定线程,任一时刻只会有一个加速键表处于活动状态。在一个加速键表句柄被传递给TranslateAccelerator函数后,对应的加速键表即进入活动状态。 加速键表的创建 创建加速表分为几个步骤: 首先,用资源编译器创建加速键表资源并加入应用程序的可执行文件。 然后,在运行时使用LoadAccelerators将加速键表加载到内存并获取其句柄。 也可以通过向CreateAcceleratorTable传递一个ACCEL结构的数组来创建一个加速键表,此方法可用于用户自定义加速键。 系统会自动销毁由LoadAccelerators加载的加速键表。但CreateAcceleratorTable创建的加速键表必须由应用程序销毁,否则,在应用程序关闭后还将一直存在在内存中。 加速键表可被复制和更改。 加速键击键分配 ASCII字符编码和虚拟键码可用于定义加速键。使用ASCII字符编码定义的加速键是区分大小写的。因此,通常使用虚拟键码。 应避免加速键与菜单助词符冲突,因为加速键会覆盖助记符,那样将使用户混淆。 若在应用程序中定义了与系统加速键表中已存在的加速键,则应用程序的加速键将覆盖系统加速键,不过仅在应用程序的上下文中。由于会使系统加速键出现不正常的表现,因此应避免这种情况。 系统加速键包括: ALT+ESC Switches to the next application. ALT+F4 Closes an application or a window. ALT+HYPHEN Opens the Window menu for a document window. ALT+PRINT SCREEN Copies an image in the active window onto the clipboard. ALT+SPACEBAR Opens the Window menu for the application's main window. ALT+TAB Switches to the next application. CTRL+ESC Switches to the Start menu. CTRL+F4 Closes the active group or document window. F1 Starts the application's help file, if one exists. PRINT SCREEN Copies an image on the screen onto the clipboard. SHIFT+ALT+TAB Switches to the previous application. The user must press and hold down ALT+SHIFT while pressing TAB. 加速键与菜单 使用加速键就像选择菜单项一样:都导致系统向相应的窗口过程发送一个WM_COMMAND或WM_SYSCOMMAND消息。由于加速键提供种从菜单中选择命令的快捷方式,应用程序通常为加速键和相应的菜单项使用相同的标识符。 应用程序处理加速键WM_COMMAND消息与处理菜单项WM_COMMAND消息完全一样。不过,为了支持可能存在的差异处理,WM_COMMAND也包含了一个确定消息来源的标记。WM_SYSCOMMAND不包含此标记。 标识符决定了加速键将产生WM_COMMAND还是WM_SYSCOMMAND消息。如果标识符与系统菜单中菜单项相同,则产生WM_SYSCOMMAND,否则产生WM_COMMAND消息。 如果加速键与某菜单项的标识符相同并且此菜单项被禁用,则此加速键也被禁用,将不产生WM_COMMAND或WM_SYSCOMMAND消息。如果对应的窗口被最小化了,加速键同样不会产生命令消息。 热键 热键通过RegisterHotKey注册。 按下键时,Windows查找所有已注册热键,存在匹配项时,向注册热键的线程的消息队列发送WM_HOTKEY消息。WM_HOTKEY将被放置在队列的前端。 热键的击键组合不能重复注册。 加速键与热键 均用于将键盘事件转换成其它消息,但使用场合不同。 加速键在应用程序上下文中有效,不同应用程序可定义多套加速键表。 热键在系统范围内有效,但关联到特定线程上的特定窗体。 添加加速键有几种办法,最直接的就是在资源文件中添加,然后使用 LoadAccelerators 加载。
这篇文字说明了如何在运行时创建加速键表。 // accelerator.h #include <windows.h> #include <windowsx.h> #include <tchar.h> #define ID_PRIN 25 // accelerator.cpp #include "accelerator.h" BOOL OnCreate(HWND, LPCREATESTRUCT lpCreateStruct) { ACCEL accel[] = { { FSHIFT | FVIRTKEY, 'N', SC_MINIMIZE }, // 最小化窗口Shift+ N { FSHIFT | FVIRTKEY, 'X', SC_MAXIMIZE }, // 最大化窗口 Shift + X { FSHIFT | FVIRTKEY, 'R', SC_RESTORE }, // 还原窗口 Shift + R { FALT | FCONTROL | FVIRTKEY, 'P', ID_PRIN }, // 输出文字 Ctrl + Alt + P { FCONTROL | FVIRTKEY, VK_F4, SC_CLOSE }, // 退出 Ctrl + F4 }; // 创建加速键表 *(HACCEL*)lpCreateStruct->lpCreateParams = CreateAcceleratorTable(accel, 5); return TRUE; } VOID OnCommand(HWND hwnd, INT id, HWND, UINT) { HDC hDC; RECT rc; TCHAR szTime[32]; SYSTEMTIME systime; switch (id) { // 输出文字 case ID_PRIN: GetClientRect(hwnd, &rc); GetLocalTime(&systime); wsprintf(szTime, _T("%04d.%02d.%02d %02d:%02d:%02d"), systime.wYear, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond); DrawText(hDC = GetDC(hwnd), szTime, -1, &rc, DT_SINGLELINE | DT_VCENTER | DT_CENTER); ReleaseDC(hwnd, hDC); break; } } VOID OnDestroy(HWND) { PostQuitMessage(0); } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_CREATE, OnCreate); HANDLE_MSG(hwnd, WM_COMMAND, OnCommand); HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy); default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } VOID AcceleratorMain() { WNDCLASS wnd = { 0 }; MSG msg; HACCEL hAccel; wnd.lpfnWndProc = WndProc; wnd.hInstance = GetModuleHandle(NULL); wnd.hCursor = LoadCursor(NULL, IDC_ARROW); wnd.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME; wnd.lpszClassName = _T("Accelerator"); RegisterClass(&wnd); if (!CreateWindow((TCHAR*)_T("Accelerator"), _T("Accelerator"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, wnd.hInstance, &hAccel)) ExitProcess(0); while (GetMessage(&msg, NULL, 0, 0)) { // 变换加速键消息 if (!TranslateAccelerator(msg.hwnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } // DestroyAcceleratorTable(hAccel); ExitProcess(msg.wParam); } |