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);
}
原文地址:https://www.cnblogs.com/zhiweiyouzhishenghuo/p/5005548.html