c++ - 我正在测试 WM_MENUCHAR 但代码没有按预期工作

标签 c++ c winapi

下面的例子使用了一个只有系统菜单的应用程序窗口,我试图理解消息 WM_MENUCHAR 应该如何使用。 MSDN doc对于消息说:

Sent when a menu is active and the user presses a key that does not correspond to any mnemonic or accelerator key. This message is sent to the window that owns the menu.

在下面的示例中,每当我按下 Alt + AAlt + B 或其他任何键时,WM_MENUCHAR 都会发送到窗口过程。

我假设 Alt 键激活系统菜单,因为所有发送到 WndProc() 的 WM_MENUCHAR 消息都有 HIWORD(wParam) = MF_SYSMENU 以及原因消息发送到窗口是因为程序没有快捷键表,系统菜单也没有助记符。

我不清楚这一点:响应 WM_MENUCHAR 代码返回 MAKELPARAM(5, MNC_SELECT) 但此返回值不会调用Close 系统菜单中的菜单项(请记住,5 是系统菜单中此菜单项的基数 0 订单号)。我错过了什么?

#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
    WNDCLASSEX  wndclassx;

    wndclassx.cbSize = sizeof(WNDCLASSEX);
    wndclassx.style = CS_HREDRAW | CS_VREDRAW;
    wndclassx.lpfnWndProc = WndProc;
    wndclassx.cbClsExtra = 0;
    wndclassx.cbWndExtra = 0;
    wndclassx.hInstance = hInstance;
    wndclassx.hIcon = 0;
    wndclassx.hCursor = LoadCursor(0, IDC_ARROW);
    wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wndclassx.lpszClassName = L"WndProc";
    wndclassx.lpszMenuName = nullptr;
    wndclassx.hIconSm = 0;

    if (!RegisterClassEx(&wndclassx)) return 0;

    HWND hWnd = CreateWindow(L"WndProc", L"WM_MENUCHAR", WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0);

    ShowWindow(hWnd, SW_MAXIMIZE);
    UpdateWindow(hWnd);

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

    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_MENUCHAR:
        return MAKELPARAM(5, MNC_SELECT);

        case WM_DESTROY:
        PostQuitMessage(0);
        break;

        default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

最佳答案

MNC_SELECT 仅“选择”由低位字指定的菜单项,即如果您可以看到菜单,则该菜单项将突出显示。看起来毫无意义,不确定用例是什么。如果您想真正调用菜单项,您可以使用 MNC_EXECUTE。

此外,在 WM_MENUCHAR 消息的 lParam 中返回的默认“系统菜单”显然只有一项——激活实际系统菜单的一项。在实际的系统菜单中,“最大化”和“关闭”之间的分隔符算作一个菜单项,因此“关闭”菜单项的序号实际上是6。因此这段代码将执行以下操作:如果您按下Alt+ A 并按住Alt,会弹出window系统菜单。如果您随后再次按 A,它将关闭窗口。

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int count = 0;
    switch (message)
    {
    case WM_MENUCHAR:
        count = GetMenuItemCount((HMENU)lParam);
        if (count == 1)
            return MAKELRESULT(0, MNC_EXECUTE);
        else
            return MAKELRESULT(6, MNC_EXECUTE);

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

我认为以下内容显示了消息的完整预期用途,包括 MNC_SELECT 和 MNC_EXECUTE。如果您按住 Alt 并继续按 A,它将弹出系统菜单并从上到下突出显示其中的每个项目,直到它到达最后一个项目(通常是“关闭”),然后执行。请注意在“选择”分隔符时突出显示“最大化”和突出显示“关闭”之间奇怪的 noop。

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int count = 0;
    static int selected = 1;
    switch (message)
    {
    case WM_MENUCHAR:
        count = GetMenuItemCount((HMENU)lParam);
        if (count == 1)
            return MAKELRESULT(0, MNC_EXECUTE);
        else
        {
            if (selected < count)
                return MAKELRESULT(selected++, MNC_SELECT);
            else
                return MAKELRESULT(count - 1, MNC_EXECUTE);
        }

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

关于c++ - 我正在测试 WM_MENUCHAR 但代码没有按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46858310/

相关文章:

c++ - 为什么范围算法在比较器参数之后有投影参数?

c++ - QApplication 子对象在系统关闭时不会被销毁

c - Windows 从 cygwin root 获取类似 Unix 的路径

c++ - 为什么在 C++20 中使用范围时管道运算符不起作用?

c++ - 编译 Qt 应用程序以获得更好的调试信息 (Linux)

c - 为什么可以在 C 中使用多个分号?

c - 使用 openssl 从 tls 证书中有效地获取通用名称

c - NtFsControlFile函数错误

c++ - 如何在 C++ 中获取进程名称

c++ - FindFirstFile 和 FindNextFile 问题