From f7c36c6d942ec3821b10ca6d5009e3df0f0bc63a Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Thu, 26 Jun 2025 18:20:49 +0900 Subject: [PATCH] [SHELL32_APITEST] Certainly close newly-opened windows (#8171) It's frustrating when windows open during a test and remain after the test is over. JIRA issue: ROSTESTS-402 - Delete closewnd.cpp. Enhance closewnd.h. - Modify ShellExecCmdLine, ShellExec_RunDLL, ShellExecuteEx, and ShellExecuteW testcases. - Certainly close the newly-opened windows. --- .../rostests/apitests/shell32/CMakeLists.txt | 1 - .../apitests/shell32/RealShellExecuteEx.cpp | 17 ++- .../apitests/shell32/ShellExecCmdLine.cpp | 96 +++++-------- .../apitests/shell32/ShellExec_RunDLL.cpp | 35 ++--- .../apitests/shell32/ShellExecuteEx.cpp | 23 +++- .../apitests/shell32/ShellExecuteW.cpp | 71 ++-------- .../rostests/apitests/shell32/closewnd.cpp | 118 ---------------- modules/rostests/apitests/shell32/closewnd.h | 126 +++++++++++++++++- 8 files changed, 210 insertions(+), 277 deletions(-) delete mode 100644 modules/rostests/apitests/shell32/closewnd.cpp diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 4ac921e0692..8cc8c938c7d 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -2,7 +2,6 @@ spec2def(shell32_apitest.exe shell32_apitest.spec) list(APPEND SOURCE - closewnd.cpp AddCommas.cpp CFSFolder.cpp CheckEscapes.cpp diff --git a/modules/rostests/apitests/shell32/RealShellExecuteEx.cpp b/modules/rostests/apitests/shell32/RealShellExecuteEx.cpp index 2f0552fd79b..f59b0ebe4c2 100644 --- a/modules/rostests/apitests/shell32/RealShellExecuteEx.cpp +++ b/modules/rostests/apitests/shell32/RealShellExecuteEx.cpp @@ -53,7 +53,10 @@ static void TEST_Start(void) static void TEST_End(void) { - Sleep(500); + // Execution can be asynchronous; you have to wait for it to finish. + Sleep(2000); + + // Close newly-opened window(s) GetWindowList(&s_List2); CloseNewWindows(&s_List1, &s_List2); FreeWindowList(&s_List1); @@ -62,8 +65,6 @@ static void TEST_End(void) static void TEST_RealShellExecuteExA(void) { - TEST_Start(); - INT_PTR ret; ret = (INT_PTR)s_fnRealShellExecuteExA( @@ -82,14 +83,10 @@ static void TEST_RealShellExecuteExA(void) ok_long((LONG)ret, 42); else ok_long((LONG)ret, 2); - - TEST_End(); } static void TEST_RealShellExecuteExW(void) { - TEST_Start(); - INT_PTR ret; ret = (INT_PTR)s_fnRealShellExecuteExW( @@ -105,8 +102,6 @@ static void TEST_RealShellExecuteExW(void) NULL, 0); ok_long((LONG)ret, 42); - - TEST_End(); } START_TEST(RealShellExecuteEx) @@ -129,8 +124,12 @@ START_TEST(RealShellExecuteEx) return; } + TEST_Start(); + TEST_RealShellExecuteExA(); TEST_RealShellExecuteExW(); + TEST_End(); + FreeLibrary(s_hSHELL32); } diff --git a/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp b/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp index 37ee9c0260e..ef06fd9ee79 100644 --- a/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp +++ b/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp @@ -9,6 +9,7 @@ #include #include #include "shell32_apitest_sub.h" +#include "closewnd.h" #define NDEBUG #include @@ -549,46 +550,6 @@ static const TEST_ENTRY s_entries_2[] = { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir }, }; -typedef struct OPENWNDS -{ - UINT count; - HWND *phwnd; -} OPENWNDS; - -static OPENWNDS s_wi0 = { 0 }, s_wi1 = { 0 }; - -static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) -{ - OPENWNDS *info = (OPENWNDS *)lParam; - info->phwnd = (HWND *)realloc(info->phwnd, (info->count + 1) * sizeof(HWND)); - if (!info->phwnd) - return FALSE; - info->phwnd[info->count] = hwnd; - ++(info->count); - return TRUE; -} - -static void CleanupNewlyCreatedWindows(void) -{ - EnumWindows(EnumWindowsProc, (LPARAM)&s_wi1); - for (UINT i1 = 0; i1 < s_wi1.count; ++i1) - { - BOOL bFound = FALSE; - for (UINT i0 = 0; i0 < s_wi0.count; ++i0) - { - if (s_wi1.phwnd[i1] == s_wi0.phwnd[i0]) - { - bFound = TRUE; - break; - } - } - if (!bFound) - PostMessageW(s_wi1.phwnd[i1], WM_CLOSE, 0, 0); - } - free(s_wi1.phwnd); - ZeroMemory(&s_wi1, sizeof(s_wi1)); -} - static void DoEntry(const TEST_ENTRY *pEntry) { HRESULT hr; @@ -626,8 +587,33 @@ static void DoEntry(const TEST_ENTRY *pEntry) ok(result == pEntry->result, "Line %d: result expected %d, was %d\n", pEntry->lineno, pEntry->result, result); +} - CleanupNewlyCreatedWindows(); +static WINDOW_LIST s_List1, s_List2; + +static VOID TEST_ShellExecCmdLine(VOID) +{ + GetWindowList(&s_List1); + + // do tests + for (size_t i = 0; i < _countof(s_entries_1); ++i) + { + DoEntry(&s_entries_1[i]); + } + SetEnvironmentVariableW(L"PATH", s_cur_dir); + for (size_t i = 0; i < _countof(s_entries_2); ++i) + { + DoEntry(&s_entries_2[i]); + } + + // Execution can be asynchronous; you have to wait for it to finish. + Sleep(2000); + + // Close newly-opened window(s) + GetWindowList(&s_List2); + CloseNewWindows(&s_List1, &s_List2); + FreeWindowList(&s_List1); + FreeWindowList(&s_List2); } START_TEST(ShellExecCmdLine) @@ -657,14 +643,6 @@ START_TEST(ShellExecCmdLine) return; } - // record open windows - if (!EnumWindows(EnumWindowsProc, (LPARAM)&s_wi0)) - { - skip("EnumWindows failed\n"); - free(s_wi0.phwnd); - return; - } - // s_win_test_exe GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe)); PathAppendW(s_win_test_exe, L"test program.exe"); @@ -672,7 +650,6 @@ START_TEST(ShellExecCmdLine) if (!ret) { skip("Please retry with admin rights\n"); - free(s_wi0.phwnd); return; } @@ -700,27 +677,14 @@ START_TEST(ShellExecCmdLine) // s_cur_dir GetCurrentDirectoryW(_countof(s_cur_dir), s_cur_dir); - // do tests - for (size_t i = 0; i < _countof(s_entries_1); ++i) - { - DoEntry(&s_entries_1[i]); - } - SetEnvironmentVariableW(L"PATH", s_cur_dir); - for (size_t i = 0; i < _countof(s_entries_2); ++i) - { - DoEntry(&s_entries_2[i]); - } + TEST_ShellExecCmdLine(); - Sleep(2000); - CleanupNewlyCreatedWindows(); + // Some process can lock the file of s_win_test_exe + Sleep(1000); // clean up ok(DeleteFileW(s_win_test_exe), "failed to delete the test file\n"); ok(DeleteFileW(s_sys_bat_file), "failed to delete the test file\n"); ok(DeleteFileA("Test File 1.txt"), "failed to delete the test file\n"); ok(DeleteFileA("Test File 2.bat"), "failed to delete the test file\n"); - free(s_wi0.phwnd); - - DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, TRUE, TRUE); - Sleep(100); } diff --git a/modules/rostests/apitests/shell32/ShellExec_RunDLL.cpp b/modules/rostests/apitests/shell32/ShellExec_RunDLL.cpp index bea1d8b0682..c3000ca6be2 100644 --- a/modules/rostests/apitests/shell32/ShellExec_RunDLL.cpp +++ b/modules/rostests/apitests/shell32/ShellExec_RunDLL.cpp @@ -26,36 +26,41 @@ static VOID TEST_ShellExec_RunDLLW(VOID) ShellExec_RunDLLW(NULL, NULL, L"?0?notepad.exe", SW_SHOWNORMAL); } -static VOID CleanupWindowList(VOID) +static BOOL CloseNotepad(VOID) { - GetWindowListForClose(&s_List2); + HWND hwndNew; + WCHAR szClass[64]; + + // Execution can be asynchronous; you have to wait for it to finish. + Sleep(1000); + + // Close newly-opened window(s) + GetWindowList(&s_List2); + hwndNew = FindNewWindow(&s_List1, &s_List2); + if (!GetClassNameW(hwndNew, szClass, _countof(szClass))) + szClass[0] = UNICODE_NULL; CloseNewWindows(&s_List1, &s_List2); FreeWindowList(&s_List1); FreeWindowList(&s_List2); + return lstrcmpiW(szClass, L"Notepad") == 0; } START_TEST(ShellExec_RunDLL) { - HWND hwndNotepad; + BOOL ret; GetWindowList(&s_List1); TEST_ShellExec_RunDLL(); - Sleep(1000); - hwndNotepad = FindWindowW(L"Notepad", NULL); - ok(hwndNotepad != NULL, "Notepad not found\n"); - CleanupWindowList(); + ret = CloseNotepad(); + ok(ret, "Notepad not found\n"); GetWindowList(&s_List1); TEST_ShellExec_RunDLLA(); - Sleep(1000); - hwndNotepad = FindWindowW(L"Notepad", NULL); - ok(hwndNotepad != NULL, "Notepad not found\n"); - CleanupWindowList(); + ret = CloseNotepad(); + ok(ret, "Notepad not found\n"); GetWindowList(&s_List1); TEST_ShellExec_RunDLLW(); - Sleep(1000); - hwndNotepad = FindWindowW(L"Notepad", NULL); - ok(hwndNotepad != NULL, "Notepad not found\n"); - CleanupWindowList(); + ret = CloseNotepad(); + ok(ret, "Notepad not found\n"); } diff --git a/modules/rostests/apitests/shell32/ShellExecuteEx.cpp b/modules/rostests/apitests/shell32/ShellExecuteEx.cpp index 64759a49c1e..3343713631a 100644 --- a/modules/rostests/apitests/shell32/ShellExecuteEx.cpp +++ b/modules/rostests/apitests/shell32/ShellExecuteEx.cpp @@ -314,17 +314,30 @@ static BOOL TEST_Start(void) static void TEST_End(void) { - GetWindowListForClose(&s_List2); - CloseNewWindows(&s_List1, &s_List2); - FreeWindowList(&s_List1); - FreeWindowList(&s_List2); - DeleteFileW(s_win_test_exe); DeleteFileW(s_sys_test_exe); DeleteFileW(s_win_txt_file); DeleteFileW(s_sys_txt_file); DeleteFileW(s_win_bat_file); DeleteFileW(s_sys_bat_file); + + // Execution can be asynchronous; you have to wait for it to finish. + INT nCount = GetWindowCount(); + for (INT i = 0; i < 100; ++i) + { + INT nOldCount = nCount; + Sleep(3000); + nCount = GetWindowCount(); + if (nOldCount == nCount) + break; + } + Sleep(3000); + + // Close newly-opened window(s) + GetWindowList(&s_List2); + CloseNewWindows(&s_List1, &s_List2); + FreeWindowList(&s_List1); + FreeWindowList(&s_List2); } static void test_properties() diff --git a/modules/rostests/apitests/shell32/ShellExecuteW.cpp b/modules/rostests/apitests/shell32/ShellExecuteW.cpp index 8046c3e5282..d367203e32f 100644 --- a/modules/rostests/apitests/shell32/ShellExecuteW.cpp +++ b/modules/rostests/apitests/shell32/ShellExecuteW.cpp @@ -10,18 +10,22 @@ #include #include #include +#include "closewnd.h" #define WAIT_SLEEP 700 // ShellExecuteW(handle, "open", , , NULL, SW_SHOWNORMAL); +static WINDOW_LIST s_List1, s_List2; + START_TEST(ShellExecuteW) { INT ret; HINSTANCE hInstance; - HWND hWnd; WCHAR WinDir[MAX_PATH], SysDir[MAX_PATH], SysDrive[MAX_PATH]; + GetWindowList(&s_List1); + if (!GetWindowsDirectoryW(WinDir, _countof(WinDir))) { skip("GetWindowsDirectoryW failed\n"); @@ -47,104 +51,57 @@ START_TEST(ShellExecuteW) ret = (INT)(UINT_PTR)hInstance; ok(ret > 31, "TEST #1: ret:%d, LastError: %ld\n", ret, GetLastError()); trace("TEST #1 ret: %d.\n", ret); - if (hInstance) - { - Sleep(WAIT_SLEEP); - // Terminate Window - hWnd = FindWindowW(NULL, L"Display Properties"); - PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); - } // TEST #2: Open Notepad hInstance = ShellExecuteW(NULL, L"open", L"notepad.exe", NULL, NULL, SW_SHOWNORMAL); ret = (INT)(UINT_PTR)hInstance; ok(ret > 31, "TEST #2: ret:%d, LastError: %ld\n", ret, GetLastError()); trace("TEST #2 ret: %d.\n", ret); - if (hInstance) - { - Sleep(WAIT_SLEEP); - // Terminate Window - hWnd = FindWindowW(L"Notepad", L"Untitled - Notepad"); - PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); - } // TEST #3: Open Windows folder hInstance = ShellExecuteW(NULL, NULL, WinDir, NULL, NULL, SW_SHOWNORMAL); ret = (INT)(UINT_PTR)hInstance; ok(ret > 31, "TEST #3: ret:%d, LastError: %ld\n", ret, GetLastError()); trace("TEST #3 ret: %d.\n", ret); - if (hInstance) - { - Sleep(WAIT_SLEEP); - // Terminate Window - hWnd = FindWindowW(L"CabinetWClass", WinDir); - PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); - } // TEST #4: Open system32 folder hInstance = ShellExecuteW(NULL, L"open", SysDir, NULL, NULL, SW_SHOWNORMAL); ret = (INT)(UINT_PTR)hInstance; ok(ret > 31, "TEST #4: ret:%d, LastError: %ld\n", ret, GetLastError()); trace("TEST #4 ret: %d.\n", ret); - if (hInstance) - { - Sleep(WAIT_SLEEP); - // Terminate Window - hWnd = FindWindowW(L"CabinetWClass", SysDir); - PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); - } // TEST #5: Open %SystemDrive% hInstance = ShellExecuteW(NULL, L"explore", SysDrive, NULL, NULL, SW_SHOWNORMAL); ret = (INT)(UINT_PTR)hInstance; ok(ret > 31, "TEST #5: ret:%d, LastError: %ld\n", ret, GetLastError()); trace("TEST #5 ret: %d.\n", ret); - if (hInstance) - { - Sleep(WAIT_SLEEP); - // Terminate Window - hWnd = FindWindowW(L"ExploreWClass", SysDrive); - PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); - } // TEST #6: Open Explorer Search on %SYSTEMDRIVE% hInstance = ShellExecuteW(NULL, L"find", SysDrive, NULL, NULL, SW_SHOWNORMAL); ret = (INT)(UINT_PTR)hInstance; ok(ret > 31, "TEST #6: ret:%d, LastError: %ld\n", ret, GetLastError()); trace("TEST #6 ret: %d.\n", ret); - if (hInstance) - { - Sleep(WAIT_SLEEP); - // Terminate Window - hWnd = FindWindowW(L"CabinetWClass", L"Search Results"); - PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); - } // TEST #7: Open My Documents ("::{450d8fba-ad25-11d0-98a8-0800361b1103}") hInstance = ShellExecuteW(NULL, NULL, L"::{450d8fba-ad25-11d0-98a8-0800361b1103}", NULL, NULL, SW_SHOWNORMAL); ret = (INT)(UINT_PTR)hInstance; ok(ret > 31, "TEST #7: ret:%d, LastError: %ld\n", ret, GetLastError()); trace("TEST #7 ret: %d.\n", ret); - if (hInstance) - { - Sleep(WAIT_SLEEP); - // Terminate Window - hWnd = FindWindowW(L"CabinetWClass", NULL); - PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); - } // TEST #8: Open My Documents ("shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}") hInstance = ShellExecuteW(NULL, L"open", L"shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}", NULL, NULL, SW_SHOWNORMAL); ret = (INT)(UINT_PTR)hInstance; ok(ret > 31, "TEST #8: ret:%d, LastError: %ld\n", ret, GetLastError()); trace("TEST #8 ret: %d.\n", ret); - if (hInstance) - { - Sleep(WAIT_SLEEP); - // Terminate Window - hWnd = FindWindowW(L"CabinetWClass", NULL); - PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); - } + + // Execution can be asynchronous; you have to wait for it to finish. + Sleep(2000); + + // Close newly-opened window(s) + GetWindowList(&s_List2); + CloseNewWindows(&s_List1, &s_List2); + FreeWindowList(&s_List1); + FreeWindowList(&s_List2); } // Windows Server 2003 and Windows XP SP3 return values (Win 7 returns 42 in all cases) diff --git a/modules/rostests/apitests/shell32/closewnd.cpp b/modules/rostests/apitests/shell32/closewnd.cpp deleted file mode 100644 index 2a7d8991b5c..00000000000 --- a/modules/rostests/apitests/shell32/closewnd.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * PROJECT: ReactOS API tests - * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) - * PURPOSE: Close windows after tests - * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) - */ - -#include "shelltest.h" -#include "closewnd.h" - -void FreeWindowList(PWINDOW_LIST pList) -{ - free(pList->m_phWnds); - pList->m_phWnds = NULL; - pList->m_chWnds = 0; -} - -static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) -{ - if (!IsWindowVisible(hwnd)) - return TRUE; - - PWINDOW_LIST pList = (PWINDOW_LIST)lParam; - SIZE_T cb = (pList->m_chWnds + 1) * sizeof(HWND); - HWND *phWnds = (HWND *)realloc(pList->m_phWnds, cb); - if (!phWnds) - return FALSE; - phWnds[pList->m_chWnds++] = hwnd; - pList->m_phWnds = phWnds; - return TRUE; -} - -void GetWindowList(PWINDOW_LIST pList) -{ - pList->m_phWnds = NULL; - pList->m_chWnds = 0; - EnumWindows(EnumWindowsProc, (LPARAM)pList); -} - -void GetWindowListForClose(PWINDOW_LIST pList) -{ - for (UINT tries = 5; tries--;) - { - if (tries) - FreeWindowList(pList); - GetWindowList(pList); - Sleep(500); - WINDOW_LIST list; - GetWindowList(&list); - SIZE_T count = list.m_chWnds; - FreeWindowList(&list); - if (count == pList->m_chWnds) - break; - } -} - -static HWND FindInWindowList(const WINDOW_LIST &list, HWND hWnd) -{ - for (SIZE_T i = 0; i < list.m_chWnds; ++i) - { - if (list.m_phWnds[i] == hWnd) - return hWnd; - } - return NULL; -} - -HWND FindNewWindow(PWINDOW_LIST List1, PWINDOW_LIST List2) -{ - for (SIZE_T i2 = 0; i2 < List2->m_chWnds; ++i2) - { - HWND hWnd = List2->m_phWnds[i2]; - if (!IsWindowEnabled(hWnd) || !IsWindowVisible(hWnd)) - continue; - - BOOL bFoundInList1 = FALSE; - for (SIZE_T i1 = 0; i1 < List1->m_chWnds; ++i1) - { - if (hWnd == List1->m_phWnds[i1]) - { - bFoundInList1 = TRUE; - break; - } - } - - if (!bFoundInList1) - return hWnd; - } - return NULL; -} - -static void WaitForForegroundWindow(HWND hWnd, UINT wait = 250) -{ - for (UINT waited = 0, interval = 50; waited < wait; waited += interval) - { - if (GetForegroundWindow() == hWnd || !IsWindowVisible(hWnd)) - return; - Sleep(interval); - } -} - -void CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2) -{ - for (SIZE_T i = 0; i < List2->m_chWnds; ++i) - { - HWND hWnd = List2->m_phWnds[i]; - if (!IsWindow(hWnd) || FindInWindowList(*List1, hWnd)) - continue; - - SwitchToThisWindow(hWnd, TRUE); - WaitForForegroundWindow(hWnd); - - if (!PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0)) - { - DWORD_PTR result; - SendMessageTimeoutW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0, 0, 3000, &result); - } - } -} diff --git a/modules/rostests/apitests/shell32/closewnd.h b/modules/rostests/apitests/shell32/closewnd.h index 096ce88c1fe..2e62f18cd3a 100644 --- a/modules/rostests/apitests/shell32/closewnd.h +++ b/modules/rostests/apitests/shell32/closewnd.h @@ -2,7 +2,7 @@ * PROJECT: ReactOS API tests * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Close windows after tests - * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + * COPYRIGHT: Copyright 2024-2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ #pragma once @@ -13,8 +13,122 @@ typedef struct WINDOW_LIST HWND *m_phWnds; } WINDOW_LIST, *PWINDOW_LIST; -void GetWindowList(PWINDOW_LIST pList); -void GetWindowListForClose(PWINDOW_LIST pList); -HWND FindNewWindow(PWINDOW_LIST List1, PWINDOW_LIST List2); -void CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2); -void FreeWindowList(PWINDOW_LIST pList); +static inline VOID FreeWindowList(PWINDOW_LIST pList) +{ + free(pList->m_phWnds); + pList->m_phWnds = NULL; + pList->m_chWnds = 0; +} + +static inline BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) +{ + if (!IsWindowVisible(hwnd)) + return TRUE; + + PWINDOW_LIST pList = (PWINDOW_LIST)lParam; + SIZE_T cb = (pList->m_chWnds + 1) * sizeof(HWND); + HWND *phWnds = (HWND *)realloc(pList->m_phWnds, cb); + if (!phWnds) + return FALSE; + phWnds[pList->m_chWnds++] = hwnd; + pList->m_phWnds = phWnds; + return TRUE; +} + +static inline VOID GetWindowList(PWINDOW_LIST pList) +{ + pList->m_phWnds = NULL; + pList->m_chWnds = 0; + EnumWindows(EnumWindowsProc, (LPARAM)pList); +} + +static inline HWND FindInWindowList(const WINDOW_LIST &list, HWND hWnd) +{ + for (SIZE_T i = 0; i < list.m_chWnds; ++i) + { + if (list.m_phWnds[i] == hWnd) + return hWnd; + } + return NULL; +} + +static inline VOID CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2) +{ + for (SIZE_T i = 0; i < List2->m_chWnds; ++i) + { + HWND hWnd = List2->m_phWnds[i]; + if (!IsWindowVisible(hWnd) || FindInWindowList(*List1, hWnd)) + continue; + + if (!PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0)) + { + DWORD_PTR result; + if (!SendMessageTimeoutW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_ABORTIFHUNG, 3000, &result)) + { + SwitchToThisWindow(hWnd, TRUE); + + // SwitchToThisWindow may take time + Sleep(500); + + // Alt+F4 + INPUT inputs[4]; + ZeroMemory(&inputs, sizeof(inputs)); + inputs[0].type = inputs[1].type = inputs[2].type = inputs[3].type = INPUT_KEYBOARD; + inputs[0].ki.wVk = inputs[3].ki.wVk = VK_LMENU; + inputs[1].ki.wVk = inputs[2].ki.wVk = VK_F4; + inputs[2].ki.dwFlags = inputs[3].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(_countof(inputs), inputs, sizeof(INPUT)); + + // Closing a window may take time + Sleep(1000); + + if (IsWindowVisible(hWnd)) + { + CHAR szClass[64]; + GetClassNameA(hWnd, szClass, _countof(szClass)); + trace("Unable to close window %p (%s)\n", hWnd, szClass); + } + } + } + } +} + +static inline HWND FindNewWindow(PWINDOW_LIST List1, PWINDOW_LIST List2) +{ + for (SIZE_T i2 = 0; i2 < List2->m_chWnds; ++i2) + { + HWND hWnd = List2->m_phWnds[i2]; + if (!IsWindowEnabled(hWnd) || !IsWindowVisible(hWnd)) + continue; + + BOOL bFoundInList1 = FALSE; + for (SIZE_T i1 = 0; i1 < List1->m_chWnds; ++i1) + { + if (hWnd == List1->m_phWnds[i1]) + { + bFoundInList1 = TRUE; + break; + } + } + + if (!bFoundInList1) + return hWnd; + } + return NULL; +} + +static inline BOOL CALLBACK CountWindowsProc(HWND hwnd, LPARAM lParam) +{ + if (!IsWindowVisible(hwnd)) + return TRUE; + + *(PINT)lParam += 1; + return TRUE; +} + +static inline INT GetWindowCount(VOID) +{ + INT nCount = 0; + EnumWindows(CountWindowsProc, (LPARAM)&nCount); + return nCount; +}