[ROSTESTS] Add an interactive "menuaccels" test to view window keyboard messages

This test allows viewing the window keyboard messages (`WM_KEYDOWN/UP`,
`WM_CHAR`, ...) that are sent by Win32k whenever the user presses keys,
including what happens when Alt+Numpad combinations are typed in.

The application shows the list of received messages in a format similar
to that of Microsoft Spy++.
This commit is contained in:
Hermès Bélusca-Maïto
2026-07-04 19:28:17 +02:00
parent 6117b1bdef
commit c67be4ceff
7 changed files with 1099 additions and 0 deletions
@@ -1,5 +1,6 @@
add_subdirectory(biditext)
add_subdirectory(editalign)
add_subdirectory(menuaccels)
add_subdirectory(messagebox)
add_subdirectory(paintdesktop)
add_subdirectory(psmtest)
@@ -0,0 +1,6 @@
add_rc_deps(menuaccels.rc ${CMAKE_CURRENT_SOURCE_DIR}/menuaccels.ico)
add_executable(menuaccels menuaccels.c menuaccels.rc)
set_module_type(menuaccels win32gui UNICODE)
add_importlibs(menuaccels user32 msvcrt kernel32)
add_rostests_file(TARGET menuaccels SUBDIR suppl)
@@ -0,0 +1,317 @@
/*
* PROJECT: ReactOS Tests
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
* PURPOSE: Common data
* COPYRIGHT: Copyright 2026 Hermès Bélusca-Maïto <[email protected]>
*/
/*
* References:
* https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
* https://bsakatu.net/doc/virtual-key-of-windows/
*/
static const PCSTR VkNames[] =
{
/* 0x00 */
"VK_NONE",
"VK_LBUTTON",
"VK_RBUTTON",
"VK_CANCEL",
"VK_MBUTTON",
// #if (_WIN32_WINNT >= 0x0500)
"VK_XBUTTON1",
"VK_XBUTTON2",
// #endif /* _WIN32_WINNT >= 0x0500 */
NULL, // Reserved
"VK_BACK",
"VK_TAB",
NULL, // Reserved
NULL, // Reserved
"VK_CLEAR",
"VK_RETURN",
NULL, // Unassigned
NULL, // Unassigned
/* 0x10 */
"VK_SHIFT",
"VK_CONTROL",
"VK_MENU",
"VK_PAUSE",
"VK_CAPITAL",
"VK_KANA-VK_HANGUL",
"VK_IME_ON",
"VK_JUNJA",
"VK_FINAL",
"VK_HANJA-VK_KANJI",
"VK_IME_OFF",
"VK_ESCAPE",
"VK_CONVERT",
"VK_NONCONVERT",
"VK_ACCEPT",
"VK_MODECHANGE",
/* 0x20 */
"VK_SPACE",
"VK_PRIOR",
"VK_NEXT",
"VK_END",
"VK_HOME",
"VK_LEFT",
"VK_UP",
"VK_RIGHT",
"VK_DOWN",
"VK_SELECT",
"VK_PRINT",
"VK_EXECUTE",
"VK_SNAPSHOT",
"VK_INSERT",
"VK_DELETE",
"VK_HELP",
/* 0x30 */
/* VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39) */
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
NULL, // Undefined
NULL, // Undefined
NULL, // Undefined
NULL, // Undefined
NULL, // Undefined
NULL, // Undefined
/* 0x40 */
NULL, // Undefined
/* VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A) */
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
/* 0x50 */
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"VK_LWIN",
"VK_RWIN",
"VK_APPS",
NULL, // Reserved
"VK_SLEEP",
/* 0x60 */
"VK_NUMPAD0",
"VK_NUMPAD1",
"VK_NUMPAD2",
"VK_NUMPAD3",
"VK_NUMPAD4",
"VK_NUMPAD5",
"VK_NUMPAD6",
"VK_NUMPAD7",
"VK_NUMPAD8",
"VK_NUMPAD9",
"VK_MULTIPLY",
"VK_ADD",
"VK_SEPARATOR",
"VK_SUBTRACT",
"VK_DECIMAL",
"VK_DIVIDE",
/* 0x70 */
"VK_F1",
"VK_F2",
"VK_F3",
"VK_F4",
"VK_F5",
"VK_F6",
"VK_F7",
"VK_F8",
"VK_F9",
"VK_F10",
"VK_F11",
"VK_F12",
"VK_F13",
"VK_F14",
"VK_F15",
"VK_F16",
/* 0x80 */
"VK_F17",
"VK_F18",
"VK_F19",
"VK_F20",
"VK_F21",
"VK_F22",
"VK_F23",
"VK_F24",
// #if (_WIN32_WINNT >= 0x0604)
"VK_NAVIGATION_VIEW", // Reserved
"VK_NAVIGATION_MENU", // Reserved
"VK_NAVIGATION_UP", // Reserved
"VK_NAVIGATION_DOWN", // Reserved
"VK_NAVIGATION_LEFT", // Reserved
"VK_NAVIGATION_RIGHT", // Reserved
"VK_NAVIGATION_ACCEPT", // Reserved
"VK_NAVIGATION_CANCEL", // Reserved
// #endif /* _WIN32_WINNT >= 0x0604 */
/* 0x90 */
"VK_NUMLOCK",
"VK_SCROLL",
/*
* OEM specific
*/
/* NEC PC-9800 kbd definitions */
// "VK_OEM_NEC_EQUAL", // '=' key on numpad // Same value as VK_OEM_FJ_JISHO
/*
* Fujitsu/OASYS kbd definitions
*/
"VK_OEM_FJ_JISHO", // 'Dictionary' key
"VK_OEM_FJ_MASSHOU", // 'Unregister word' key
"VK_OEM_FJ_TOUROKU", // 'Register word' key
"VK_OEM_FJ_LOYA", // 'Left OYAYUBI' key
"VK_OEM_FJ_ROYA", // 'Right OYAYUBI' key
NULL, // Unassigned
NULL, // Unassigned
NULL, // Unassigned
NULL, // Unassigned
NULL, // Unassigned
NULL, // Unassigned
NULL, // Unassigned
NULL, // Unassigned
NULL, // Unassigned
/* 0xA0 */
"VK_LSHIFT",
"VK_RSHIFT",
"VK_LCONTROL",
"VK_RCONTROL",
"VK_LMENU",
"VK_RMENU",
// #if (_WIN32_WINNT >= 0x0500)
"VK_BROWSER_BACK",
"VK_BROWSER_FORWARD",
"VK_BROWSER_REFRESH",
"VK_BROWSER_STOP",
"VK_BROWSER_SEARCH",
"VK_BROWSER_FAVORITES",
"VK_BROWSER_HOME",
"VK_VOLUME_MUTE",
"VK_VOLUME_DOWN",
"VK_VOLUME_UP",
/* 0xB0 */
"VK_MEDIA_NEXT_TRACK",
"VK_MEDIA_PREV_TRACK",
"VK_MEDIA_STOP",
"VK_MEDIA_PLAY_PAUSE",
"VK_LAUNCH_MAIL",
"VK_LAUNCH_MEDIA_SELECT",
"VK_LAUNCH_APP1",
"VK_LAUNCH_APP2",
// #endif /* _WIN32_WINNT >= 0x0500 */
NULL, // Reserved
NULL, // Reserved
"VK_OEM_1", // ';:' for US
"VK_OEM_PLUS", // '+' any country
"VK_OEM_COMMA", // ',' any country
"VK_OEM_MINUS", // '-' any country
"VK_OEM_PERIOD", // '.' any country
"VK_OEM_2", // '/?' for US
/* 0xC0 */
"VK_OEM_3", // '`~' for US
NULL, // Reserved
NULL, // Reserved
// #if (_WIN32_WINNT >= 0x0604)
"VK_GAMEPAD_A",
"VK_GAMEPAD_B",
"VK_GAMEPAD_X",
"VK_GAMEPAD_Y",
"VK_GAMEPAD_RIGHT_SHOULDER",
"VK_GAMEPAD_LEFT_SHOULDER",
"VK_GAMEPAD_LEFT_TRIGGER",
"VK_GAMEPAD_RIGHT_TRIGGER",
"VK_GAMEPAD_DPAD_UP",
"VK_GAMEPAD_DPAD_DOWN",
"VK_GAMEPAD_DPAD_LEFT",
"VK_GAMEPAD_DPAD_RIGHT",
"VK_GAMEPAD_MENU",
/* 0xD0 */
"VK_GAMEPAD_VIEW",
"VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON",
"VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON",
"VK_GAMEPAD_LEFT_THUMBSTICK_UP",
"VK_GAMEPAD_LEFT_THUMBSTICK_DOWN",
"VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT",
"VK_GAMEPAD_LEFT_THUMBSTICK_LEFT",
"VK_GAMEPAD_RIGHT_THUMBSTICK_UP",
"VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN",
"VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT",
"VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT",
// #endif /* _WIN32_WINNT >= 0x0604 */
"VK_OEM_4", // '[{' for US
"VK_OEM_5", // '\|' for US
"VK_OEM_6", // ']}' for US
"VK_OEM_7", // ''"' for US
"VK_OEM_8",
/* 0xE0 */
NULL, // Reserved
"VK_OEM_AX", // 'AX' key on Japanese AX kbd // OEM specific
"VK_OEM_102", // "<>" or "\|" on RT 102-key kbd // OEM specific
"VK_ICO_HELP", // Help key on ICO // OEM specific
"VK_ICO_00", // 00 key on ICO // OEM specific
// #if (WINVER >= 0x0400)
"VK_PROCESSKEY",
// #endif /* WINVER >= 0x0400 */
"VK_ICO_CLEAR", // OEM specific
// #if (_WIN32_WINNT >= 0x0500)
"VK_PACKET",
// #endif /* _WIN32_WINNT >= 0x0500 */
NULL, // Unassigned
/*
* OEM specific
* Nokia/Ericsson definitions
*/
"VK_OEM_RESET",
"VK_OEM_JUMP",
"VK_OEM_PA1",
"VK_OEM_PA2",
"VK_OEM_PA3",
"VK_OEM_WSCTRL",
"VK_OEM_CUSEL",
/* 0xF0 */
"VK_OEM_ATTN",
"VK_OEM_FINISH",
"VK_OEM_COPY",
"VK_OEM_AUTO",
"VK_OEM_ENLW",
"VK_OEM_BACKTAB",
/* END of OEM definitions */
"VK_ATTN",
"VK_CRSEL",
"VK_EXSEL",
"VK_EREOF",
"VK_PLAY",
"VK_ZOOM",
"VK_NONAME", // Reserved
"VK_PA1",
"VK_OEM_CLEAR",
"VK_UNKNOWN"
};
C_ASSERT(_countof(VkNames) == 0x100);
/* EOF */
@@ -0,0 +1,612 @@
/*
* PROJECT: ReactOS Tests
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
* PURPOSE: Tests the interaction between menus, accelerators, and Alt+Numpad keys
* COPYRIGHT: Copyright 2026 Hermès Bélusca-Maïto <[email protected]>
*/
/* HEADERS *******************************************************************/
/* C Headers */
#include <stdio.h>
#include <stdlib.h>
/* PSDK Headers */
#include <windef.h>
#include <winbase.h>
#include <winuser.h>
#include "resource.h"
/* GLOBALS *******************************************************************/
static HINSTANCE g_hInst;
static const PCWSTR szWindowClass = L"TESTAPP";
static HWND g_hEdit, g_hMsgDump;
static ATOM g_aHotKey1, g_aHotKey2;
static HHOOK g_hHook1, g_hHook2, g_hHook3;
static BOOL g_bShowEdit = TRUE;
static BOOL g_bXlatAccels = TRUE;
#define EDIT_STYLE_WRAP (WS_CHILD | WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_NOHIDESEL)
#define EDIT_STYLE (EDIT_STYLE_WRAP | WS_HSCROLL | ES_AUTOHSCROLL)
/* FUNCTIONS *****************************************************************/
LRESULT CALLBACK
HookProcCallWnd(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
LRESULT CALLBACK
HookProcGetMsg(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
LRESULT CALLBACK
HookProcMessage(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
LRESULT CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static ATOM
MyRegisterClass(_In_ HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
HICON hSmIcon, hBgIcon;
hSmIcon = LoadImageW(hInstance,
MAKEINTRESOURCEW(IDI_TESTAPP),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_SHARED);
hBgIcon = LoadImageW(hInstance,
MAKEINTRESOURCEW(IDI_TESTAPP),
IMAGE_ICON,
GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON),
LR_SHARED);
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = hBgIcon;
wcex.hCursor = LoadCursorW(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTAPP);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = hSmIcon;
return RegisterClassExW(&wcex);
}
int APIENTRY
wWinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
HWND hWnd;
MSG msg;
HACCEL hAccelTable;
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
/* Register ourselves */
if (!MyRegisterClass(hInstance))
return FALSE;
g_hInst = hInstance;
/* Create the main window and load the accelerators */
hWnd = CreateWindowW(szWindowClass, L"MyTestApp", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
if (!hWnd)
return FALSE;
/* Show ourselves */
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
/* Set up the accelerators, hotkey, and window hooks */
hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(IDC_TESTAPP));
if (!hAccelTable)
OutputDebugStringA("LoadAcceleratorsW() failed\n");
g_aHotKey1 = GlobalAddAtomW(L"TESTAPP Alt 4 Hotkey");
if (!RegisterHotKey(NULL, g_aHotKey1, MOD_ALT, VK_NUMPAD4))
OutputDebugStringA("RegisterHotKey() failed\n"); //DPRINT1("RegisterHotKey failed with %lu\n", GetLastError());
g_aHotKey2 = GlobalAddAtomW(L"TESTAPP Alt 5 Hotkey");
if (!RegisterHotKey(hWnd, g_aHotKey2, MOD_ALT, VK_NUMPAD5))
OutputDebugStringA("RegisterHotKey() failed\n"); //DPRINT1("RegisterHotKey failed with %lu\n", GetLastError());
g_hHook1 = SetWindowsHookExW(WH_CALLWNDPROC, HookProcCallWnd, NULL, GetCurrentThreadId());
g_hHook2 = SetWindowsHookExW(WH_GETMESSAGE, HookProcGetMsg, NULL, GetCurrentThreadId());
g_hHook3 = SetWindowsHookExW(WH_MSGFILTER, HookProcMessage, NULL, GetCurrentThreadId());
/* Pump the queue */
while (GetMessageW(&msg, NULL, 0, 0))
{
if ((msg.message == WM_HOTKEY) && (msg.wParam == g_aHotKey1))
{
// if (LOWORD(msg.lParam) == MOD_ALT && HIWORD(msg.lParam) == VK_NUMPAD4)
PostMessageW(hWnd, WM_COMMAND, MAKEWPARAM(IDM_FOUR, 1), 0);
continue;
}
if (!g_bXlatAccels || !TranslateAcceleratorW(hWnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
/* Cleanup and return */
UnhookWindowsHookEx(g_hHook3);
UnhookWindowsHookEx(g_hHook2);
UnhookWindowsHookEx(g_hHook1);
UnregisterHotKey(NULL, g_aHotKey2);
GlobalDeleteAtom(g_aHotKey2);
UnregisterHotKey(NULL, g_aHotKey1);
GlobalDeleteAtom(g_aHotKey1);
return (int)msg.wParam;
}
static void
SetMenuString(
_In_ HMENU hMenu,
_In_ UINT uCommand,
_In_ LPCWSTR pszString)
{
WCHAR szMenu[128];
if (IS_INTRESOURCE(pszString))
{
LoadStringW(g_hInst, PtrToUlong(pszString), szMenu, _countof(szMenu));
pszString = szMenu;
}
ModifyMenuW(hMenu, uCommand, MF_BYCOMMAND | MF_STRING, uCommand, pszString);
}
static void
UpdateEditWnd(_In_ HWND hWndParent)
{
/* Change the menu command string */
SetMenuString(GetMenu(hWndParent), IDM_SHOWHIDEEDIT,
g_bShowEdit ? MAKEINTRESOURCEW(IDS_EDITHIDE)
: MAKEINTRESOURCEW(IDS_EDITSHOW));
/* Show or hide the editable edit control and set focus to it if needed */
ShowWindow(g_hEdit, g_bShowEdit ? SW_SHOW : SW_HIDE);
if (g_bShowEdit)
SetFocus(g_hEdit);
/* Re-arrange controls */
PostMessageW(hWndParent, WM_SIZE, 0, 0);
}
static void
UpdateAccelXlatMenu(_In_ HWND hWnd)
{
/* Change the menu command string */
SetMenuString(GetMenu(hWnd), IDM_ACCELXLAT,
g_bXlatAccels ? MAKEINTRESOURCEW(IDS_ACCELNOXLAT)
: MAKEINTRESOURCEW(IDS_ACCELXLAT));
}
static void
DoCreateEditChildren(_In_ HWND hWndParent)
{
g_hEdit = CreateWindowW(/*WC_EDITW*/ L"Edit", L"", EDIT_STYLE_WRAP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
hWndParent, NULL, g_hInst, NULL);
SendMessageW(g_hEdit, EM_LIMITTEXT, 0, 0);
g_hMsgDump = CreateWindowW(/*WC_EDITW*/ L"Edit", L"", EDIT_STYLE_WRAP | ES_READONLY,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
hWndParent, NULL, g_hInst, NULL);
SendMessageW(g_hMsgDump, EM_LIMITTEXT, 0, 0);
/* Finally shows new edit controls */
ShowWindow(g_hMsgDump, SW_SHOW);
UpdateEditWnd(hWndParent);
/* Re-arrange controls */
PostMessageW(hWndParent, WM_SIZE, 0, 0);
}
static void
AppendTextLineA(
_In_ HWND hEdit,
_In_ PCSTR pszLine)
{
DWORD l, r;
SendMessageW(hEdit, EM_GETSEL, (WPARAM)&l, (LPARAM)&r);
//SendMessageW(hEdit, EM_SETSEL, -1, -1);
SendMessageW(hEdit, EM_SETSEL, GetWindowTextLengthW(hEdit), -1);
SendMessageA(hEdit, EM_REPLACESEL, FALSE, (LPARAM)pszLine);
SendMessageW(hEdit, EM_SETSEL, l, r);
}
static void dump_printf(const CHAR *fmt, ...)
{
CHAR szText[512];
int len;
va_list va;
va_start(va, fmt);
len = _vsnprintf(szText, _countof(szText) - 2, fmt, va);
if (len < 0)
*szText = ANSI_NULL;
va_end(va);
/* Ensure the line ends with CR-LF */
len = min(len, _countof(szText) - 3);
szText[len + 0] = '\r';
szText[len + 1] = '\n';
szText[len + 2] = ANSI_NULL;
/* Append the text line */
AppendTextLineA(g_hMsgDump, szText);
//DbgPrint("TEST: %s\n", szText);
OutputDebugStringA(szText);
}
#define MSGDUMP_PRINTF dump_printf
//#include "msgdump.h"
#define MSGDUMP_PREFIX ""
#define MSGDUMP_API WINAPI
/* For 'PCSTR VkNames[]' */
#include "data.c"
static int
GetVkName(
_In_ UINT vk,
_Out_writes_(cchBuff) PSTR pszBuffer,
_In_ SIZE_T cchBuff)
{
PCSTR vkName;
if (vk > 0xFF)
vk = 0xFF; // VK_UNKNOWN
vkName = VkNames[vk];
if (!vkName)
{
return _snprintf(pszBuffer, cchBuff, "%lX", vk);
}
else
{
if ((0x30 <= vk && vk <= 0x39) || (0x41 <= vk && vk <= 0x5A))
return _snprintf(pszBuffer, cchBuff, "'%s'", vkName);
else
return _snprintf(pszBuffer, cchBuff, "%s", vkName);
}
}
static __inline void MSGDUMP_API
MD_OnKey(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ BOOL fDown, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
UINT vk = (UINT)wParam;
UINT cRepeat = (UINT)LOWORD(lParam);
USHORT flags = HIWORD(lParam);
UCHAR ScanCode = (flags & 0xFF);
CHAR szVkName[100];
GetVkName(vk, szVkName, _countof(szVkName));
if (fDown)
{
MSGDUMP_PRINTF("%p %c %sWM_KEYDOWN\tnVirtKey:%s cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, szVkName, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
else
{
MSGDUMP_PRINTF("%p %c %sWM_KEYUP\tnVirtKey:%s cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, szVkName, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
}
static __inline void MSGDUMP_API
MD_OnChar(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
WCHAR ch = (WCHAR)wParam;
UINT cRepeat = (UINT)LOWORD(lParam);
USHORT flags = HIWORD(lParam);
UCHAR ScanCode = (flags & 0xFF);
MSGDUMP_PRINTF("%p %c %sWM_CHAR\tchCharCode:'%04X' (%u) cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, ch, ch, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
static __inline void MSGDUMP_API
MD_OnDeadChar(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
WCHAR ch = (WCHAR)wParam;
UINT cRepeat = (UINT)LOWORD(lParam);
USHORT flags = HIWORD(lParam);
UCHAR ScanCode = (flags & 0xFF);
MSGDUMP_PRINTF("%p %c %sWM_DEADCHAR\tchCharCode:'%04X' (%u) cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, ch, ch, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
static __inline void MSGDUMP_API
MD_OnSysKey(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ BOOL fDown, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
UINT vk = (UINT)wParam;
UINT cRepeat = (UINT)LOWORD(lParam);
USHORT flags = HIWORD(lParam);
UCHAR ScanCode = (flags & 0xFF);
CHAR szVkName[100];
GetVkName(vk, szVkName, _countof(szVkName));
if (fDown)
{
MSGDUMP_PRINTF("%p %c %sWM_SYSKEYDOWN\tnVirtKey:%s cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, szVkName, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
else
{
MSGDUMP_PRINTF("%p %c %sWM_SYSKEYUP\tnVirtKey:%s cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, szVkName, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
}
static __inline void MSGDUMP_API
MD_OnSysChar(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
WCHAR ch = (WCHAR)wParam;
UINT cRepeat = (UINT)LOWORD(lParam);
USHORT flags = HIWORD(lParam);
UCHAR ScanCode = (flags & 0xFF);
MSGDUMP_PRINTF("%p %c %sWM_SYSCHAR\tchCharCode:'%04X' (%u) cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, ch, ch, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
static __inline void MSGDUMP_API
MD_OnSysDeadChar(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
WCHAR ch = (WCHAR)wParam;
UINT cRepeat = (UINT)LOWORD(lParam);
USHORT flags = HIWORD(lParam);
UCHAR ScanCode = (flags & 0xFF);
MSGDUMP_PRINTF("%p %c %sWM_SYSDEADCHAR\tchCharCode:'%04X' (%u) cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, ch, ch, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
static __inline void MSGDUMP_API
MD_OnUniChar(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
WCHAR ch = (WCHAR)wParam;
UINT cRepeat = (UINT)LOWORD(lParam);
USHORT flags = HIWORD(lParam);
UCHAR ScanCode = (flags & 0xFF);
MSGDUMP_PRINTF("%p %c %sWM_UNICHAR\tchCharCode:'%04X' (%u) cRepeat:%u ScanCode:%02X fExtended:%u fAltDown:%u fRepeat:%u fUp:%u [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, ch, ch, cRepeat, ScanCode,
!!(flags & KF_EXTENDED), !!(flags & KF_ALTDOWN), !!(flags & KF_REPEAT), !!(flags & KF_UP),
wParam, lParam);
}
static __inline void MSGDUMP_API
MD_OnHotKey(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
int idHotKey = (int)wParam;
UINT fuModifiers = (UINT)LOWORD(lParam);
UINT vk = (UINT)HIWORD(lParam);
CHAR szVkName[100];
GetVkName(vk, szVkName, _countof(szVkName));
// TODO: Convert fuModifiers to "human-readable" names.
MSGDUMP_PRINTF("%p %c %sWM_HOTKEY\tidHotKey:%04X fuModifiers:%u nVirtKey:%s [wParam:%08lX lParam:%08lX]",
hwnd, Pfx, MSGDUMP_PREFIX, idHotKey, fuModifiers, szVkName,
wParam, lParam);
}
static __inline LRESULT MSGDUMP_API
MD_msgdump(_In_ CHAR Pfx, _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN: MD_OnKey(Pfx, hwnd, TRUE, wParam, lParam); break;
case WM_KEYUP: MD_OnKey(Pfx, hwnd, FALSE, wParam, lParam); break;
case WM_CHAR: MD_OnChar(Pfx, hwnd, wParam, lParam); break;
case WM_DEADCHAR: MD_OnDeadChar(Pfx, hwnd, wParam, lParam); break;
case WM_SYSKEYDOWN: MD_OnSysKey(Pfx, hwnd, TRUE, wParam, lParam); break;
case WM_SYSKEYUP: MD_OnSysKey(Pfx, hwnd, FALSE, wParam, lParam); break;
case WM_SYSCHAR: MD_OnSysChar(Pfx, hwnd, wParam, lParam); break;
case WM_SYSDEADCHAR: MD_OnSysDeadChar(Pfx, hwnd, wParam, lParam); break;
case WM_UNICHAR: MD_OnUniChar(Pfx, hwnd, wParam, lParam); break;
case WM_HOTKEY: MD_OnHotKey(Pfx, hwnd, wParam, lParam); break;
case WM_IME_CHAR: break; // TODO
case WM_IME_KEYDOWN: break; // TODO
case WM_IME_KEYUP: break; // TODO
}
return 0;
}
LRESULT CALLBACK
HookProcCallWnd(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
PCWPSTRUCT pcwp;
//if (nCode < 0) /* Do not process the message */
// return CallNextHookEx(g_hHook1, nCode, wParam, lParam);
pcwp = (PCWPSTRUCT)lParam;
MD_msgdump('C', pcwp->hwnd, pcwp->message, pcwp->wParam, pcwp->lParam);
return CallNextHookEx(g_hHook1, nCode, wParam, lParam);
}
LRESULT CALLBACK
HookProcGetMsg(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
PMSG pmsg;
//if (nCode < 0) /* Do not process the message */
// return CallNextHookEx(g_hHook2, nCode, wParam, lParam);
pmsg = (PMSG)lParam;
MD_msgdump('G', pmsg->hwnd, pmsg->message, pmsg->wParam, pmsg->lParam);
return CallNextHookEx(g_hHook2, nCode, wParam, lParam);
}
LRESULT CALLBACK
HookProcMessage(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
PMSG pmsg;
//if (nCode < 0) /* Do not process the message */
// return CallNextHookEx(g_hHook3, nCode, wParam, lParam);
pmsg = (PMSG)lParam;
MD_msgdump('M', pmsg->hwnd, pmsg->message, pmsg->wParam, pmsg->lParam);
return CallNextHookEx(g_hHook3, nCode, wParam, lParam);
}
LRESULT CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// MD_msgdump(hWnd, uMsg, wParam, lParam);
switch (uMsg)
{
case WM_CREATE:
DoCreateEditChildren(hWnd);
UpdateAccelXlatMenu(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_HOTKEY:
if (wParam == g_aHotKey1) // && LOWORD(lParam) == MOD_ALT && HIWORD(lParam) == VK_NUMPAD4
PostMessageW(hWnd, WM_COMMAND, MAKEWPARAM(IDM_FOUR, 1), 0);
else if (wParam == g_aHotKey2) // && LOWORD(lParam) == MOD_ALT && HIWORD(lParam) == VK_NUMPAD5
PostMessageW(hWnd, WM_COMMAND, MAKEWPARAM(IDM_FIVE, 1), 0);
break;
case WM_COMMAND:
{
WORD wmId = LOWORD(wParam);
//WORD wmEvent = HIWORD(wParam);
switch (wmId)
{
case IDM_ONE: case IDM_TWO: case IDM_THREE:
case IDM_FOUR: case IDM_FIVE:
case IDM_THIRTEEN: case IDM_FOURTEEN: case IDM_FIFTEEN:
{
/* See `IDC_TESTAPP ACCELERATORS` */
static const PCWSTR Messages[] =
{L"One (Alt '1' ASCII)", L"Two (Alt 2 VIRTKEY)", L"Three (Alt VK_NUMPAD3 VIRTKEY)",
L"Four (Alt VK_NUMPAD4 -- WM_HOTKEY thread)", L"Five (Alt VK_NUMPAD5 -- WM_HOTKEY hWnd)",
L"Thirteen (Alt 'D' ASCII)", L"Fourteen (Alt E VIRTKEY)", L"Fifteen (Alt F VIRTKEY)"};
if (IDM_ONE <= wmId && wmId <= IDM_FIVE)
wmId -= IDM_ONE;
else if (IDM_THIRTEEN <= wmId && wmId <= IDM_FIFTEEN)
wmId -= IDM_THIRTEEN - (IDM_FIVE - IDM_ONE + 1);
MessageBoxW(hWnd, Messages[wmId], L"Info", MB_OK);
break;
}
case IDM_SHOWHIDEEDIT:
g_bShowEdit = !g_bShowEdit;
UpdateEditWnd(hWnd);
break;
case IDM_ACCELXLAT:
g_bXlatAccels = !g_bXlatAccels;
UpdateAccelXlatMenu(hWnd);
break;
case IDM_CLEARLOG:
SendMessageW(g_hMsgDump, WM_SETTEXT, 0, (LPARAM)L"");
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
break;
}
case WM_SIZE:
{
RECT rc;
GetClientRect(hWnd, &rc);
if (g_bShowEdit)
{
MoveWindow(g_hEdit, 0, 0, rc.right, rc.bottom / 2, TRUE);
MoveWindow(g_hMsgDump, 0, rc.bottom / 2, rc.right, rc.bottom / 2, TRUE);
}
else
{
MoveWindow(g_hMsgDump, 0, 0, rc.right, rc.bottom, TRUE);
}
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
}
default:
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
return 0;
}
/* EOF */
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -0,0 +1,116 @@
/*
* PROJECT: ReactOS Tests
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
* PURPOSE: Resources
* COPYRIGHT: Copyright 2026 Hermès Bélusca-Maïto <[email protected]>
*/
#include <winnt.rh>
#include <winuser.rh>
#include "resource.h"
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Menu & Accelerators Test"
#define REACTOS_STR_INTERNAL_NAME "menuaccels"
#define REACTOS_STR_ORIGINAL_FILENAME "menuaccels.exe"
#include <reactos/version.rc>
IDI_TESTAPP ICON "menuaccels.ico"
IDC_TESTAPP MENU
BEGIN
POPUP "Zero (&0)"
BEGIN
MENUITEM "<IDS_EDITSHOWHIDE>", IDM_SHOWHIDEEDIT
MENUITEM "<IDS_ACCELXLAT>", IDM_ACCELXLAT
MENUITEM "&Clear Log", IDM_CLEARLOG
MENUITEM "&Quit", IDM_EXIT
END
POPUP "One (&1)"
BEGIN
MENUITEM "Alpha", ID_ONE_ALPHA
END
POPUP "Two (&2)"
BEGIN
MENUITEM "Bravo", ID_TWO_BRAVO
END
POPUP "Three (&3)"
BEGIN
MENUITEM "Charlie", ID_THREE_CHARLIE
END
POPUP "Four (&4)"
BEGIN
MENUITEM "Delta", ID_FOUR_DELTA
END
POPUP "Five (&5)"
BEGIN
MENUITEM "Echo", ID_FIVE_ECHO
END
POPUP "Six (&6)"
BEGIN
MENUITEM "Foxtrot", ID_SIX_FOXTROT
END
POPUP "Seven (&7)"
BEGIN
MENUITEM "Golf", ID_SEVEN_GOLF
END
POPUP "Eight (&8)"
BEGIN
MENUITEM "Hotel", ID_EIGHT_HOTEL
END
POPUP "Nine (&9)"
BEGIN
MENUITEM "India", ID_NINE_INDIA
END
POPUP "Ten (&A)"
BEGIN
MENUITEM "Juliet", ID_TEN_JULIET
END
POPUP "Eleven (&B)"
BEGIN
MENUITEM "Kilo", ID_ELEVEN_KILO
END
POPUP "Twelve (&C)"
BEGIN
MENUITEM "Lima", ID_TWELVE_LIMA
END
POPUP "Thirteen (&D)"
BEGIN
MENUITEM "Mike", ID_THIRTEEN_MIKE
END
POPUP "Fourteen (&E)"
BEGIN
MENUITEM "November", ID_FOURTEEN_NOVEMBER
END
POPUP "Fifteen (&F)"
BEGIN
MENUITEM "Oscar", ID_FIFTEEN_OSCAR
END
POPUP "Plus (&+)"
BEGIN
MENUITEM "Papa", ID_PLUS_PAPA
END
POPUP "Dot (&.)"
BEGIN
MENUITEM "Quebec", ID_DOT_QUEBEC
END
END
IDC_TESTAPP ACCELERATORS
BEGIN
"1", IDM_ONE, ASCII, ALT, NOINVERT
"2", IDM_TWO, VIRTKEY, ALT, NOINVERT
VK_NUMPAD3, IDM_THREE, VIRTKEY, ALT, NOINVERT
"D", IDM_THIRTEEN, ASCII, ALT, NOINVERT
"E", IDM_FOURTEEN, VIRTKEY, ALT, NOINVERT
"F", IDM_FIFTEEN, VIRTKEY, ALT, NOINVERT
END
STRINGTABLE
BEGIN
IDS_EDITSHOW "&Show Edit"
IDS_EDITHIDE "&Hide Edit"
IDS_ACCELXLAT "&Translate accelerators"
IDS_ACCELNOXLAT "&Don't translate accelerators"
END
@@ -0,0 +1,47 @@
/*
* PROJECT: ReactOS Tests
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
* PURPOSE: Resources header
* COPYRIGHT: Copyright 2026 Hermès Bélusca-Maïto <[email protected]>
*/
#define IDC_STATIC (-1)
#define IDI_TESTAPP 100
#define IDC_TESTAPP 101
#define IDS_EDITSHOW 110
#define IDS_EDITHIDE 111
#define IDS_ACCELXLAT 112
#define IDS_ACCELNOXLAT 113
#define IDM_SHOWHIDEEDIT 200
#define IDM_ACCELXLAT 201
#define IDM_CLEARLOG 202
#define IDM_EXIT 203
#define IDM_ONE 204
#define IDM_TWO 205
#define IDM_THREE 206
#define IDM_FOUR 207
#define IDM_FIVE 208
#define IDM_THIRTEEN 209
#define IDM_FOURTEEN 210
#define IDM_FIFTEEN 211
#define ID_ONE_ALPHA 220
#define ID_TWO_BRAVO 221
#define ID_THREE_CHARLIE 222
#define ID_FOUR_DELTA 223
#define ID_FIVE_ECHO 224
#define ID_SIX_FOXTROT 225
#define ID_SEVEN_GOLF 226
#define ID_EIGHT_HOTEL 227
#define ID_NINE_INDIA 228
#define ID_TEN_JULIET 229
#define ID_ELEVEN_KILO 230
#define ID_TWELVE_LIMA 231
#define ID_THIRTEEN_MIKE 232
#define ID_FOURTEEN_NOVEMBER 233
#define ID_FIFTEEN_OSCAR 234
#define ID_PLUS_PAPA 235
#define ID_DOT_QUEBEC 236