1 /* 2 * PROJECT: ReactOS API tests 3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) 4 * PURPOSE: debugging and analysis of message states 5 * COPYRIGHT: Copyright 2019-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #include "precomp.h" 9 #include "undocuser.h" 10 #include "winxx.h" 11 #include <imm.h> 12 #include <strsafe.h> 13 14 #define MAX_MSGS 512 15 16 static MSG s_Msgs[MAX_MSGS]; 17 static UINT s_cMsgs = 0; 18 static CHAR s_prefix[16] = ""; 19 static HWND s_hMainWnd = NULL; 20 static HWND s_hwndEdit = NULL; 21 static HWND s_hImeWnd = NULL; 22 static WNDPROC s_fnOldEditWndProc = NULL; 23 static WNDPROC s_fnOldImeWndProc = NULL; 24 25 static void MsgDumpPrintf(LPCSTR fmt, ...) 26 { 27 static char s_szText[1024]; 28 va_list va; 29 va_start(va, fmt); 30 StringCbVPrintfA(s_szText, sizeof(s_szText), fmt, va); 31 trace("%s", s_szText); 32 va_end(va); 33 } 34 #define MSGDUMP_TPRINTF MsgDumpPrintf 35 #define MSGDUMP_PREFIX s_prefix 36 #include "msgdump.h" /* msgdump.h needs MSGDUMP_TPRINTF and MSGDUMP_PREFIX */ 37 38 static void MD_build_prefix(void) 39 { 40 DWORD Flags = InSendMessageEx(NULL); 41 INT i = 0; 42 43 if (Flags & ISMEX_CALLBACK) 44 s_prefix[i++] = 'C'; 45 if (Flags & ISMEX_NOTIFY) 46 s_prefix[i++] = 'N'; 47 if (Flags & ISMEX_REPLIED) 48 s_prefix[i++] = 'R'; 49 if (Flags & ISMEX_SEND) 50 s_prefix[i++] = 'S'; 51 if (i == 0) 52 s_prefix[i++] = 'P'; 53 54 s_prefix[i++] = ':'; 55 s_prefix[i++] = ' '; 56 s_prefix[i] = 0; 57 } 58 59 #define STAGE_1 1 60 #define STAGE_2 2 61 #define STAGE_3 3 62 #define STAGE_4 4 63 #define STAGE_5 5 64 65 static INT findMessage(INT iMsg, HWND hwnd, UINT uMsg) 66 { 67 if (iMsg == -1) 68 iMsg = 0; 69 for (; iMsg < s_cMsgs; ++iMsg) 70 { 71 if (s_Msgs[iMsg].message == uMsg && s_Msgs[iMsg].hwnd == hwnd) 72 return iMsg; 73 } 74 return -1; 75 } 76 77 typedef struct TEST_ENTRY 78 { 79 INT line; 80 LPCSTR name; 81 UINT iFound; 82 } TEST_ENTRY, *PTEST_ENTRY; 83 84 static VOID DoAnalyzeEntries(size_t nCount, PTEST_ENTRY pEntries) 85 { 86 size_t i; 87 for (i = 0; i < nCount - 1; ++i) 88 { 89 PTEST_ENTRY entry1 = &pEntries[i]; 90 PTEST_ENTRY entry2 = &pEntries[i + 1]; 91 ok(entry1->iFound < entry2->iFound, 92 "Line %d: message wrong order (%d >= %d): %s vs %s\n", 93 entry1->line, entry1->iFound, entry2->iFound, 94 entry1->name, entry2->name); 95 } 96 } 97 98 static VOID DoAnalyzeAllMessages(VOID) 99 { 100 size_t i; 101 TEST_ENTRY entries1[] = 102 { 103 { __LINE__, "WM_NCCREATE", findMessage(0, s_hMainWnd, WM_NCCREATE) }, 104 { __LINE__, "WM_NCCALCSIZE", findMessage(0, s_hMainWnd, WM_NCCALCSIZE) }, 105 { __LINE__, "WM_CREATE", findMessage(0, s_hMainWnd, WM_CREATE) }, 106 { __LINE__, "WM_PARENTNOTIFY", findMessage(0, s_hMainWnd, WM_PARENTNOTIFY) }, 107 { __LINE__, "WM_WINDOWPOSCHANGING", findMessage(0, s_hMainWnd, WM_WINDOWPOSCHANGING) }, 108 { __LINE__, "WM_ACTIVATEAPP", findMessage(0, s_hMainWnd, WM_ACTIVATEAPP) }, 109 { __LINE__, "WM_NCACTIVATE", findMessage(0, s_hMainWnd, WM_NCACTIVATE) }, 110 { __LINE__, "WM_ACTIVATE", findMessage(0, s_hMainWnd, WM_ACTIVATE) }, 111 { __LINE__, "WM_IME_SETCONTEXT", findMessage(0, s_hMainWnd, WM_IME_SETCONTEXT) }, 112 { __LINE__, "WM_IME_NOTIFY", findMessage(0, s_hMainWnd, WM_IME_NOTIFY) }, 113 { __LINE__, "WM_SETFOCUS", findMessage(0, s_hMainWnd, WM_SETFOCUS) }, 114 { __LINE__, "WM_KILLFOCUS", findMessage(0, s_hMainWnd, WM_KILLFOCUS) }, 115 }; 116 INT iFound1 = entries1[_countof(entries1) - 1].iFound; 117 TEST_ENTRY entries2[] = 118 { 119 { __LINE__, "WM_IME_SETCONTEXT", findMessage(iFound1, s_hMainWnd, WM_IME_SETCONTEXT) }, 120 { __LINE__, "WM_IME_SETCONTEXT", findMessage(iFound1, s_hwndEdit, WM_IME_SETCONTEXT) }, 121 { __LINE__, "WM_SETFOCUS", findMessage(iFound1, s_hwndEdit, WM_SETFOCUS) }, 122 { __LINE__, "WM_SHOWWINDOW", findMessage(iFound1, s_hMainWnd, WM_SHOWWINDOW) }, 123 { __LINE__, "WM_WINDOWPOSCHANGING", findMessage(iFound1, s_hMainWnd, WM_WINDOWPOSCHANGING) }, 124 { __LINE__, "WM_NCPAINT", findMessage(iFound1, s_hMainWnd, WM_NCPAINT) }, 125 { __LINE__, "WM_ERASEBKGND", findMessage(iFound1, s_hMainWnd, WM_ERASEBKGND) }, 126 { __LINE__, "WM_WINDOWPOSCHANGED", findMessage(iFound1, s_hMainWnd, WM_WINDOWPOSCHANGED) }, 127 { __LINE__, "WM_SIZE", findMessage(iFound1, s_hMainWnd, WM_SIZE) }, 128 { __LINE__, "WM_MOVE", findMessage(iFound1, s_hMainWnd, WM_MOVE) }, 129 }; 130 INT iFound2 = entries2[_countof(entries2) - 1].iFound; 131 TEST_ENTRY entries3[] = 132 { 133 { __LINE__, "WM_IME_KEYDOWN", findMessage(iFound2, s_hwndEdit, WM_IME_KEYDOWN) }, 134 { __LINE__, "WM_KEYDOWN", findMessage(iFound2, s_hwndEdit, WM_KEYDOWN) }, 135 { __LINE__, "WM_IME_KEYUP", findMessage(iFound2, s_hwndEdit, WM_IME_KEYUP) }, 136 { __LINE__, "WM_CHAR", findMessage(iFound2, s_hwndEdit, WM_CHAR) }, 137 { __LINE__, "WM_IME_CHAR", findMessage(iFound2, s_hwndEdit, WM_IME_CHAR) }, 138 }; 139 INT iFound3 = entries3[_countof(entries3) - 1].iFound; 140 TEST_ENTRY entries4[] = 141 { 142 { __LINE__, "WM_IME_NOTIFY", findMessage(iFound3, s_hwndEdit, WM_IME_NOTIFY) }, 143 { __LINE__, "WM_IME_NOTIFY", findMessage(iFound3, s_hImeWnd, WM_IME_NOTIFY) }, 144 { __LINE__, "WM_IME_SETCONTEXT", findMessage(iFound3, s_hwndEdit, WM_IME_SETCONTEXT) }, 145 { __LINE__, "WM_IME_SETCONTEXT", findMessage(iFound3, s_hImeWnd, WM_IME_SETCONTEXT) }, 146 }; 147 INT iFound4 = entries4[_countof(entries4) - 1].iFound; 148 TEST_ENTRY entries5[] = 149 { 150 { __LINE__, "WM_DESTROY", findMessage(iFound4, s_hMainWnd, WM_DESTROY) }, 151 { __LINE__, "WM_DESTROY", findMessage(iFound4, s_hwndEdit, WM_DESTROY) }, 152 { __LINE__, "WM_NCDESTROY", findMessage(iFound4, s_hwndEdit, WM_NCDESTROY) }, 153 { __LINE__, "WM_NCDESTROY", findMessage(iFound4, s_hMainWnd, WM_NCDESTROY) }, 154 }; 155 DoAnalyzeEntries(_countof(entries1), entries1); 156 DoAnalyzeEntries(_countof(entries2), entries2); 157 DoAnalyzeEntries(_countof(entries3), entries3); 158 //DoAnalyzeEntries(_countof(entries4), entries4); // No order 159 DoAnalyzeEntries(_countof(entries5), entries5); 160 161 ok(iFound1 < entries2[0].iFound, "%d vs %d\n", iFound1, entries2[0].iFound); 162 ok(iFound2 < entries3[0].iFound, "%d vs %d\n", iFound2, entries3[0].iFound); 163 ok(iFound3 < entries4[0].iFound, "%d vs %d\n", iFound3, entries4[0].iFound); 164 ok(iFound4 < entries5[0].iFound, "%d vs %d\n", iFound4, entries5[0].iFound); 165 166 for (i = 0; i < _countof(entries4); ++i) 167 { 168 ok(entries4[i].iFound != -1, "entries4[%d].iFound was -1\n", i); 169 } 170 } 171 172 static LRESULT CALLBACK 173 EditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 174 { 175 LRESULT ret; 176 MSG msg = { hwnd, uMsg, wParam, lParam }; 177 178 /* Build s_prefix */ 179 MD_build_prefix(); 180 181 /* message dump */ 182 MD_msgdump(hwnd, uMsg, wParam, lParam); 183 184 /* Add message */ 185 if (s_cMsgs < MAX_MSGS) 186 s_Msgs[s_cMsgs++] = msg; 187 188 /* Do inner task */ 189 ret = CallWindowProc(s_fnOldEditWndProc, hwnd, uMsg, wParam, lParam); 190 191 /* message return */ 192 StringCbCopyA(s_prefix, sizeof(s_prefix), "R: "); 193 MD_msgresult(hwnd, uMsg, wParam, lParam, ret); 194 195 return ret; 196 } 197 198 static LRESULT CALLBACK 199 ImeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 200 { 201 LRESULT ret; 202 MSG msg = { hwnd, uMsg, wParam, lParam }; 203 204 /* Build s_prefix */ 205 MD_build_prefix(); 206 207 /* message dump */ 208 MD_msgdump(hwnd, uMsg, wParam, lParam); 209 210 /* Add message */ 211 if (s_cMsgs < MAX_MSGS) 212 s_Msgs[s_cMsgs++] = msg; 213 214 /* Do inner task */ 215 ret = CallWindowProc(s_fnOldImeWndProc, hwnd, uMsg, wParam, lParam); 216 217 /* message return */ 218 StringCbCopyA(s_prefix, sizeof(s_prefix), "R: "); 219 MD_msgresult(hwnd, uMsg, wParam, lParam, ret); 220 221 return ret; 222 } 223 224 static LRESULT CALLBACK 225 InnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 226 { 227 switch (uMsg) 228 { 229 case WM_NCCREATE: 230 s_hMainWnd = hwnd; 231 trace("s_hMainWnd: %p\n", s_hMainWnd); 232 break; 233 case WM_CREATE: 234 s_hwndEdit = CreateWindowW(L"EDIT", NULL, WS_CHILD | WS_VISIBLE, 235 0, 0, 100, 20, hwnd, NULL, GetModuleHandleW(NULL), NULL); 236 ok(s_hwndEdit != NULL, "s_hwndEdit was NULL\n"); 237 trace("s_hwndEdit: %p\n", s_hwndEdit); 238 s_fnOldEditWndProc = 239 (WNDPROC)SetWindowLongPtrW(s_hwndEdit, GWLP_WNDPROC, (LONG_PTR)EditWindowProc); 240 SetFocus(s_hwndEdit); 241 PostMessageW(hwnd, WM_COMMAND, STAGE_1, 0); 242 break; 243 case WM_COMMAND: 244 switch (LOWORD(wParam)) 245 { 246 case STAGE_1: 247 s_hImeWnd = ImmGetDefaultIMEWnd(hwnd); 248 ok(s_hImeWnd != NULL, "s_hImeWnd was NULL\n"); 249 trace("s_hImeWnd: %p\n", s_hImeWnd ); 250 s_fnOldImeWndProc = (WNDPROC)SetWindowLongPtrW(s_hImeWnd, GWLP_WNDPROC, 251 (LONG_PTR)ImeWindowProc); 252 PostMessageW(hwnd, WM_COMMAND, STAGE_2, 0); 253 break; 254 case STAGE_2: 255 PostMessageW(s_hwndEdit, WM_IME_KEYDOWN, 'A', 0); 256 PostMessageW(hwnd, WM_COMMAND, STAGE_3, 0); 257 break; 258 case STAGE_3: 259 PostMessageW(s_hwndEdit, WM_IME_KEYUP, 'A', 0); 260 PostMessageW(hwnd, WM_COMMAND, STAGE_4, 0); 261 break; 262 case STAGE_4: 263 PostMessageW(s_hwndEdit, WM_IME_CHAR, 'A', 0); 264 PostMessageW(hwnd, WM_COMMAND, STAGE_5, 0); 265 break; 266 case STAGE_5: 267 PostMessageW(hwnd, WM_CLOSE, 0, 0); 268 break; 269 } 270 break; 271 case WM_DESTROY: 272 PostQuitMessage(0); 273 break; 274 } 275 276 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 277 } 278 279 static LRESULT CALLBACK 280 WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 281 { 282 LRESULT ret; 283 MSG msg = { hwnd, uMsg, wParam, lParam }; 284 285 /* Build s_prefix */ 286 MD_build_prefix(); 287 288 /* message dump */ 289 MD_msgdump(hwnd, uMsg, wParam, lParam); 290 291 /* Add message */ 292 if (s_cMsgs < MAX_MSGS) 293 s_Msgs[s_cMsgs++] = msg; 294 295 /* Do inner task */ 296 ret = InnerWindowProc(hwnd, uMsg, wParam, lParam); 297 298 /* message return */ 299 StringCbCopyA(s_prefix, sizeof(s_prefix), "R: "); 300 MD_msgresult(hwnd, uMsg, wParam, lParam, ret); 301 302 return ret; 303 } 304 305 START_TEST(MessageStateAnalyzer) 306 { 307 WNDCLASSW wc; 308 HWND hwnd; 309 MSG msg; 310 static const WCHAR s_szName[] = L"MessageStateAnalyzer"; 311 312 /* register window class */ 313 ZeroMemory(&wc, sizeof(wc)); 314 wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; 315 wc.lpfnWndProc = WindowProc; 316 wc.hInstance = GetModuleHandleW(NULL); 317 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 318 wc.hCursor = LoadCursor(NULL, IDC_ARROW); 319 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); 320 wc.lpszClassName = s_szName; 321 if (!RegisterClassW(&wc)) 322 { 323 skip("RegisterClassW failed.\n"); 324 return; 325 } 326 327 /* create a window */ 328 hwnd = CreateWindowW(s_szName, s_szName, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 329 CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, NULL, NULL, 330 GetModuleHandleW(NULL), NULL); 331 if (!hwnd) 332 { 333 skip("CreateWindowW failed.\n"); 334 return; 335 } 336 337 /* message loop */ 338 while (GetMessageW(&msg, NULL, 0, 0)) 339 { 340 TranslateMessage(&msg); 341 DispatchMessageW(&msg); 342 } 343 344 DoAnalyzeAllMessages(); 345 } 346