向窗口应用程序发送 WM_CHANGEUISTATE 后,当使用鼠标访问菜单时,助记符不会显示在系统菜单上

Ayr*_*osa 5 c c++ windows winapi

下面的代码将 WM_CHANGEUISTATE 发送到窗口过程本身,并带有参数:

LOWORD(wParam) = UIS_CLEAR

HIWORD(wParam) = UISF_HIDEACCEL

lParam = 0x00000000

当用鼠标左键单击窗口客户区时。

根据此博客雷蒙德陈本应被显示在系统菜单中的助记符,当菜单用鼠标访问。以下段落摘自这篇文章:

清除标志会显示相应的指标。例如,如果您有 UISF_HIDEFOCUS 的 UIS_CLEAR,这意味着您想要显示焦点指示器。

就我而言,我有一个 UISF_HIDEACCEL 的 UIS_CLEAR,这意味着我想显示菜单加速器。

如果您运行下面的代码并在应用程序客户区上用鼠标左键单击,您应该使加速器在系统菜单中可见,即使使用鼠标访问该菜单也是如此。但这不会发生,即,如果您通过左键单击窗口图标或右键单击窗口标题栏来激活系统菜单,则系统菜单中的助记符将不会显示。我错过了什么?

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

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
    WNDCLASSEX  wndclassx;
    wchar_t szAppName[] = L"WM_CHANGEUISTATE";

    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 = szAppName;
    wndclassx.lpszMenuName = nullptr;
    wndclassx.hIconSm = 0;

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

    HWND hWnd = CreateWindow(szAppName, szAppName, 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_LBUTTONUP:
        {
            BOOL b;

            // Note that in my system (Windows 10) I have:
            //
            // Control Panel > Ease of Access > Ease of Access Center > Make the keyboard easier
            //
            // and the option "Underline keyboard shortcuts and access keys" unmarked (the default). Therefore, the value
            // returned in b below will be 0x00000000 (FALSE).

            SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &b, 0);

            // If b = FALSE, the SendMessage() below should underline the accelerators in the System menu, when this menu is
            // accessed with the mouse. But that doesn't work. Why?

            if( !b ) SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL), NULL);
        }
        break;

        // Setting a break in the return below, one can see that WM_CHANGEUISTATE message is being sent to the window and
        // passed to DefWindowProc().

        case WM_CHANGEUISTATE:
        return DefWindowProc(hwnd, message, wParam, lParam);


        case WM_DESTROY:
        PostQuitMessage(0);
        break;

        default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

And*_*ers 1

这似乎是 Windows 中的一个错误/疏忽。在显示系统菜单之前DefWindowProc不发送。WM_QUERYUISTATE菜单实现直接检查最后一个输入事件,如果它来自键盘,则显示下划线。

我尝试发送和发布WM_CHANGEUISTATEWM_UPDATEUISTATE回复WM_ENTERMENULOOPWM_INITMENUPOPUP、 ,WM_NCRBUTTONDOWNWM_SYSCOMMAND没有任何运气。

我能想到的唯一解决方法是通过更改为来破解/ HTSYSMENUicon 菜单:SC_MOUSEMENUSC_KEYMENU

case WM_SYSCOMMAND:
  if ((wParam & 0xFFF0) == SC_MOUSEMENU)
  {
    return SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, ' ');
  }
  return DefWindowProc(hwnd, message, wParam, lParam);
Run Code Online (Sandbox Code Playgroud)

SC_KEYMENU有特殊处理DefWindowProc并强制下划线(如果适用)。

这不处理图标、标题或任务栏上的右键单击...