摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P373

键盘加速键是可以生成 WM_COMMAND(或者有时是 WM_SYSCOMMAND)消息的组合键。通常,程序使用键盘加速键来复制公共菜单选项的动作,但它们也可以执行非菜单功能。例如,某些 Windows 程序有一个含有 Delete 或 Clear 选项的 Edit 菜单;这些程序通常指定 Del 键作为这个选项的键盘加速键。用户可以按 Alt 组合键从菜单中选择 Delete 选项,也可以简单地按 Del 键来使用键盘加速键。当窗口过程接收到 WM_COMMAND 消息时,它不需要检测该命令是来自于键盘加速键。

10.3.1  为什么你应该使用键盘加速键

你可能会问:为什么我应该使用键盘加速键?为什么我不能捕捉 WM_KEYDOWN 或 WM_CHAR 消息并自己复制菜单功能?它有什么优点?对一个单窗口程序,你当然可以捕捉键盘消息。但是使用键盘加速键的一个简单优点是,你不需要重写菜单和键盘加速键处理逻辑。

对有多个窗口和窗口过程的应用程序,键盘加速键变得十分重要。正如我们所见,对拥有当前输入焦点的窗口,Windows 会将键盘消息发送给它的窗口过程。然而,对键盘加速键而言,Windows 会将 WM_COMMAND 消息发送给一个句柄在 Windows 函数 TranslateAccelerator 中指定的窗口过程。通常,这会是你的主窗口,亦即拥有菜单的那个窗口,这意味着响应键盘加速键的逻辑不需要被复制到每一个窗口过程。

如果你使用非模态对话框(在第 11 章讨论)或在主窗口的客户区内使用子窗口,这个优点就变得极其重要。如果给多个窗口定义了同一个键盘加速键,只需在其中一个窗口包含这个逻辑即可。子窗口不会接收到来自键盘加速键的 WM_COMMAND 消息。

10.3.2  指定加速键的一些规则

理论上,你可以使用几乎任何虚拟键或字符键与 Shift 键、Ctrl 键或 Alt 键的组合来定义键盘加速键。然而,你应该试图和其他应用程序保持一致,以避免干扰 Windows 对键盘的使用。你 应该避免使用Tab、回车键、Esc 和空格键作为键盘加速键,因为它们通常保留给系统功能。

系统加速键最通常的用途是用于程序中 Edit 菜单的菜单项。对这些菜单项推荐使用的键盘加速键在 Windows 3.0 和 Windows 3.1 之间有变化,因此同时支持新旧两种加速键很常见,如下表所示:

功  能 旧加速键 新加速键
 Undo(撤销)  Alt+Backspace  Ctrl+Z
 Cut(剪切)  Shift+Del  Ctrl+X
  Copy(复制)  Ctrl+Ins  Ctrl+C
 Paste(粘贴)  Shift+Ins  Ctrl+V
 Delete 或 Clear(删除)  Del  Del

另一个很常见的加速键是 F1 功能键,它用来激活帮助。 应该避免使用 F4、F5 和 F6 键,因为它们经常被多文档界面(Multiple Document Interface, MDI)程序用作特殊功能,我们会在第 19 章中讨论 MDI。

10.3.3  加速键表

你可以在 Developer Studio 中定义加速键表。为了方便在你的程序中加载加速键表,可以使它与应用程序同名(这也适用于菜单和图标)。

每个加速键有一个 ID 和一个击键组合,这些你可以在 Accelerator Properties 对话框中定义。如果你已经定义了菜单,Aidan DI 会存在与组合框中,你不需要重新输入它们。

加速键可以是虚拟键代码或 ASCII 字符与 Shift、Ctrl 或 Alt 键的组合。你可以在字母前加^来指定 ASCII 字符与 Ctrl 键的组合。你也可以从组合框中选择虚拟键代码。

在为菜单项定义键盘加速键时,应当在菜单文本中包含相应的键组合。Tab 字符(\t)能将文本和加速键分开,这样加速键会排列在第二列。为了在菜单中标识加速键,可以使用文字 Ctrl、Shift 或 Alt,后面跟着一个加号和键名(例如 Shift+F6 或 Ctrl+F6)。

10.3.4  加载加速键表

在应用程序中,应使用 LoadAccelerators 函数来把加速键表加载到内存并获得它的句柄。LoadAccelerators 语句和 LoadIcon、LoadCursor 及 LoadMenu 语句很相似。

首先用类型 HANDLE 定义一个加速键表的句柄:

HANDLE hAccel;

然后加载加速键表:

hAccel = LoadAccelerators (hInstance, TEXT("MyAccelerators"));

和图标、鼠标指针及菜单一样,你也可以用数字来命名加速键表,然后在 LoadAccelerators 中通过 MAKEINTRESOURCE 宏来使用该数字,或者将数字用引号括起来并加上前缀#字符。

10.3.5  翻译按键

我们现在要改动三行代码,这三行代码是本书迄今为止对所有 Windows 程序都通用的。这三行代码就是标准消息循环:

while (GetMessage (&msg, NULL, 0, 0))
{TranslateMessage (&msg);DispatchMessage (&msg);
}

下面显示我们如何改变它来使用键盘加速键表:

while (GetMessage (&msg, NULL, 0, 0))
{if (!TranslateAccelerator (hwnd, hAccel, &msg)){
<pre name="code" class="cpp">        TranslateMessage (&msg);DispatchMessage (&msg);

}} TranslateAccelerator 函数确定保存在 msg 消息结构中的消息是否是键盘消息。如果是,该函数在加速键表中寻找句柄为 hAccel 的匹配值。如果找到匹配值,它会调用句柄为 hwnd 的窗口过程。如果键盘加速键 ID 对应系统菜单的一个菜单项,则相应消息为 WM_SYSCOMMAND;否则,消息是 WM_COMMAND。

当 TranslateAccelerator 返回时,如果消息被翻译过(并且已被发送给窗口过程),则返回值为非零值,否则返回值为零。如果 TranslateAccelerator 返回非零值,你就不应该再调用 TranslateMessage 和 DispatchMessage,而是应该返回 GetMessage 循环。

TranslateAccelerator 中的 hwnd 参数看起来不太合适,因为消息循环中的三个函数都不需要它。并且,消息结构自身(结构变量 msg)有一个名字为 hwnd 的成员,它也是一个窗口句柄。

这个函数之所以有些不同的原因是:msg 结构的字段是由 GetMessage 调用来填充的。当 GetMessage 的第二个参数是 NULL 时,它检索属于该应用程序的所有窗口的消息。当 GetMessage 返回时,msg 结构的 hwnd 成员是将会得到该消息的窗口句柄。然而,当 TranslateAccelerator 将键盘消息翻译成 WM_COMMAND 或 WM_SYSCOMMAND 消息时,它将 msg.hwnd 窗口句柄替换成该函数的第一个参数所指定的窗口句柄。于是 Windows 会将所有键盘加速键消息发送给同一个窗口,即使程序中另外一个窗口当前拥有输入焦点。当模态对话框或消息框拥有输入焦点时,TranslateAccelerator 不翻译键盘消息,因为这些窗口的消息不通过程序的消息循环。

某些情况下,当应用程序中另一个窗口(比如非模态对话框)拥有输入焦点时,你并不想翻译键盘消息。如何处理这种情况将在第 11 章介绍。

10.3.6  接收加速键消息

当一个键盘加速键对应系统菜单的一个菜单项时,TranslateAccelerator 会向窗口过程发送一条 WM_SYSCOMMAND 消息;否则,TranslateAccelerator 向窗口过程发送一条 WM_COMMAND 消息。下表列出了对于键盘加速键、菜单命令和子窗口控件你会接收到的 WM_COMMAND 消息的类型:

  加 速 键 菜  单 控  件
 LOWORD(wParam)  加速键 ID  菜单 ID  控件 ID
 HIWORD(wParam)  1  0  通知码
 lParam  0  0  子窗口句柄

如果键盘加速键对应某个菜单项,那么窗口过程还会接收到 WM_INITMENU,WM_INITMENUPOPU 和 WM_MENUSELECT 消息,就像菜单项被选择了一样。在处理 WM_INITMENUPOPUP 时,程序通常可以启用或禁用弹出菜单的菜单项。使用键盘加速键时,你仍然拥有这种机制。当一个键盘加速键对应一个禁用或变灰的菜单项时,TranslateAccelerator 不会向窗口过程发送 WM_COMMAND 或 WM_SYSCOMMAND 消息。

如果当前窗口被最小化,对于映射到启用的系统菜单项的键盘加速键,TranslateAccelerator 将向窗口过程发送 WM_SYSCOMMAND 消息而不是 WM_COMMAND 消息。对于没有映射到任何菜单项的加速键,TranslateAccelerator 也会向窗口过程发送 WM_COMMAND 消息。

10.3.7  带有菜单和加速键的 POPPAD 程序

在第 9 章,我们创建了 POPPAD1 程序,它使用一个子窗口编辑空间来实现了一个简单的记事本(notepad)。在本章,我们将加入 File 和 Edit 菜单,并相应将程序称为 POPPAD2。Edit 中菜单项都能功能;File 菜单功能我们会在第 11 章实现,Print 功能在第 13 章实现。POPPAD2 程序如图 10-11 所示。

/*--------------------------------------------------------POPPAD2.C --   Popup Editor Version 2 (includes menu)(c) Charles Petzold, 1998
--------------------------------------------------------*/
#include <windows.h>
#include "resource.h"#define ID_EDIT 1LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);TCHAR szAppName[] = TEXT("PopPad2");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{HACCEL      hAccel;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(hInstance, szAppName);wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = szAppName;wndclass.lpszClassName = szAppName;if (!RegisterClass(&wndclass)){MessageBox(NULL, TEXT("This program requires Windows NT!"),szAppName, MB_ICONERROR);return 0;}hwnd = CreateWindow(szAppName, szAppName,WS_OVERLAPPEDWINDOW,GetSystemMetrics(SM_CXSCREEN) / 4, GetSystemMetrics(SM_CYSCREEN) / 4,GetSystemMetrics(SM_CXSCREEN) / 2,GetSystemMetrics(SM_CYSCREEN) / 2,NULL, NULL, hInstance, NULL);hAccel = LoadAccelerators(hInstance, szAppName);ShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);while (GetMessage(&msg, NULL, 0, 0)){if (!TranslateAccelerator(hwnd, hAccel, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);}}return msg.wParam;
}int AskConfirmation (HWND hwnd)
{return MessageBox(hwnd, TEXT("Really want to close PopPad2?"),szAppName, MB_YESNO | MB_ICONQUESTION);
}LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static HWND hwndEidt;int           iSelect, iEnable;switch (message){case WM_CREATE:hwndEidt = CreateWindow(TEXT("edit"), NULL,WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |WS_BORDER | ES_LEFT | ES_MULTILINE |ES_AUTOHSCROLL | ES_AUTOVSCROLL,0, 0, 0, 0, hwnd, (HMENU)ID_EDIT,((LPCREATESTRUCT)lParam)->hInstance, NULL);return 0;case WM_SETFOCUS:SetFocus(hwndEidt);return 0;case WM_SIZE:MoveWindow(hwndEidt, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);return 0;case WM_INITMENUPOPUP:if (lParam == 1){EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO,SendMessage(hwndEidt, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED);EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE,IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);iSelect = SendMessage(hwndEidt, EM_GETSEL, 0, 0);if (HIWORD(iSelect) == LOWORD(iSelect))iEnable = MF_GRAYED;elseiEnable = MF_ENABLED;EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, iEnable);EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, iEnable);EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, iEnable);return 0;}break;case WM_COMMAND:if (lParam){if (LOWORD(wParam) == ID_EDIT)if (HIWORD(wParam) == EN_ERRSPACE ||HIWORD(wParam) == EN_MAXTEXT)MessageBox(hwnd, TEXT("Edit control out of space."),szAppName, MB_OK | MB_ICONSTOP);} else switch (LOWORD (wParam)){case IDM_FILE_NEW:case IDM_FILE_OPEN:case IDM_FILE_SAVE:case IDM_FILE_SAVE_AS:case IDM_FILE_PRINT:MessageBeep(0);return 0;case IDM_APP_EXIT:SendMessage(hwnd, WM_CLOSE, 0, 0);return 0;case IDM_EDIT_UNDO:SendMessage(hwndEidt, WM_UNDO, 0, 0);return 0;case IDM_EDIT_CUT:SendMessage(hwndEidt, WM_CUT, 0, 0);return 0;case IDM_EDIT_COPY:SendMessage(hwndEidt, WM_COPY, 0, 0);return 0;case IDM_EDIT_PASTE:SendMessage(hwndEidt, WM_PASTE, 0, 0);return 0;case IDM_EDIT_CLEAR:SendMessage(hwndEidt, WM_CLEAR, 0, 0);return 0;case IDM_EDIT_SELECT_ALL:SendMessage(hwndEidt, EM_SETSEL, 0, -1);return 0;case IDM_APP_HELP:MessageBox(hwnd, TEXT("Help not yet implemented!"),szAppName, MB_OK | MB_ICONEXCLAMATION);return 0;case IDM_APP_ABOUT:MessageBox(hwnd, TEXT("POPPAD2 (c) Charles Petzold, 1998"),szAppName, MB_OK | MB_ICONINFORMATION);return 0;}break;case WM_CLOSE:if (IDYES == AskConfirmation(hwnd))DestroyWindow(hwnd);return 0;case WM_QUERYENDSESSION:if (IDYES == AskConfirmation(hwnd))return 1;elsereturn 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(hwnd, message, wParam, lParam);
}
POPPAD2.RC (excerpts)
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"/
//
// Icon
//POPPAD2                 ICON                    "POPPAD2.ICO"/
//
// Menu
//POPPAD2 MENU
BEGINPOPUP "&File"BEGINMENUITEM "&New",                        IDM_FILE_NEWMENUITEM "&Open...",                    IDM_FILE_OPENMENUITEM "&Save",                       IDM_FILE_SAVEMENUITEM "Save &As...",                 IDM_FILE_SAVE_ASMENUITEM SEPARATORMENUITEM "&Print",                      IDM_FILE_PRINTMENUITEM SEPARATORMENUITEM "E&xit",                       IDM_APP_EXITENDPOPUP "&Edit"BEGINMENUITEM "&Undo\tCtrl+Z",               IDM_EDIT_UNDOMENUITEM SEPARATORMENUITEM "Cu&t\tCtrl+X",                IDM_EDIT_CUTMENUITEM "&Copy\tCtrl+C",               IDM_EDIT_COPYMENUITEM "&Paste\tCtrl+V",              IDM_EDIT_PASTEMENUITEM "De&lete\tDel",                IDM_EDIT_CLEARMENUITEM SEPARATORMENUITEM "&Select All",                 IDM_EDIT_SELECT_ALLENDPOPUP "&Help"BEGINMENUITEM "&Help...",                    IDM_APP_HELPMENUITEM "&About PopPad2...",           IDM_APP_ABOUTEND
END/
//
// Accelerator
//POPPAD2 ACCELERATORS
BEGINVK_BACK,        IDM_EDIT_UNDO,          VIRTKEY, ALT, NOINVERTVK_DELETE,      IDM_EDIT_CLEAR,         VIRTKEY, NOINVERTVK_DELETE,      IDM_EDIT_CUT,           VIRTKEY, SHIFT, NOINVERTVK_F1,          IDM_APP_HELP,           VIRTKEY, NOINVERTVK_INSERT,      IDM_EDIT_PASTE,         VIRTKEY, CONTROL, NOINVERTVK_INSERT,      IDM_EDIT_PASTE,         VIRTKEY, SHIFT, NOINVERT"^C",           IDM_EDIT_COPY,          ASCII,  NOINVERT"^V",           IDM_EDIT_PASTE,         ASCII,  NOINVERT"^X",           IDM_EDIT_CUT,           ASCII,  NOINVERT"^Z",           IDM_EDIT_UNDO,          ASCII,  NOINVERT
END
RESOURCE.H (excerpts)
// Microsoft Visual C++ 生成的包含文件。
// 供 PopPad2.rc 使用
//
#define IDR_ACCELERATOR1                102
#define IDI_ICON1                       104
#define IDM_FILE_NEW                    40001
#define IDM_FILE_OPEN                   40002
#define IDM_FILE_SAVE                   40003
#define IDM_FILE_SAVE_AS                40004
#define IDM_FILE_PRINT                  40005
#define IDM_APP_EXIT                    40006
#define IDM_EDIT_UNDO                   40007
#define IDM_EDIT_CUT                    40008
#define IDM_EDIT_COPY                   40009
#define IDM_EDIT_PASTE                  40010
#define IDM_EDIT_CLEAR                  40011
#define IDM_EDIT_SELECT_ALL             40012
#define IDM_APP_HELP                    40013
#define IDM_APP_ABOUT                   40014

POPPAD2.ICO

POPPAD2.RC 资源脚本文件包含菜单和加速键表。你会注意到,加速键都在 Edit 弹出菜单的字符串中指定,并跟随\t字符之后。

10.3.8  启用菜单项

窗口过程的主要任务现在包括了启用和变灰 Edit 菜单中的菜单项,这是在处理 WM_INITMENUPOPUP 消息时完成的。首先,程序检查 Edit 弹出菜单是否将要被显示。由于 Edit 在菜单中的位置索引是 1(开始的 File 菜单项为 0),所以如果 Edit 弹出菜单将要被显示,lParam 将等于 1。

为了检查 Undo 菜单项是否可以被启用,POPPAD2 发送 EM_CANUNDO 消息给编辑控件。如果编辑控件可以执行 Undo 操作,SendMessage 会返回非零值,这时该选项被启用;否则,该选项变灰:

EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO,SendMessage(hwndEidt, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED);

只有当剪贴板当前包含文本时,Paste 菜单项才应该被启用。我们可以调用 IsClipboardFormatAvailable 并传递 CF_TEXT 标识符来检测:

EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE,IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);

只有在编辑控件中有文本被选中时,Cut、Copy 和 Delete 选项才应该被启用。向编辑控件发送 EM_GETSEL 消息会返回一个包含该信息的整数值:

iSelect = SendMessage(hwndEdit, EM_GETSEL, 0, 0);

iSelect 的低位字是第一个被选中字符的位置,高位字是紧随选中文本后面的第一个字符的位置。如果这两个字相等,则表明没有文本被选中:

if (HIWORD(iSelect) == LOWORD(iSelect))iEnable = MF_GRAYED;
elseiEnable = MF_ENABLED;

iEnable 的值随后可以用于 Cut、Copy 和 Delete 菜单项:

EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, iEnable);
EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, iEnable);
EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, iEnable);

10.3.9  处理菜单项

当然,如果我们没有在 POPPAD2 中使用子窗口编辑控件,我们将面临一个问题,也就是说要具体实现 Edit 菜单中的 Undo、Cut、Copy、Paste、Clear 和 Select All 菜单。但是编辑控件简化了这个过程,因为我们只需要对应每一个菜单项向编辑控件发送一个消息:

case IDM_EDIT_UNDO:SendMessage(hwndEidt, WM_UNDO, 0, 0);return 0;case IDM_EDIT_CUT:SendMessage(hwndEidt, WM_CUT, 0, 0);return 0;case IDM_EDIT_COPY:SendMessage(hwndEidt, WM_COPY, 0, 0);return 0;case IDM_EDIT_PASTE:SendMessage(hwndEidt, WM_PASTE, 0, 0);return 0;case IDM_EDIT_CLEAR:SendMessage(hwndEidt, WM_CLEAR, 0, 0);return 0;case IDM_EDIT_SELECT_ALL:SendMessage(hwndEidt, EM_SETSEL, 0, -1);return 0;

请注意,我们可以进一步简化这个过程,即让 IDM_EDIT_UNDO、IDM_EDIT_CUT 等的值等于相对应的窗口消息 WM_UNDO、WM_CUT 等的值,以此类推。

File 弹出菜单中的 About 菜单项引发一个简单的消息框:

case IDM_APP_ABOUT:MessageBox(hwnd, TEXT("POPPAD2 (c) Charles Petzold, 1998"),szAppName, MB_OK | MB_ICONINFORMATION);return 0;

在第 11 章中,我们会把它变成一个对话框。当你从该菜单中选择 Help 菜单项或按键盘加速键 F1 时,也会引发一个消息框。

Exit 菜单项向窗口过程发送一条 WM_CLOSE 消息:

case IDM_APP_EXIT:SendMessage(hwnd, WM_CLOSE, 0, 0);return 0;

这正是 DefWindowProc 收到 wParam 为 SC_CLOSE 的 WM_SYSCOMMAND 消息时所作出的反应。

在以前的程序中,我们没有在窗口过程中处理 WM_CLOSE 消息,而是简单地将它传递给 DefWindowProc。DefWindowProc 对 WM_CLOSE 只简单地做了一件事:调用 DestroyWindow 函数。POPPAD2 则对 WM_CLOSE 消息进行了处理,而不是将它发送给 DefWindowProc。(这个事实现在不是很重要,但在第 11 章中当 POPPAD 能真正地编辑文件时,将变得非常重要。)

case WM_CLOSE:if (IDYES == AskConfirmation(hwnd))DestroyWindow(hwnd);return 0;

AskConfirmation 是 POPPAD2 中的一个函数,它显示一个消息框,要求用户对关闭程序进行确认:

int AskConfirmation (HWND hwnd)
{return MessageBox(hwnd, TEXT("Really want to close PopPad2?"),szAppName, MB_YESNO | MB_ICONQUESTION);
}

当 Yes 按钮被选中时,消息框(以及 AskConfirmation 函数)返回 IDYES。只有这时 POPPAD2 才调用 DestroyWindow。否则,这个程序不会终止。

如果想再结束程序前得到确认,还必须处理 WM_QUERYENDSESSION 消息。当用户选择关闭 Windows 时,Windows 开始向每一个窗口过程发送 WM_QUERYENDSESSION 消息。如果有任何一个窗口过程对此消息返回 0,Windows 将不会被终止。下面的代码显示了如何处理 WM_QUERYENDSESSION 消息:

case WM_QUERYENDSESSION:if (IDYES == AskConfirmation(hwnd))return 1;elsereturn 0;

如果想要在结束程序之前获得用户的认可,WM_CLOSE 和 WM_QUERYENDSESSION 消息是唯一两个必须要处理的消息。这就是为什么我们让 POPPAD2 中的 Exit 菜单项向窗口过程发送 WM_CLOSE 的原因。这样,我们避免了在第三处的确认。

如果要处理 WM_QUERYENDSESSION 消息,那么你可能也会对 WM_ENDSESSION 敢兴趣。Windows 向每一个曾接收过 WM_QUERYENDSESSION 消息的窗口发送此消息。如果由于其他程序从 WM_QUERYENDSESSION 消息返回 0 而导致终止操作失败,wParam 将被设为 0。WM_ENDSESSION 消息本质上回答了这样一个问题:我告诉 Windows 可以终止我,但是我真的被终止了吗?

虽然我在 POPPAD2 的 File 菜单中包含了常见的 New、Open、Save 和 Save As 等菜单项,但它们当前并不坐任何事情。要处理这些命令,我们需要使用对话框。现在你已经准备好了,开始学习第 11 章,研究对话框吧!

10.3 键盘加速键相关推荐

  1. mfc使用键盘加速键

    首先创建一个菜单,ID分别为IDM_FILE_NEW,IDM_FILE_OPEN,IDM_FILE_CLOSE. 分别对菜单各项进行响应. 在OnInitDialog()中,添加标题栏菜单: HMEN ...

  2. 非框架窗口键盘加速键的使用

    在设计应用程序菜单时,可以选择使用键盘加速键给任意或者全部菜单项分配快捷键,加速键也会引发WM_COMMAND消息. 第一步,是要创建一个加速键表(特殊的资源).具体方法是,在资源列表视图里面,添加加 ...

  3. 计算机二级vb程序设计教程第10章键盘与鼠标事件

    本章介绍和键盘鼠标有光的事件过程 KeyPress事件 当压下键盘上的某个键的时候,将发生KeyPress事件 精确描述: 按下某个键,我们将触发此时拥有焦点的KeyPress 事件. 输入焦点只能位 ...

  4. 【EasyUse】关于键盘加速键的几点思考

    因为EasyUse程序要用到键盘加速,中间遇到了一些难题.汇总以便以后使用. 1.该在哪个地方截取消息(?) 关于这问题,认识还不够深刻.有一点可以确定,你在程序运行的CXXApp和CXXDlg的Pr ...

  5. win 10 禁用键盘,下载vc

    1. win10 禁用笔记本键盘 sc config i8042prt start= disabled //禁用键盘 sc config i8042prt start= auto // 恢复 如果报如 ...

  6. 技法:对你的应用添加键盘加速键

    对IsDialogMessage的误解 有一个API函数可能经常容易造成误解,它就是IsDialogMessage.IsDialogMessage将会判断一个消息是否是针对指定对话框的消息,如果是的话 ...

  7. 关于VS2010中键盘加速键无效的问题

    / // // Accelerator //IDR_ACCELERATOR ACCELERATORS BEGINVK_F2, IDM_NEW, VIRTKEY, NOINVERT END 在资源中添加 ...

  8. 加载自己的键盘加速键

    // TODO: 在此添加专用代码和/或调用基类 //1.加载加速键表 HINSTANCE hInst=AfxGetResourceHandle(); LPCTSTR lpID=MAKEINTRESO ...

  9. Windows 10 美式键盘消失 解决方案

    进入注册表编辑器(regedit). 前往 计算机\HKEY_CURRENT_USER\Keyboard Layout\Preload. 如果有名为 "2" 的值,修改其内容为 0 ...

最新文章

  1. 人脸对齐端到端Super-FAN
  2. 谷歌浏览器linux版_Chrome apps 要被谷歌干掉了
  3. opengl加载显示3D模型FBX类型文件)
  4. Where is ABAP Netweaver HTTP 304 not modified set
  5. amd黑苹果万能显卡驱动_22款显卡乱战《赛博朋克2077》:AMD 4K败走麦城-显卡,赛博朋克2077,AMD,NVIDIA ——快科技(驱动之家旗下媒体)-...
  6. 技术人生:希望有生之年开发一个“自己的解释语言”
  7. Java动态代理之InvocationHandler最简单的入门教程 1
  8. 未来智能合约平台的展望
  9. 上课解除教师机控制(红蜘蛛)超详细
  10. 用C语言编写一个关机程序
  11. 电子设计大赛-室内可见光定位装置
  12. 网络安全kali渗透学习 web渗透入门 使用msf扫描靶机上mysql服务的空密码
  13. 石墨文档编辑器用的是开源库吗?
  14. App三种启动场景:冷启动、热启动、温启动
  15. c 调试易语言dll,易语言制作调试助手
  16. GC是什么?为什么会有GC?
  17. 一名叫谙忆的程序员在2021年的具体安排《打工人的那些事》
  18. 基于 React hooks + Typescript + Cesium 实现日照分析并封装对应 SunShineAnalysis 类
  19. REST - 表述性状态转移
  20. 计算机专业英语汇总(二)

热门文章

  1. 迪丽热巴代言澳洲自然营养品牌Swisse
  2. 电脑无法加载 reCaptcha 的解决方案
  3. springboot中配置logback实现打印控制台、写出文件,控制日志级别的方式
  4. win10 linux 子系统 wsl2实现ip自动转发
  5. php insert什么意思,PHP insert语法详解
  6. 「硬见小百科」三极管放大电路原理
  7. pip command
  8. Java注解和反射,springboot2精髓百度云
  9. 关于 jupyter notebook 运行时不再显示结果,行头提示符变 In [*] 的解决办法
  10. 英飞凌TC264之PWM舵机控制