1 /* 2 * PROJECT: ReactOS API tests 3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) 4 * PURPOSE: Tests for system menu messages, based on msg.c Wine test 5 * COPYRIGHT: Copyright 1999 Ove Kaaven <ovek@transgaming.com> 6 * Copyright 2003 Dimitrie O. Paun <dimi@lattica.com> 7 * Copyright 2004-2005, 2016 Dmitry Timoshkov <dmitry@baikal.ru> 8 * Copyright 2023 Egor Ananyin <ananinegor@gmail.com> 9 */ 10 11 #include "precomp.h" 12 13 #define WND_PARENT_ID 1 14 #define WND_POPUP_ID 2 15 16 static BOOL (WINAPI *pGetCurrentActCtx)(HANDLE *); 17 static BOOL (WINAPI *pQueryActCtxW)(DWORD, HANDLE, void *, ULONG, void *, SIZE_T, SIZE_T *); 18 19 static BOOL test_DestroyWindow_flag; 20 static HWINEVENTHOOK hEvent_hook; 21 static HHOOK hKBD_hook; 22 static HHOOK hCBT_hook; 23 static DWORD cbt_hook_thread_id; 24 25 typedef enum 26 { 27 sent = 0x1, 28 posted = 0x2, 29 parent = 0x4, 30 wparam = 0x8, 31 lparam = 0x10, 32 defwinproc = 0x20, 33 beginpaint = 0x40, 34 optional = 0x80, 35 hook = 0x100, 36 winevent_hook = 0x200, 37 kbd_hook = 0x400 38 } msg_flags_t; 39 40 struct message 41 { 42 UINT message; /* the WM_* code */ 43 msg_flags_t flags; /* message props */ 44 WPARAM wParam; /* expected value of wParam */ 45 LPARAM lParam; /* expected value of lParam */ 46 WPARAM wp_mask; /* mask for wParam checks */ 47 LPARAM lp_mask; /* mask for lParam checks */ 48 }; 49 50 struct recvd_message 51 { 52 UINT message; /* the WM_* code */ 53 msg_flags_t flags; /* message props */ 54 HWND hwnd; /* window that received the message */ 55 WPARAM wParam; /* expected value of wParam */ 56 LPARAM lParam; /* expected value of lParam */ 57 int line; /* source line where logged */ 58 const char *descr; /* description for trace output */ 59 char output[512]; /* trace output */ 60 }; 61 62 static int sequence_cnt, sequence_size; 63 static struct recvd_message* sequence; 64 static CRITICAL_SECTION sequence_cs; 65 66 /* user32 functions */ 67 static HWND (WINAPI *pGetAncestor)(HWND, UINT); 68 static BOOL (WINAPI *pUnhookWinEvent)(HWINEVENTHOOK); 69 70 static void init_procs(void) 71 { 72 HMODULE user32 = GetModuleHandleA("user32.dll"); 73 74 #define GET_PROC(dll, func) \ 75 p ## func = (void*)GetProcAddress(dll, #func); \ 76 if (!p ## func) { \ 77 trace("GetProcAddress(%s) failed\n", #func); \ 78 } 79 80 GET_PROC(user32, GetAncestor) 81 GET_PROC(user32, UnhookWinEvent) 82 83 #undef GET_PROC 84 } 85 86 static BOOL ignore_message(UINT message) 87 { 88 /* these are always ignored */ 89 return (message >= 0xc000 || 90 message == WM_GETICON || 91 message == WM_GETOBJECT || 92 message == WM_TIMECHANGE || 93 message == WM_DISPLAYCHANGE || 94 message == WM_DEVICECHANGE || 95 message == WM_DWMNCRENDERINGCHANGED); 96 } 97 98 #define add_message(msg) add_message_(__LINE__, msg); 99 static void add_message_(int line, const struct recvd_message *msg) 100 { 101 struct recvd_message *seq; 102 103 EnterCriticalSection(&sequence_cs); 104 if (!sequence) 105 { 106 sequence_size = 10; 107 sequence = HeapAlloc(GetProcessHeap(), 0, sequence_size * sizeof(*sequence)); 108 } 109 if (sequence_cnt == sequence_size) 110 { 111 sequence_size *= 2; 112 sequence = HeapReAlloc(GetProcessHeap(), 0, sequence, sequence_size * sizeof(*sequence)); 113 } 114 assert(sequence); 115 116 seq = &sequence[sequence_cnt++]; 117 seq->hwnd = msg->hwnd; 118 seq->message = msg->message; 119 seq->flags = msg->flags; 120 seq->wParam = msg->wParam; 121 seq->lParam = msg->lParam; 122 seq->line = line; 123 seq->descr = msg->descr; 124 seq->output[0] = 0; 125 LeaveCriticalSection(&sequence_cs); 126 127 if (msg->descr) 128 { 129 if (msg->flags & hook) 130 { 131 static const char * const CBT_code_name[10] = 132 { 133 "HCBT_MOVESIZE", 134 "HCBT_MINMAX", 135 "HCBT_QS", 136 "HCBT_CREATEWND", 137 "HCBT_DESTROYWND", 138 "HCBT_ACTIVATE", 139 "HCBT_CLICKSKIPPED", 140 "HCBT_KEYSKIPPED", 141 "HCBT_SYSCOMMAND", 142 "HCBT_SETFOCUS" 143 }; 144 const char *code_name = (msg->message <= HCBT_SETFOCUS ? CBT_code_name[msg->message] : "Unknown"); 145 146 sprintf(seq->output, "%s: hook %d (%s) wp %08Ix lp %08Ix", 147 msg->descr, msg->message, code_name, msg->wParam, msg->lParam); 148 } 149 else if (msg->flags & winevent_hook) 150 { 151 sprintf(seq->output, "%s: winevent %p %08x %08Ix %08Ix", 152 msg->descr, msg->hwnd, msg->message, msg->wParam, msg->lParam); 153 } 154 else 155 { 156 if (msg->message >= 0xc000) 157 return; /* ignore registered messages */ 158 sprintf(seq->output, "%s: %p %04x wp %08Ix lp %08Ix", 159 msg->descr, msg->hwnd, msg->message, msg->wParam, msg->lParam); 160 if (msg->flags & (sent | posted | parent | defwinproc | beginpaint)) 161 sprintf(seq->output + strlen(seq->output), " (flags %x)", msg->flags); 162 } 163 } 164 } 165 166 /* try to make sure pending X events have been processed before continuing */ 167 static void flush_events(void) 168 { 169 MSG msg; 170 int diff = 200; 171 int min_timeout = 100; 172 DWORD time = GetTickCount() + diff; 173 174 while (diff > 0) 175 { 176 if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) 177 break; 178 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) 179 DispatchMessageA(&msg); 180 diff = time - GetTickCount(); 181 } 182 } 183 184 static void flush_sequence(void) 185 { 186 EnterCriticalSection(&sequence_cs); 187 HeapFree(GetProcessHeap(), 0, sequence); 188 sequence = 0; 189 sequence_cnt = sequence_size = 0; 190 LeaveCriticalSection(&sequence_cs); 191 } 192 193 static void dump_sequence(const struct message *expected, const char *context, const char *file, int line) 194 { 195 const struct recvd_message *actual = sequence; 196 unsigned int count = 0; 197 198 trace_(file, line)("Failed sequence %s:\n", context); 199 200 while (expected->message && actual->message) 201 { 202 if (actual->output[0]) 203 { 204 if (expected->flags & hook) 205 { 206 trace_(file, line)(" %u: expected: hook %04x - actual: %s\n", 207 count, expected->message, actual->output); 208 } 209 else if (expected->flags & winevent_hook) 210 { 211 trace_(file, line)(" %u: expected: winevent %04x - actual: %s\n", 212 count, expected->message, actual->output); 213 } 214 else if (expected->flags & kbd_hook) 215 { 216 trace_(file, line)(" %u: expected: kbd %04x - actual: %s\n", 217 count, expected->message, actual->output); 218 } 219 else 220 { 221 trace_(file, line)(" %u: expected: msg %04x - actual: %s\n", 222 count, expected->message, actual->output); 223 } 224 } 225 226 if (expected->message == actual->message) 227 { 228 if ((expected->flags & defwinproc) != (actual->flags & defwinproc) && 229 (expected->flags & optional)) 230 { 231 /* don't match messages if their defwinproc status differs */ 232 expected++; 233 } 234 else 235 { 236 expected++; 237 actual++; 238 } 239 } 240 /* silently drop winevent messages if there is no support for them */ 241 else if ((expected->flags & optional) || ((expected->flags & winevent_hook) && !hEvent_hook)) 242 expected++; 243 else 244 { 245 expected++; 246 actual++; 247 } 248 249 count++; 250 } 251 252 /* optional trailing messages */ 253 while (expected->message && ((expected->flags & optional) || 254 ((expected->flags & winevent_hook) && !hEvent_hook))) 255 { 256 trace_(file, line)(" %u: expected: msg %04x - actual: nothing\n", count, expected->message); 257 expected++; 258 count++; 259 } 260 261 if (expected->message) 262 { 263 trace_(file, line)(" %u: expected: msg %04x - actual: nothing\n", count, expected->message); 264 return; 265 } 266 267 while (actual->message && actual->output[0]) 268 { 269 trace_(file, line)(" %u: expected: nothing - actual: %s\n", count, actual->output); 270 actual++; 271 count++; 272 } 273 } 274 275 #define ok_sequence(exp, contx, todo) \ 276 ok_sequence_((exp), (contx), (todo), __FILE__, __LINE__) 277 278 279 static void ok_sequence_(const struct message *expected_list, const char *context, BOOL todo, 280 const char *file, int line) 281 { 282 static const struct recvd_message end_of_sequence; 283 const struct message *expected = expected_list; 284 const struct recvd_message *actual; 285 int failcount = 0, dump = 0; 286 unsigned int count = 0; 287 288 add_message(&end_of_sequence); 289 290 actual = sequence; 291 292 while (expected->message && actual->message) 293 { 294 if (expected->message == actual->message && 295 !((expected->flags ^ actual->flags) & (hook | winevent_hook | kbd_hook))) 296 { 297 if (expected->flags & wparam) 298 { 299 if (((expected->wParam ^ actual->wParam) & ~expected->wp_mask) && todo) 300 { 301 todo_wine 302 { 303 failcount++; 304 if (strcmp(winetest_platform, "wine")) 305 dump++; 306 ok_( file, line) (FALSE, 307 "%s: %u: in msg 0x%04x expecting wParam 0x%x got 0x%x\n", 308 context, count, expected->message, expected->wParam, actual->wParam); 309 } 310 } 311 else 312 { 313 ok_( file, line)(((expected->wParam ^ actual->wParam) & ~expected->wp_mask) == 0, 314 "%s: %u: in msg 0x%04x expecting wParam 0x%x got 0x%x\n", 315 context, count, expected->message, expected->wParam, actual->wParam); 316 if ((expected->wParam ^ actual->wParam) & ~expected->wp_mask) 317 dump++; 318 } 319 } 320 321 if (expected->flags & lparam) 322 { 323 if (((expected->lParam ^ actual->lParam) & ~expected->lp_mask) && todo) 324 { 325 todo_wine 326 { 327 failcount++; 328 if (strcmp(winetest_platform, "wine")) 329 dump++; 330 ok_( file, line) (FALSE, 331 "%s: %u: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n", 332 context, count, expected->message, expected->lParam, actual->lParam); 333 } 334 } 335 else 336 { 337 ok_( file, line)(((expected->lParam ^ actual->lParam) & ~expected->lp_mask) == 0, 338 "%s: %u: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n", 339 context, count, expected->message, expected->lParam, actual->lParam); 340 if ((expected->lParam ^ actual->lParam) & ~expected->lp_mask) 341 dump++; 342 } 343 } 344 if ((expected->flags & optional) && 345 ((expected->flags ^ actual->flags) & (defwinproc | parent))) 346 { 347 /* don't match optional messages if their defwinproc or parent status differs */ 348 expected++; 349 count++; 350 continue; 351 } 352 if ((expected->flags & defwinproc) != (actual->flags & defwinproc) && todo) 353 { 354 todo_wine 355 { 356 failcount++; 357 if (strcmp(winetest_platform, "wine")) 358 dump++; 359 ok_(file, line) (FALSE, 360 "%s: %u: the msg 0x%04x should %shave been sent by DefWindowProc\n", 361 context, count, expected->message, (expected->flags & defwinproc) ? "" : "NOT "); 362 } 363 } 364 else 365 { 366 ok_(file, line) ((expected->flags & defwinproc) == (actual->flags & defwinproc), 367 "%s: %u: the msg 0x%04x should %shave been sent by DefWindowProc\n", 368 context, count, expected->message, (expected->flags & defwinproc) ? "" : "NOT "); 369 if ((expected->flags & defwinproc) != (actual->flags & defwinproc)) 370 dump++; 371 } 372 373 ok_(file, line) ((expected->flags & beginpaint) == (actual->flags & beginpaint), 374 "%s: %u: the msg 0x%04x should %shave been sent by BeginPaint\n", 375 context, count, expected->message, (expected->flags & beginpaint) ? "" : "NOT "); 376 if ((expected->flags & beginpaint) != (actual->flags & beginpaint)) 377 dump++; 378 379 ok_(file, line) ((expected->flags & (sent | posted)) == (actual->flags & (sent | posted)), 380 "%s: %u: the msg 0x%04x should have been %s\n", 381 context, count, expected->message, (expected->flags & posted) ? "posted" : "sent"); 382 if ((expected->flags & (sent | posted)) != (actual->flags & (sent | posted))) 383 dump++; 384 385 ok_(file, line) ((expected->flags & parent) == (actual->flags & parent), 386 "%s: %u: the msg 0x%04x was expected in %s\n", 387 context, count, expected->message, (expected->flags & parent) ? "parent" : "child"); 388 if ((expected->flags & parent) != (actual->flags & parent)) 389 dump++; 390 391 ok_(file, line) ((expected->flags & hook) == (actual->flags & hook), 392 "%s: %u: the msg 0x%04x should have been sent by a hook\n", 393 context, count, expected->message); 394 if ((expected->flags & hook) != (actual->flags & hook)) 395 dump++; 396 397 ok_(file, line) ((expected->flags & winevent_hook) == (actual->flags & winevent_hook), 398 "%s: %u: the msg 0x%04x should have been sent by a winevent hook\n", 399 context, count, expected->message); 400 if ((expected->flags & winevent_hook) != (actual->flags & winevent_hook)) 401 dump++; 402 403 ok_(file, line) ((expected->flags & kbd_hook) == (actual->flags & kbd_hook), 404 "%s: %u: the msg 0x%04x should have been sent by a keyboard hook\n", 405 context, count, expected->message); 406 if ((expected->flags & kbd_hook) != (actual->flags & kbd_hook)) 407 dump++; 408 409 expected++; 410 actual++; 411 } 412 /* silently drop hook messages if there is no support for them */ 413 else if ((expected->flags & optional) || 414 ((expected->flags & hook) && !hCBT_hook) || 415 ((expected->flags & winevent_hook) && !hEvent_hook) || 416 ((expected->flags & kbd_hook) && !hKBD_hook)) 417 expected++; 418 else if (todo) 419 { 420 failcount++; 421 todo_wine 422 { 423 if (strcmp(winetest_platform, "wine")) 424 dump++; 425 ok_(file, line) (FALSE, "%s: %u: the msg 0x%04x was expected, but got msg 0x%04x instead\n", 426 context, count, expected->message, actual->message); 427 } 428 goto done; 429 } 430 else 431 { 432 ok_(file, line) (FALSE, "%s: %u: the msg 0x%04x was expected, but got msg 0x%04x instead\n", 433 context, count, expected->message, actual->message); 434 dump++; 435 expected++; 436 actual++; 437 } 438 count++; 439 } 440 441 /* skip all optional trailing messages */ 442 while (expected->message && ((expected->flags & optional) || 443 ((expected->flags & hook) && !hCBT_hook) || 444 ((expected->flags & winevent_hook) && !hEvent_hook))) 445 expected++; 446 447 if (todo) 448 { 449 todo_wine 450 { 451 if (expected->message || actual->message) 452 { 453 failcount++; 454 if (strcmp(winetest_platform, "wine")) 455 dump++; 456 ok_(file, line) (FALSE, "%s: %u: the msg sequence is not complete: expected %04x - actual %04x\n", 457 context, count, expected->message, actual->message); 458 } 459 } 460 } 461 else 462 { 463 if (expected->message || actual->message) 464 { 465 dump++; 466 ok_(file, line) (FALSE, "%s: %u: the msg sequence is not complete: expected %04x - actual %04x\n", 467 context, count, expected->message, actual->message); 468 } 469 } 470 if (todo && !failcount) /* succeeded yet marked todo */ 471 todo_wine 472 { 473 if (!strcmp(winetest_platform, "wine")) 474 dump++; 475 ok_(file, line) (TRUE, "%s: marked \"todo_wine\" but succeeds\n", context); 476 } 477 478 done: 479 if (dump) dump_sequence(expected_list, context, file, line); 480 flush_sequence(); 481 } 482 483 #define expect(EXPECTED,GOT) ok((GOT)==(EXPECTED), "Expected %d, got %d\n", (EXPECTED), (GOT)) 484 485 /************* window procedures ********************/ 486 487 static LRESULT MsgCheckProc(BOOL unicode, HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 488 { 489 static LONG defwndproc_counter = 0; 490 static LONG beginpaint_counter = 0; 491 LRESULT ret; 492 struct recvd_message msg; 493 494 if (ignore_message(message)) return 0; 495 496 msg.hwnd = hwnd; 497 msg.message = message; 498 msg.flags = sent | wparam | lparam; 499 if (defwndproc_counter) msg.flags |= defwinproc; 500 if (beginpaint_counter) msg.flags |= beginpaint; 501 msg.wParam = wParam; 502 msg.lParam = lParam; 503 msg.descr = "MsgCheckProc"; 504 add_message(&msg); 505 506 defwndproc_counter++; 507 ret = unicode ? DefWindowProcW(hwnd, message, wParam, lParam) 508 : DefWindowProcA(hwnd, message, wParam, lParam); 509 defwndproc_counter--; 510 511 return ret; 512 } 513 514 static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 515 { 516 return MsgCheckProc(FALSE, hwnd, message, wParam, lParam); 517 } 518 519 static BOOL RegisterWindowClasses(void) 520 { 521 WNDCLASSA cls; 522 523 cls.style = 0; 524 cls.lpfnWndProc = MsgCheckProcA; 525 cls.cbClsExtra = 0; 526 cls.cbWndExtra = 0; 527 cls.hInstance = GetModuleHandleA(0); 528 cls.hIcon = 0; 529 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); 530 cls.hbrBackground = GetStockObject(WHITE_BRUSH); 531 cls.lpszMenuName = NULL; 532 cls.lpszClassName = "TestWindowClass"; 533 if (!RegisterClassA(&cls)) return FALSE; 534 535 return TRUE; 536 } 537 538 static BOOL is_our_logged_class(HWND hwnd) 539 { 540 char buf[256]; 541 542 if (GetClassNameA(hwnd, buf, sizeof(buf))) 543 { 544 if (!lstrcmpiA(buf, "TestWindowClass") || 545 !lstrcmpiA(buf, "ShowWindowClass") || 546 !lstrcmpiA(buf, "RecursiveActivationClass") || 547 !lstrcmpiA(buf, "TestParentClass") || 548 !lstrcmpiA(buf, "TestPopupClass") || 549 !lstrcmpiA(buf, "SimpleWindowClass") || 550 !lstrcmpiA(buf, "TestDialogClass") || 551 !lstrcmpiA(buf, "MDI_frame_class") || 552 !lstrcmpiA(buf, "MDI_client_class") || 553 !lstrcmpiA(buf, "MDI_child_class") || 554 !lstrcmpiA(buf, "my_button_class") || 555 !lstrcmpiA(buf, "my_edit_class") || 556 !lstrcmpiA(buf, "static") || 557 !lstrcmpiA(buf, "ListBox") || 558 !lstrcmpiA(buf, "ComboBox") || 559 !lstrcmpiA(buf, "MyDialogClass") || 560 !lstrcmpiA(buf, "#32770") || 561 !lstrcmpiA(buf, "#32768")) 562 return TRUE; 563 } 564 return FALSE; 565 } 566 567 static LRESULT CALLBACK cbt_hook_proc(int nCode, WPARAM wParam, LPARAM lParam) 568 { 569 HWND hwnd; 570 571 ok(cbt_hook_thread_id == GetCurrentThreadId(), "we didn't ask for events from other threads\n"); 572 573 if (nCode == HCBT_CLICKSKIPPED) 574 { 575 /* ignore this event, XP sends it a lot when switching focus between windows */ 576 return CallNextHookEx(hCBT_hook, nCode, wParam, lParam); 577 } 578 579 if (nCode == HCBT_SYSCOMMAND || nCode == HCBT_KEYSKIPPED) 580 { 581 struct recvd_message msg; 582 583 msg.hwnd = 0; 584 msg.message = nCode; 585 msg.flags = hook | wparam | lparam; 586 msg.wParam = wParam; 587 msg.lParam = lParam; 588 msg.descr = "CBT"; 589 add_message(&msg); 590 591 return CallNextHookEx(hCBT_hook, nCode, wParam, lParam); 592 } 593 594 if (nCode == HCBT_DESTROYWND) 595 { 596 if (test_DestroyWindow_flag) 597 { 598 DWORD style = GetWindowLongA((HWND)wParam, GWL_STYLE); 599 if (style & WS_CHILD) 600 lParam = GetWindowLongPtrA((HWND)wParam, GWLP_ID); 601 else if (style & WS_POPUP) 602 lParam = WND_POPUP_ID; 603 else 604 lParam = WND_PARENT_ID; 605 } 606 } 607 608 /* Log also SetFocus(0) calls */ 609 hwnd = wParam ? (HWND)wParam : (HWND)lParam; 610 611 if (is_our_logged_class(hwnd)) 612 { 613 struct recvd_message msg; 614 615 msg.hwnd = hwnd; 616 msg.message = nCode; 617 msg.flags = hook | wparam | lparam; 618 msg.wParam = wParam; 619 msg.lParam = lParam; 620 msg.descr = "CBT"; 621 add_message(&msg); 622 } 623 return CallNextHookEx(hCBT_hook, nCode, wParam, lParam); 624 } 625 626 /*************************** Menu test ******************************/ 627 628 static const struct message wm_popup_menu_4[] = 629 { 630 { HCBT_CREATEWND, hook }, 631 { WM_ENTERMENULOOP, sent | wparam | lparam, 0, 0 }, 632 { WM_INITMENU, sent | lparam, 0, 0 }, 633 { WM_INITMENUPOPUP, sent | lparam, 0, 0x10000 }, 634 { HCBT_KEYSKIPPED, hook | wparam | lparam | optional, VK_DOWN, 0x10000001 }, 635 { WM_MENUSELECT, sent, MAKEWPARAM(0,0xffff), 0 }, 636 { HCBT_KEYSKIPPED, hook | wparam | lparam | optional, VK_DOWN, 0xd0000001 }, 637 { HCBT_KEYSKIPPED, hook | wparam | lparam | optional, VK_ESCAPE, 0x10000001 }, 638 { HCBT_DESTROYWND, hook }, 639 { WM_UNINITMENUPOPUP, sent | lparam, 0, 0x20000000 }, 640 { WM_MENUSELECT, sent, MAKEWPARAM(0,0xffff), 0 }, 641 { WM_EXITMENULOOP, sent | wparam | lparam, 0, 0 }, 642 { HCBT_KEYSKIPPED, hook | wparam | lparam | optional, VK_ESCAPE, 0xc0000001 }, 643 { WM_KEYUP, sent | wparam | lparam, VK_ESCAPE, 0xc0000001 }, 644 { 0 } 645 }; 646 647 static LRESULT WINAPI parent_menu_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) 648 { 649 if (message == WM_ENTERIDLE || 650 message == WM_INITMENU || 651 message == WM_INITMENUPOPUP || 652 message == WM_MENUSELECT || 653 message == WM_PARENTNOTIFY || 654 message == WM_ENTERMENULOOP || 655 message == WM_EXITMENULOOP || 656 message == WM_UNINITMENUPOPUP || 657 message == WM_KEYDOWN || 658 message == WM_KEYUP || 659 message == WM_CHAR || 660 message == WM_SYSKEYDOWN || 661 message == WM_SYSKEYUP || 662 message == WM_SYSCHAR || 663 message == WM_COMMAND || 664 message == WM_MENUCOMMAND) 665 { 666 struct recvd_message msg; 667 668 msg.hwnd = hwnd; 669 msg.message = message; 670 msg.flags = sent | wparam | lparam; 671 msg.wParam = wp; 672 msg.lParam = lp; 673 msg.descr = "parent_menu_proc"; 674 add_message(&msg); 675 } 676 677 return DefWindowProcA(hwnd, message, wp, lp); 678 } 679 680 static void test_menu_messages(void) 681 { 682 MSG msg; 683 WNDCLASSA cls; 684 HWND hwnd; 685 RECT rect; 686 687 cls.style = 0; 688 cls.lpfnWndProc = parent_menu_proc; 689 cls.cbClsExtra = 0; 690 cls.cbWndExtra = 0; 691 cls.hInstance = GetModuleHandleA(0); 692 cls.hIcon = 0; 693 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); 694 cls.hbrBackground = GetStockObject(WHITE_BRUSH); 695 cls.lpszMenuName = NULL; 696 cls.lpszClassName = "TestMenuClass"; 697 UnregisterClassA(cls.lpszClassName, cls.hInstance); 698 if (!RegisterClassA(&cls)) 699 assert(0); 700 701 SetLastError(0xdeadbeef); 702 hwnd = CreateWindowExA(0, "TestMenuClass", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 703 100, 100, 200, 200, 0, 0, 0, NULL); 704 ok(hwnd != 0, "LoadMenuA error %lu\n", GetLastError()); 705 706 SetForegroundWindow(hwnd); 707 flush_events(); 708 709 trace("testing system menu\n"); 710 GetWindowRect(hwnd, &rect); 711 SetCursorPos(rect.left + 30, rect.top + 10); 712 flush_sequence(); 713 mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0); 714 mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); 715 keybd_event(VK_DOWN, 0, 0, 0); 716 keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0); 717 keybd_event(VK_ESCAPE, 0, 0, 0); 718 keybd_event(VK_ESCAPE, 0, KEYEVENTF_KEYUP, 0); 719 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) 720 { 721 TranslateMessage(&msg); 722 DispatchMessageA(&msg); 723 } 724 ok_sequence(wm_popup_menu_4, "system menu command", FALSE); 725 726 DestroyWindow(hwnd); 727 } 728 729 static void init_funcs(void) 730 { 731 HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); 732 733 #define X(f) p##f = (void*)GetProcAddress(hKernel32, #f) 734 X(GetCurrentActCtx); 735 X(QueryActCtxW); 736 #undef X 737 } 738 739 static void init_tests() 740 { 741 init_funcs(); 742 743 InitializeCriticalSection(&sequence_cs); 744 init_procs(); 745 746 if (!RegisterWindowClasses()) 747 assert(0); 748 749 cbt_hook_thread_id = GetCurrentThreadId(); 750 hCBT_hook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId()); 751 if (!hCBT_hook) 752 win_skip("cannot set global hook, will skip hook tests\n"); 753 } 754 755 static void cleanup_tests() 756 { 757 BOOL ret; 758 UnhookWindowsHookEx(hCBT_hook); 759 if (pUnhookWinEvent && hEvent_hook) 760 { 761 ret = pUnhookWinEvent(hEvent_hook); 762 ok(ret, "UnhookWinEvent error %ld\n", GetLastError()); 763 SetLastError(0xdeadbeef); 764 ok(!pUnhookWinEvent(hEvent_hook), "UnhookWinEvent succeeded\n"); 765 ok(GetLastError() == ERROR_INVALID_HANDLE || /* Win2k */ 766 GetLastError() == 0xdeadbeef, /* Win9x */ 767 "unexpected error %ld\n", GetLastError()); 768 } 769 DeleteCriticalSection(&sequence_cs); 770 } 771 772 START_TEST(SystemMenu) 773 { 774 init_tests(); 775 test_menu_messages(); 776 cleanup_tests(); 777 } 778