Skip to content

Commit 3c35117

Browse files
authored
[NTUSER][USER32_APITEST] TrackPopupMenuEx: Check flags (reactos#8094)
Validate flags. JIRA issue: CORE-3247 - In NtUserTrackPopupMenuEx function, validate the flags. - If validation failed, then set last error and return FALSE. - Add SAL annotations to NtUserTrackPopupMenuEx. - Keep reference to menu in the function. - Add TrackPopupMenuEx testcase to user32_apitest.
1 parent 4cf350a commit 3c35117

File tree

6 files changed

+395
-27
lines changed

6 files changed

+395
-27
lines changed

modules/rostests/apitests/user32/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ list(APPEND SOURCE
6060
SystemMenu.c
6161
SystemParametersInfo.c
6262
TrackMouseEvent.c
63+
TrackPopupMenuEx.c
6364
VirtualKey.c
6465
WndProc.c
6566
wsprintf.c)
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
/*
2+
* PROJECT: ReactOS API tests
3+
* LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4+
* PURPOSE: Tests for TrackPopupMenuEx
5+
* COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ <[email protected]>
6+
*/
7+
8+
#include "precomp.h"
9+
#include <versionhelpers.h>
10+
11+
#define CLASSNAME L"TrackPopupMenuEx tests"
12+
#define MENUCLASS L"#32768"
13+
14+
#define VALID_TPM_FLAGS ( \
15+
TPM_LAYOUTRTL | TPM_NOANIMATION | TPM_VERNEGANIMATION | TPM_VERPOSANIMATION | \
16+
TPM_HORNEGANIMATION | TPM_HORPOSANIMATION | TPM_RETURNCMD | \
17+
TPM_NONOTIFY | TPM_VERTICAL | TPM_BOTTOMALIGN | TPM_VCENTERALIGN | \
18+
TPM_RIGHTALIGN | TPM_CENTERALIGN | TPM_RIGHTBUTTON | TPM_RECURSE \
19+
)
20+
21+
#ifndef TPM_WORKAREA
22+
#define TPM_WORKAREA 0x10000
23+
#endif
24+
25+
static VOID
26+
TEST_InvalidFlags(VOID)
27+
{
28+
HWND hwnd = GetDesktopWindow();
29+
HMENU hMenu = CreatePopupMenu();
30+
BOOL ret;
31+
32+
ret = AppendMenuW(hMenu, MF_STRING, 100, L"(Dummy)");
33+
ok_int(ret, TRUE);
34+
35+
INT iBit;
36+
UINT uFlags;
37+
for (iBit = 0; iBit < sizeof(DWORD) * CHAR_BIT; ++iBit)
38+
{
39+
uFlags = (1 << iBit);
40+
if (uFlags & ~VALID_TPM_FLAGS)
41+
{
42+
SetLastError(0xBEEFCAFE);
43+
ret = TrackPopupMenuEx(hMenu, uFlags, 0, 0, hwnd, NULL);
44+
ok_int(ret, FALSE);
45+
if (uFlags == TPM_WORKAREA && IsWindows7OrGreater())
46+
ok_err(ERROR_INVALID_PARAMETER);
47+
else
48+
ok_err(ERROR_INVALID_FLAGS);
49+
}
50+
}
51+
52+
DestroyMenu(hMenu);
53+
}
54+
55+
static VOID
56+
TEST_InvalidSize(VOID)
57+
{
58+
HWND hwnd = GetDesktopWindow();
59+
HMENU hMenu = CreatePopupMenu();
60+
TPMPARAMS params;
61+
UINT uFlags = TPM_RIGHTBUTTON;
62+
BOOL ret;
63+
64+
ZeroMemory(&params, sizeof(params));
65+
66+
ret = AppendMenuW(hMenu, MF_STRING, 100, L"(Dummy)");
67+
ok_int(ret, TRUE);
68+
69+
SetLastError(0xBEEFCAFE);
70+
params.cbSize = 0;
71+
ret = TrackPopupMenuEx(hMenu, uFlags, 0, 0, hwnd, &params);
72+
ok_int(ret, FALSE);
73+
ok_err(ERROR_INVALID_PARAMETER);
74+
75+
SetLastError(0xBEEFCAFE);
76+
params.cbSize = sizeof(params) - 1;
77+
ret = TrackPopupMenuEx(hMenu, uFlags, 0, 0, hwnd, &params);
78+
ok_int(ret, FALSE);
79+
ok_err(ERROR_INVALID_PARAMETER);
80+
81+
SetLastError(0xBEEFCAFE);
82+
params.cbSize = sizeof(params) + 1;
83+
ret = TrackPopupMenuEx(hMenu, uFlags, 0, 0, hwnd, &params);
84+
ok_int(ret, FALSE);
85+
ok_err(ERROR_INVALID_PARAMETER);
86+
87+
DestroyMenu(hMenu);
88+
}
89+
90+
#define DELAY 100
91+
#define INTERVAL 300
92+
93+
typedef enum tagAUTO_CLICK
94+
{
95+
AUTO_LEFT_CLICK,
96+
AUTO_RIGHT_CLICK,
97+
AUTO_LEFT_DOUBLE_CLICK,
98+
AUTO_RIGHT_DOUBLE_CLICK,
99+
} AUTO_CLICK;
100+
101+
typedef enum tagAUTO_KEY
102+
{
103+
AUTO_KEY_DOWN,
104+
AUTO_KEY_UP,
105+
AUTO_KEY_DOWN_UP,
106+
} AUTO_KEY;
107+
108+
static VOID
109+
AutoKey(AUTO_KEY type, UINT vKey)
110+
{
111+
if (type == AUTO_KEY_DOWN_UP)
112+
{
113+
AutoKey(AUTO_KEY_DOWN, vKey);
114+
AutoKey(AUTO_KEY_UP, vKey);
115+
return;
116+
}
117+
118+
INPUT input;
119+
ZeroMemory(&input, sizeof(input));
120+
121+
input.type = INPUT_KEYBOARD;
122+
input.ki.wVk = vKey;
123+
input.ki.dwFlags = ((type == AUTO_KEY_UP) ? KEYEVENTF_KEYUP : 0);
124+
SendInput(1, &input, sizeof(INPUT));
125+
Sleep(DELAY);
126+
}
127+
128+
static VOID
129+
AutoClick(AUTO_CLICK type, INT x, INT y)
130+
{
131+
INPUT input;
132+
ZeroMemory(&input, sizeof(input));
133+
134+
INT nScreenWidth = GetSystemMetrics(SM_CXSCREEN) - 1;
135+
INT nScreenHeight = GetSystemMetrics(SM_CYSCREEN) - 1;
136+
137+
input.type = INPUT_MOUSE;
138+
input.mi.dx = (LONG)(x * (65535.0f / nScreenWidth));
139+
input.mi.dy = (LONG)(y * (65535.0f / nScreenHeight));
140+
input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
141+
SendInput(1, &input, sizeof(INPUT));
142+
Sleep(DELAY);
143+
144+
input.mi.dx = input.mi.dy = 0;
145+
146+
INT i, count = 1;
147+
switch (type)
148+
{
149+
case AUTO_LEFT_DOUBLE_CLICK:
150+
count = 2;
151+
// FALL THROUGH
152+
case AUTO_LEFT_CLICK:
153+
{
154+
for (i = 0; i < count; ++i)
155+
{
156+
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
157+
SendInput(1, &input, sizeof(INPUT));
158+
Sleep(DELAY);
159+
160+
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
161+
SendInput(1, &input, sizeof(INPUT));
162+
Sleep(DELAY);
163+
}
164+
break;
165+
}
166+
case AUTO_RIGHT_DOUBLE_CLICK:
167+
count = 2;
168+
// FALL THROUGH
169+
case AUTO_RIGHT_CLICK:
170+
{
171+
for (i = 0; i < count; ++i)
172+
{
173+
input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
174+
SendInput(1, &input, sizeof(INPUT));
175+
Sleep(DELAY);
176+
177+
input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
178+
SendInput(1, &input, sizeof(INPUT));
179+
Sleep(DELAY);
180+
}
181+
break;
182+
}
183+
}
184+
}
185+
186+
static POINT
187+
CenterPoint(const RECT *prc)
188+
{
189+
POINT pt = { (prc->left + prc->right) / 2, (prc->top + prc->bottom) / 2 };
190+
return pt;
191+
}
192+
193+
typedef struct tagCOUNTMENUWND
194+
{
195+
INT nMenuCount;
196+
} COUNTMENUWND, *PCOUNTMENUWND;
197+
198+
static BOOL CALLBACK
199+
CountMenuWndProc(HWND hwnd, LPARAM lParam)
200+
{
201+
if (!IsWindowVisible(hwnd))
202+
return TRUE;
203+
204+
WCHAR szClass[64];
205+
GetClassNameW(hwnd, szClass, _countof(szClass));
206+
if (lstrcmpiW(szClass, MENUCLASS) != 0)
207+
return TRUE;
208+
209+
PCOUNTMENUWND pData = (PCOUNTMENUWND)lParam;
210+
pData->nMenuCount += 1;
211+
return TRUE;
212+
}
213+
214+
static INT
215+
CountMenuWnds(VOID)
216+
{
217+
COUNTMENUWND data = { 0 };
218+
EnumWindows(CountMenuWndProc, (LPARAM)&data);
219+
return data.nMenuCount;
220+
}
221+
222+
static DWORD WINAPI
223+
TEST_Tracking_ThreadFunc(LPVOID arg)
224+
{
225+
HWND hwnd = (HWND)arg;
226+
227+
ok_int(CountMenuWnds(), 0);
228+
229+
RECT rc;
230+
GetWindowRect(hwnd, &rc);
231+
POINT pt = CenterPoint(&rc);
232+
233+
AutoClick(AUTO_RIGHT_CLICK, pt.x, pt.y);
234+
Sleep(INTERVAL);
235+
236+
ok_int(CountMenuWnds(), 1);
237+
238+
AutoKey(AUTO_KEY_DOWN_UP, VK_DOWN);
239+
Sleep(INTERVAL);
240+
241+
ok_int(CountMenuWnds(), 1);
242+
243+
AutoKey(AUTO_KEY_DOWN_UP, VK_RETURN);
244+
Sleep(INTERVAL);
245+
246+
ok_int(CountMenuWnds(), 0);
247+
248+
PostMessageW(hwnd, WM_CLOSE, 0, 0);
249+
return 0;
250+
}
251+
252+
static VOID
253+
OnRButtonDown(HWND hwnd)
254+
{
255+
SetForegroundWindow(hwnd);
256+
257+
POINT pt;
258+
GetCursorPos(&pt);
259+
260+
HMENU hMenu = CreatePopupMenu();
261+
BOOL ret = AppendMenuW(hMenu, MF_STRING, 100, L"(Dummy)");
262+
ok_int(ret, TRUE);
263+
264+
UINT uFlags = TPM_RIGHTBUTTON | TPM_RETURNCMD;
265+
INT nCmdID = (INT)TrackPopupMenuEx(hMenu, uFlags, pt.x, pt.y, hwnd, NULL);
266+
267+
ok_int(nCmdID, 100);
268+
269+
DestroyMenu(hMenu);
270+
}
271+
272+
static LRESULT CALLBACK
273+
WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
274+
{
275+
switch (uMsg)
276+
{
277+
case WM_CREATE:
278+
return 0;
279+
case WM_RBUTTONDOWN:
280+
OnRButtonDown(hwnd);
281+
break;
282+
case WM_DESTROY:
283+
PostQuitMessage(0);
284+
break;
285+
default:
286+
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
287+
}
288+
return 0;
289+
}
290+
291+
static VOID
292+
TEST_Tracking(VOID)
293+
{
294+
HINSTANCE hInstance = GetModuleHandleW(NULL);;
295+
296+
WNDCLASSW wc = { 0, WindowProc };
297+
wc.hInstance = hInstance;
298+
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
299+
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
300+
wc.hbrBackground = (HBRUSH)UlongToHandle(COLOR_3DFACE + 1);
301+
wc.lpszClassName = CLASSNAME;
302+
if (!RegisterClassW(&wc))
303+
{
304+
skip("RegisterClassW failed\n");
305+
return;
306+
}
307+
308+
DWORD style = WS_OVERLAPPEDWINDOW;
309+
HWND hwnd = CreateWindowW(CLASSNAME, CLASSNAME, style,
310+
0, 0, 320, 200, NULL, NULL, hInstance, NULL);
311+
if (!hwnd)
312+
{
313+
skip("CreateWindowW failed\n");
314+
return;
315+
}
316+
317+
ShowWindow(hwnd, SW_SHOWNORMAL);
318+
UpdateWindow(hwnd);
319+
320+
HANDLE hThread = CreateThread(NULL, 0, TEST_Tracking_ThreadFunc, hwnd, 0, NULL);
321+
if (!hThread)
322+
{
323+
skip("CreateThread failed\n");
324+
DestroyWindow(hwnd);
325+
return;
326+
}
327+
CloseHandle(hThread);
328+
329+
MSG msg;
330+
while (GetMessageW(&msg, NULL, 0, 0))
331+
{
332+
TranslateMessage(&msg);
333+
DispatchMessageW(&msg);
334+
}
335+
}
336+
337+
START_TEST(TrackPopupMenuEx)
338+
{
339+
TEST_InvalidFlags();
340+
TEST_InvalidSize();
341+
TEST_Tracking();
342+
}

modules/rostests/apitests/user32/testlist.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extern void func_SwitchToThisWindow(void);
6161
extern void func_SystemParametersInfo(void);
6262
extern void func_SystemMenu(void);
6363
extern void func_TrackMouseEvent(void);
64+
extern void func_TrackPopupMenuEx(void);
6465
extern void func_VirtualKey(void);
6566
extern void func_WndProc(void);
6667
extern void func_wsprintf(void);
@@ -125,6 +126,7 @@ const struct test winetest_testlist[] =
125126
{ "SystemMenu", func_SystemMenu },
126127
{ "SystemParametersInfo", func_SystemParametersInfo },
127128
{ "TrackMouseEvent", func_TrackMouseEvent },
129+
{ "TrackPopupMenuEx", func_TrackPopupMenuEx },
128130
{ "VirtualKey", func_VirtualKey },
129131
{ "WndProc", func_WndProc },
130132
{ "wsprintfApi", func_wsprintf },

win32ss/include/ntuser.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,12 +1425,12 @@ NtUserThunkedMenuItemInfo(
14251425
BOOL
14261426
NTAPI
14271427
NtUserTrackPopupMenuEx(
1428-
HMENU hmenu,
1429-
UINT fuFlags,
1430-
int x,
1431-
int y,
1432-
HWND hwnd,
1433-
LPTPMPARAMS lptpm);
1428+
_In_ HMENU hMenu,
1429+
_In_ UINT fuFlags,
1430+
_In_ INT x,
1431+
_In_ INT y,
1432+
_In_ HWND hwnd,
1433+
_In_opt_ LPTPMPARAMS lptpm);
14341434

14351435
HKL
14361436
NTAPI

0 commit comments

Comments
 (0)