Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After sending a WM_CHANGEUISTATE to the window app, the mnemonics are not shown on the system menu, when the menu is accessed with the mouse

The code below, sends a WM_CHANGEUISTATE to the window procedure itself, with the arguments:

LOWORD(wParam) = UIS_CLEAR

HIWORD(wParam) = UISF_HIDEACCEL

lParam = 0x00000000

when the window client area is left clicked with the mouse.

According to this blog by Raymond Chen this should make the mnemonics in the System menu to be shown, when the menu is accessed with the mouse. The paragraph below was extracted from this article:

Clearing a flag shows the corresponding indicator. For example, if you have a UIS_CLEAR for UISF_HIDE­FOCUS, that means that you want to show focus indicators.

In my case, I have a UIS_CLEAR for UISF_HIDEACCEL, meaning that I want to show the menu accelerators.

If you run the code below and click with the mouse left button on the app client area, you should make the accelerators visible in the System menu, even when this menu is accessed with the mouse. But that doesn't happen, i.e., if you activate the system menu with a left click on the window's icon, or with a right click on the window's title bar, the mnemonics in the System menu will not be shown. 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;
    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;
}
like image 960
Ayrosa Avatar asked Nov 07 '22 14:11

Ayrosa


1 Answers

This seems like a bug/oversight in Windows. DefWindowProc does not send WM_QUERYUISTATE before displaying the system menu. The menu implementation checks the last input event directly and if it was from a keyboard it displays the underlines.

I tried sending and posting WM_CHANGEUISTATE and WM_UPDATEUISTATE in response to WM_ENTERMENULOOP, WM_INITMENUPOPUP, WM_NCRBUTTONDOWN and WM_SYSCOMMAND without any luck.

The only workaround I was able to come up with is to hack the HTSYSMENU/icon menu by changing SC_MOUSEMENU to SC_KEYMENU:

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

SC_KEYMENU has special handling in DefWindowProc and forces underlines if applicable.

This does not handle right-click on the icon, caption nor task bar...

like image 114
Anders Avatar answered Nov 15 '22 08:11

Anders