/* * PROJECT: ReactOS API tests * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) * PURPOSE: debugging and analysis of message states * COPYRIGHT: Copyright 2019-2023 Katayama Hirofumi MZ */ #include "precomp.h" #include "undocuser.h" #include "winxx.h" #include #include #define MAX_MSGS 512 static MSG s_Msgs[MAX_MSGS]; static UINT s_cMsgs = 0; static CHAR s_prefix[16] = ""; static HWND s_hMainWnd = NULL; static HWND s_hwndEdit = NULL; static HWND s_hImeWnd = NULL; static WNDPROC s_fnOldEditWndProc = NULL; static WNDPROC s_fnOldImeWndProc = NULL; static void MsgDumpPrintf(LPCSTR fmt, ...) { static char s_szText[1024]; va_list va; va_start(va, fmt); StringCbVPrintfA(s_szText, sizeof(s_szText), fmt, va); trace("%s", s_szText); va_end(va); } #define MSGDUMP_TPRINTF MsgDumpPrintf #define MSGDUMP_PREFIX s_prefix #include "msgdump.h" /* msgdump.h needs MSGDUMP_TPRINTF and MSGDUMP_PREFIX */ static void MD_build_prefix(void) { DWORD Flags = InSendMessageEx(NULL); INT i = 0; if (Flags & ISMEX_CALLBACK) s_prefix[i++] = 'C'; if (Flags & ISMEX_NOTIFY) s_prefix[i++] = 'N'; if (Flags & ISMEX_REPLIED) s_prefix[i++] = 'R'; if (Flags & ISMEX_SEND) s_prefix[i++] = 'S'; if (i == 0) s_prefix[i++] = 'P'; s_prefix[i++] = ':'; s_prefix[i++] = ' '; s_prefix[i] = 0; } #define STAGE_1 1 #define STAGE_2 2 #define STAGE_3 3 #define STAGE_4 4 #define STAGE_5 5 static INT findMessage(INT iMsg, HWND hwnd, UINT uMsg) { if (iMsg == -1) iMsg = 0; for (; iMsg < s_cMsgs; ++iMsg) { if (s_Msgs[iMsg].message == uMsg && s_Msgs[iMsg].hwnd == hwnd) return iMsg; } return -1; } typedef struct TEST_ENTRY { INT line; LPCSTR name; UINT iFound; } TEST_ENTRY, *PTEST_ENTRY; static VOID DoAnalyzeEntries(size_t nCount, PTEST_ENTRY pEntries) { size_t i; for (i = 0; i < nCount - 1; ++i) { PTEST_ENTRY entry1 = &pEntries[i]; PTEST_ENTRY entry2 = &pEntries[i + 1]; ok(entry1->iFound < entry2->iFound, "Line %d: message wrong order (%d >= %d): %s vs %s\n", entry1->line, entry1->iFound, entry2->iFound, entry1->name, entry2->name); } } static VOID DoAnalyzeAllMessages(VOID) { size_t i; TEST_ENTRY entries1[] = { { __LINE__, "WM_NCCREATE", findMessage(0, s_hMainWnd, WM_NCCREATE) }, { __LINE__, "WM_NCCALCSIZE", findMessage(0, s_hMainWnd, WM_NCCALCSIZE) }, { __LINE__, "WM_CREATE", findMessage(0, s_hMainWnd, WM_CREATE) }, { __LINE__, "WM_PARENTNOTIFY", findMessage(0, s_hMainWnd, WM_PARENTNOTIFY) }, { __LINE__, "WM_WINDOWPOSCHANGING", findMessage(0, s_hMainWnd, WM_WINDOWPOSCHANGING) }, { __LINE__, "WM_ACTIVATEAPP", findMessage(0, s_hMainWnd, WM_ACTIVATEAPP) }, { __LINE__, "WM_NCACTIVATE", findMessage(0, s_hMainWnd, WM_NCACTIVATE) }, { __LINE__, "WM_ACTIVATE", findMessage(0, s_hMainWnd, WM_ACTIVATE) }, { __LINE__, "WM_IME_SETCONTEXT", findMessage(0, s_hMainWnd, WM_IME_SETCONTEXT) }, { __LINE__, "WM_IME_NOTIFY", findMessage(0, s_hMainWnd, WM_IME_NOTIFY) }, { __LINE__, "WM_SETFOCUS", findMessage(0, s_hMainWnd, WM_SETFOCUS) }, { __LINE__, "WM_KILLFOCUS", findMessage(0, s_hMainWnd, WM_KILLFOCUS) }, }; INT iFound1 = entries1[_countof(entries1) - 1].iFound; TEST_ENTRY entries2[] = { { __LINE__, "WM_IME_SETCONTEXT", findMessage(iFound1, s_hMainWnd, WM_IME_SETCONTEXT) }, { __LINE__, "WM_IME_SETCONTEXT", findMessage(iFound1, s_hwndEdit, WM_IME_SETCONTEXT) }, { __LINE__, "WM_SETFOCUS", findMessage(iFound1, s_hwndEdit, WM_SETFOCUS) }, { __LINE__, "WM_SHOWWINDOW", findMessage(iFound1, s_hMainWnd, WM_SHOWWINDOW) }, { __LINE__, "WM_WINDOWPOSCHANGING", findMessage(iFound1, s_hMainWnd, WM_WINDOWPOSCHANGING) }, { __LINE__, "WM_NCPAINT", findMessage(iFound1, s_hMainWnd, WM_NCPAINT) }, { __LINE__, "WM_ERASEBKGND", findMessage(iFound1, s_hMainWnd, WM_ERASEBKGND) }, { __LINE__, "WM_WINDOWPOSCHANGED", findMessage(iFound1, s_hMainWnd, WM_WINDOWPOSCHANGED) }, { __LINE__, "WM_SIZE", findMessage(iFound1, s_hMainWnd, WM_SIZE) }, { __LINE__, "WM_MOVE", findMessage(iFound1, s_hMainWnd, WM_MOVE) }, }; INT iFound2 = entries2[_countof(entries2) - 1].iFound; TEST_ENTRY entries3[] = { { __LINE__, "WM_IME_KEYDOWN", findMessage(iFound2, s_hwndEdit, WM_IME_KEYDOWN) }, { __LINE__, "WM_KEYDOWN", findMessage(iFound2, s_hwndEdit, WM_KEYDOWN) }, { __LINE__, "WM_IME_KEYUP", findMessage(iFound2, s_hwndEdit, WM_IME_KEYUP) }, { __LINE__, "WM_CHAR", findMessage(iFound2, s_hwndEdit, WM_CHAR) }, { __LINE__, "WM_IME_CHAR", findMessage(iFound2, s_hwndEdit, WM_IME_CHAR) }, }; INT iFound3 = entries3[_countof(entries3) - 1].iFound; TEST_ENTRY entries4[] = { { __LINE__, "WM_IME_NOTIFY", findMessage(iFound3, s_hwndEdit, WM_IME_NOTIFY) }, { __LINE__, "WM_IME_NOTIFY", findMessage(iFound3, s_hImeWnd, WM_IME_NOTIFY) }, { __LINE__, "WM_IME_SETCONTEXT", findMessage(iFound3, s_hwndEdit, WM_IME_SETCONTEXT) }, { __LINE__, "WM_IME_SETCONTEXT", findMessage(iFound3, s_hImeWnd, WM_IME_SETCONTEXT) }, }; INT iFound4 = entries4[_countof(entries4) - 1].iFound; TEST_ENTRY entries5[] = { { __LINE__, "WM_DESTROY", findMessage(iFound4, s_hMainWnd, WM_DESTROY) }, { __LINE__, "WM_DESTROY", findMessage(iFound4, s_hwndEdit, WM_DESTROY) }, { __LINE__, "WM_NCDESTROY", findMessage(iFound4, s_hwndEdit, WM_NCDESTROY) }, { __LINE__, "WM_NCDESTROY", findMessage(iFound4, s_hMainWnd, WM_NCDESTROY) }, }; DoAnalyzeEntries(_countof(entries1), entries1); DoAnalyzeEntries(_countof(entries2), entries2); DoAnalyzeEntries(_countof(entries3), entries3); //DoAnalyzeEntries(_countof(entries4), entries4); // No order DoAnalyzeEntries(_countof(entries5), entries5); ok(iFound1 < entries2[0].iFound, "%d vs %d\n", iFound1, entries2[0].iFound); ok(iFound2 < entries3[0].iFound, "%d vs %d\n", iFound2, entries3[0].iFound); ok(iFound3 < entries4[0].iFound, "%d vs %d\n", iFound3, entries4[0].iFound); ok(iFound4 < entries5[0].iFound, "%d vs %d\n", iFound4, entries5[0].iFound); for (i = 0; i < _countof(entries4); ++i) { ok(entries4[i].iFound != -1, "entries4[%d].iFound was -1\n", i); } } static LRESULT CALLBACK EditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT ret; MSG msg = { hwnd, uMsg, wParam, lParam }; /* Build s_prefix */ MD_build_prefix(); /* message dump */ MD_msgdump(hwnd, uMsg, wParam, lParam); /* Add message */ if (s_cMsgs < MAX_MSGS) s_Msgs[s_cMsgs++] = msg; /* Do inner task */ ret = CallWindowProc(s_fnOldEditWndProc, hwnd, uMsg, wParam, lParam); /* message return */ StringCbCopyA(s_prefix, sizeof(s_prefix), "R: "); MD_msgresult(hwnd, uMsg, wParam, lParam, ret); return ret; } static LRESULT CALLBACK ImeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT ret; MSG msg = { hwnd, uMsg, wParam, lParam }; /* Build s_prefix */ MD_build_prefix(); /* message dump */ MD_msgdump(hwnd, uMsg, wParam, lParam); /* Add message */ if (s_cMsgs < MAX_MSGS) s_Msgs[s_cMsgs++] = msg; /* Do inner task */ ret = CallWindowProc(s_fnOldImeWndProc, hwnd, uMsg, wParam, lParam); /* message return */ StringCbCopyA(s_prefix, sizeof(s_prefix), "R: "); MD_msgresult(hwnd, uMsg, wParam, lParam, ret); return ret; } static LRESULT CALLBACK InnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_NCCREATE: s_hMainWnd = hwnd; trace("s_hMainWnd: %p\n", s_hMainWnd); break; case WM_CREATE: s_hwndEdit = CreateWindowW(L"EDIT", NULL, WS_CHILD | WS_VISIBLE, 0, 0, 100, 20, hwnd, NULL, GetModuleHandleW(NULL), NULL); ok(s_hwndEdit != NULL, "s_hwndEdit was NULL\n"); trace("s_hwndEdit: %p\n", s_hwndEdit); s_fnOldEditWndProc = (WNDPROC)SetWindowLongPtrW(s_hwndEdit, GWLP_WNDPROC, (LONG_PTR)EditWindowProc); SetFocus(s_hwndEdit); PostMessageW(hwnd, WM_COMMAND, STAGE_1, 0); break; case WM_COMMAND: switch (LOWORD(wParam)) { case STAGE_1: s_hImeWnd = ImmGetDefaultIMEWnd(hwnd); ok(s_hImeWnd != NULL, "s_hImeWnd was NULL\n"); trace("s_hImeWnd: %p\n", s_hImeWnd ); s_fnOldImeWndProc = (WNDPROC)SetWindowLongPtrW(s_hImeWnd, GWLP_WNDPROC, (LONG_PTR)ImeWindowProc); PostMessageW(hwnd, WM_COMMAND, STAGE_2, 0); break; case STAGE_2: PostMessageW(s_hwndEdit, WM_IME_KEYDOWN, 'A', 0); PostMessageW(hwnd, WM_COMMAND, STAGE_3, 0); break; case STAGE_3: PostMessageW(s_hwndEdit, WM_IME_KEYUP, 'A', 0); PostMessageW(hwnd, WM_COMMAND, STAGE_4, 0); break; case STAGE_4: PostMessageW(s_hwndEdit, WM_IME_CHAR, 'A', 0); PostMessageW(hwnd, WM_COMMAND, STAGE_5, 0); break; case STAGE_5: PostMessageW(hwnd, WM_CLOSE, 0, 0); break; } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProcW(hwnd, uMsg, wParam, lParam); } static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT ret; MSG msg = { hwnd, uMsg, wParam, lParam }; /* Build s_prefix */ MD_build_prefix(); /* message dump */ MD_msgdump(hwnd, uMsg, wParam, lParam); /* Add message */ if (s_cMsgs < MAX_MSGS) s_Msgs[s_cMsgs++] = msg; /* Do inner task */ ret = InnerWindowProc(hwnd, uMsg, wParam, lParam); /* message return */ StringCbCopyA(s_prefix, sizeof(s_prefix), "R: "); MD_msgresult(hwnd, uMsg, wParam, lParam, ret); return ret; } START_TEST(MessageStateAnalyzer) { WNDCLASSW wc; HWND hwnd; MSG msg; static const WCHAR s_szName[] = L"MessageStateAnalyzer"; /* register window class */ ZeroMemory(&wc, sizeof(wc)); wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; wc.lpfnWndProc = WindowProc; wc.hInstance = GetModuleHandleW(NULL); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); wc.lpszClassName = s_szName; if (!RegisterClassW(&wc)) { skip("RegisterClassW failed.\n"); return; } /* create a window */ hwnd = CreateWindowW(s_szName, s_szName, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, NULL, NULL, GetModuleHandleW(NULL), NULL); if (!hwnd) { skip("CreateWindowW failed.\n"); return; } /* message loop */ while (GetMessageW(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } DoAnalyzeAllMessages(); }