[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.
This commit is contained in:
Katayama Hirofumi MZ 2025-06-26 18:20:49 +09:00 committed by GitHub
parent f1332c7722
commit f7c36c6d94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 210 additions and 277 deletions

View file

@ -2,7 +2,6 @@
spec2def(shell32_apitest.exe shell32_apitest.spec)
list(APPEND SOURCE
closewnd.cpp
AddCommas.cpp
CFSFolder.cpp
CheckEscapes.cpp

View file

@ -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);
}

View file

@ -9,6 +9,7 @@
#include <strsafe.h>
#include <versionhelpers.h>
#include "shell32_apitest_sub.h"
#include "closewnd.h"
#define NDEBUG
#include <debug.h>
@ -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);
}

View file

@ -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");
}

View file

@ -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()

View file

@ -10,18 +10,22 @@
#include <stdio.h>
#include <winbase.h>
#include <shlwapi.h>
#include "closewnd.h"
#define WAIT_SLEEP 700
// ShellExecuteW(handle, "open", <path_to_executable>, <parameters>, 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)

View file

@ -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);
}
}
}

View file

@ -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;
}