Après avoir envoyé un WM_CHANGEUISTATE à l’application window, les mnémoniques ne s’affichent pas dans le menu système lorsque le menu est accessible avec la souris

Le code ci-dessous, envoie un WM_CHANGEUISTATE à la procédure de fenêtre elle-même, avec les arguments:

LOWORD (wParam) = UIS_CLEAR

HIWORD (wParam) = UISF_HIDEACCEL

lParam = 0x00000000

lorsque la zone client de la fenêtre est laissée cliquée avec la souris.

Selon ce blog de Raymond Chen, les mnémoniques du menu Système devraient être affichées lorsque le menu est accessible avec la souris. Le paragraphe ci-dessous a été extrait de cet article:

Effacer un indicateur affiche l’indicateur correspondant. Par exemple, si vous avez un UIS_CLEAR pour UISF_HIDEFOCUS, cela signifie que vous souhaitez afficher des indicateurs de focus.

Dans mon cas, j’ai un UIS_CLEAR pour UISF_HIDEACCEL, ce qui signifie que je veux afficher les accélérateurs de menu.

Si vous exécutez le code ci-dessous et cliquez avec le bouton gauche de la souris sur la zone client de l’application, vous devez rendre les accélérateurs visibles dans le menu Système, même lorsque vous accédez à ce menu avec la souris. Mais cela ne se produit pas, c.-à-d. Si vous activez le menu système avec un clic gauche sur l’icône de la fenêtre ou avec un clic droit sur la barre de titre de la fenêtre, les mnémoniques du menu Système ne seront pas affichées. Qu’est-ce que je rate?

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

Cela semble être un bogue / oubli dans Windows. DefWindowProc n’envoie pas WM_QUERYUISTATE avant d’afficher le menu système. L’implémentation du menu vérifie directement le dernier événement saisi et, s’il s’agissait d’un clavier, il affiche les soulignements.

J’ai essayé d’envoyer et de poster WM_CHANGEUISTATE et WM_UPDATEUISTATE en réponse à WM_ENTERMENULOOP , WM_INITMENUPOPUP , WM_NCRBUTTONDOWN et WM_SYSCOMMAND sans aucun chance.

La seule solution que j’ai pu trouver consiste à pirater le menu HTSYSMENU / icon en changeant SC_MOUSEMENU en 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 a un traitement spécial dans DefWindowProc et force le cas échéant.

Cela ne gère pas le clic droit sur l’icône, la légende ni la barre des tâches …