简体   繁体   English

我正在测试WM_MENUCHAR,但代码无法按预期工作

[英]I'm testing WM_MENUCHAR but the code doesn't work as expected

The example below uses an app window with just the System menu, and I'm trying to understand how the message WM_MENUCHAR is supposed to be used. 下面的示例使用仅带有System菜单的应用程序窗口,我正在尝试了解如何使用消息WM_MENUCHAR The MSDN doc for the message says: 该消息的MSDN文档说:

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. 此消息将发送到拥有该菜单的窗口。

In the example below whenever I press the keys Alt + A , Alt + B , or whatever, the WM_MENUCHAR is sent to the window proc. 在下面的示例中,每当我按下Alt + AAlt + B或其他任何键时, WM_MENUCHAR被发送到窗口proc。

I'm assuming that the Alt key activates the system menu, since all the WM_MENUCHAR messages sent to WndProc() have HIWORD(wParam) = MF_SYSMENU and the reason why the message is sent to the window is due to the fact that the program doesn't have an accelerator table, nor does the system menu contains any mnemonic. 我假设Alt键激活系统菜单,因为发送到WndProc()所有WM_MENUCHAR消息都有HIWORD(wParam) = MF_SYSMENU并且消息被发送到窗口的原因是由于程序没有没有加速器表,系统菜单也不包含任何助记符。

This is point that is not clear to me: in response to the WM_MENUCHAR the code returns MAKELPARAM(5, MNC_SELECT) but this return value doesn't invoke the Close menu item in the System menu (remember that 5 is the base-0 order number for this menu item in the System menu). 这一点对我来说是不明确的:响应WM_MENUCHAR ,代码返回MAKELPARAM(5, MNC_SELECT)但该返回值不会调用System菜单中的Close菜单项(请记住5是base-0顺序)系统菜单中此菜单项的编号。 What am I missing? 我错过了什么?

#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 merely "selects" the menu item specified by the low word, ie if you could see the menu that menu item would be highlighted. MNC_SELECT仅“选择”由低位字指定的菜单项,即,如果您可以看到菜单项将突出显示的菜单。 Seems pretty pointless, not sure what the use case is. 看起来毫无意义,不确定用例是什么。 If you wanted to actually invoke the menu item, you would use MNC_EXECUTE. 如果您想实际调用菜单项,则可以使用MNC_EXECUTE。

Additionally, the default "system menu" returned in lParam of the WM_MENUCHAR message apparently has only one item -- one that activates the actual system menu. 此外,在WM_MENUCHAR消息的lParam中返回的默认“系统菜单”显然只有一个项目 - 一个用于激活实际系统菜单。 In the actual system menu, the separator between "maximize" and "close" counts as a menu item, so the ordinal number of the "close" menu item is actually 6. So this code will do the following: if you press Alt+A and keep holding Alt, it will pop up the window system menu. 在实际的系统菜单中,“最大化”和“关闭”之间的分隔符计为菜单项,因此“关闭”菜单项的序号实际为6.因此,此代码将执行以下操作:如果按Alt + A并保持按住Alt,它会弹出窗口系统菜单。 If you then press A again, it will close the 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;
}

And the following I believe shows the full intended use of the message, including MNC_SELECT and MNC_EXECUTE. 我认为以下内容显示了消息的完整用途,包括MNC_SELECT和MNC_EXECUTE。 If you hold down Alt and keep pressing A it will pop up the system menu and highlight every item on it from top to bottom until it gets to the last item (typically "close") which it then executes. 如果您按住Alt并按住A键,它将弹出系统菜单并从上到下突出显示它上面的每个项目,直到它到达最后一个项目(通常是“关闭”)然后执行。 Note the weird noop between highlighting "maximize" and highlighting "close" when it "selects" the separator. 注意当突出显示“最大化”和突出显示“关闭”时,它会“选择”分隔符。

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM