diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 642f398f9ec..62cbc898ee6 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -2,6 +2,7 @@ spec2def(shell32_apitest.exe shell32_apitest.spec) list(APPEND SOURCE + closewnd.cpp AddCommas.cpp CFSFolder.cpp CheckEscapes.cpp @@ -22,6 +23,7 @@ list(APPEND SOURCE PathIsEqualOrSubFolder.cpp PathIsTemporary.cpp PathResolve.cpp + RealShellExecuteEx.cpp SHAppBarMessage.cpp SHChangeNotify.cpp SHCreateDataObject.cpp diff --git a/modules/rostests/apitests/shell32/RealShellExecuteEx.cpp b/modules/rostests/apitests/shell32/RealShellExecuteEx.cpp new file mode 100644 index 00000000000..2f0552fd79b --- /dev/null +++ b/modules/rostests/apitests/shell32/RealShellExecuteEx.cpp @@ -0,0 +1,136 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Test for RealShellExecuteExA/W + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + */ + +#include "shelltest.h" +#include "closewnd.h" +#include + +BOOL IsWindowsServer2003SP2OrGreater(void) +{ + return IsWindowsVersionOrGreater(5, 2, 2); +} + +typedef HINSTANCE (WINAPI *FN_RealShellExecuteExA)( + _In_opt_ HWND hwnd, + _In_opt_ LPCSTR lpOperation, + _In_opt_ LPCSTR lpFile, + _In_opt_ LPCSTR lpParameters, + _In_opt_ LPCSTR lpDirectory, + _In_opt_ LPSTR lpReturn, + _In_opt_ LPCSTR lpTitle, + _In_opt_ LPVOID lpReserved, + _In_ INT nCmdShow, + _Out_opt_ PHANDLE lphProcess, + _In_ DWORD dwFlags); + +typedef HINSTANCE (WINAPI *FN_RealShellExecuteExW)( + _In_opt_ HWND hwnd, + _In_opt_ LPCWSTR lpOperation, + _In_opt_ LPCWSTR lpFile, + _In_opt_ LPCWSTR lpParameters, + _In_opt_ LPCWSTR lpDirectory, + _In_opt_ LPWSTR lpReturn, + _In_opt_ LPCWSTR lpTitle, + _In_opt_ LPVOID lpReserved, + _In_ INT nCmdShow, + _Out_opt_ PHANDLE lphProcess, + _In_ DWORD dwFlags); + +static HINSTANCE s_hSHELL32 = NULL; +static FN_RealShellExecuteExA s_fnRealShellExecuteExA = NULL; +static FN_RealShellExecuteExW s_fnRealShellExecuteExW = NULL; + +static WINDOW_LIST s_List1, s_List2; + +static void TEST_Start(void) +{ + GetWindowList(&s_List1); +} + +static void TEST_End(void) +{ + Sleep(500); + GetWindowList(&s_List2); + CloseNewWindows(&s_List1, &s_List2); + FreeWindowList(&s_List1); + FreeWindowList(&s_List2); +} + +static void TEST_RealShellExecuteExA(void) +{ + TEST_Start(); + + INT_PTR ret; + + ret = (INT_PTR)s_fnRealShellExecuteExA( + NULL, + NULL, + "notepad.exe", + NULL, + NULL, + NULL, + NULL, + NULL, + SW_SHOWDEFAULT, + NULL, + 0); + if (IsWindowsServer2003SP2OrGreater()) + 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( + NULL, + NULL, + L"notepad.exe", + NULL, + NULL, + NULL, + NULL, + NULL, + SW_SHOWDEFAULT, + NULL, + 0); + ok_long((LONG)ret, 42); + + TEST_End(); +} + +START_TEST(RealShellExecuteEx) +{ + if (IsWindowsVistaOrGreater()) + { + skip("Vista+\n"); + return; + } + + s_hSHELL32 = LoadLibraryW(L"shell32.dll"); + + s_fnRealShellExecuteExA = (FN_RealShellExecuteExA)GetProcAddress(s_hSHELL32, MAKEINTRESOURCEA(266)); + s_fnRealShellExecuteExW = (FN_RealShellExecuteExW)GetProcAddress(s_hSHELL32, MAKEINTRESOURCEA(267)); + + if (!s_fnRealShellExecuteExA || !s_fnRealShellExecuteExW) + { + skip("RealShellExecuteExA/W not found: %p, %p\n", + s_fnRealShellExecuteExA, s_fnRealShellExecuteExW); + return; + } + + TEST_RealShellExecuteExA(); + TEST_RealShellExecuteExW(); + + FreeLibrary(s_hSHELL32); +} diff --git a/modules/rostests/apitests/shell32/ShellExecuteEx.cpp b/modules/rostests/apitests/shell32/ShellExecuteEx.cpp index 0317c56fca2..6d742c3ded7 100644 --- a/modules/rostests/apitests/shell32/ShellExecuteEx.cpp +++ b/modules/rostests/apitests/shell32/ShellExecuteEx.cpp @@ -7,6 +7,7 @@ */ #include "shelltest.h" +#include "closewnd.h" #include #include #include @@ -149,7 +150,8 @@ getCommandLineFromProcess(HANDLE hProcess) static void TEST_DoTestEntryStruct(const TEST_ENTRY *pEntry) { SHELLEXECUTEINFOW info = { sizeof(info) }; - info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_WAITFORINPUTIDLE | SEE_MASK_FLAG_NO_UI; + info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_WAITFORINPUTIDLE | + SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC; info.hwnd = NULL; info.lpVerb = NULL; info.lpFile = pEntry->lpFile; @@ -212,67 +214,6 @@ enableTokenPrivilege(LPCWSTR pszPrivilege) return AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL); } -typedef struct WINDOW_LIST -{ - SIZE_T m_chWnds; - HWND *m_phWnds; -} WINDOW_LIST, *PWINDOW_LIST; - -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; -} - -static inline void TEST_GetWindowList(PWINDOW_LIST pList) -{ - EnumWindows(EnumWindowsProc, (LPARAM)pList); -} - -static void TEST_CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2) -{ - for (SIZE_T i2 = 0; i2 < List2->m_chWnds; ++i2) - { - BOOL bFoundInList1 = FALSE; - HWND hWnd = List2->m_phWnds[i2]; - for (SIZE_T i1 = 0; i1 < List1->m_chWnds; ++i1) - { - if (hWnd == List1->m_phWnds[i1]) - { - bFoundInList1 = TRUE; - goto Escape; - } - } -Escape: - if (!bFoundInList1) - { - for (INT i = 0; i < 5; ++i) - { - if (!IsWindow(hWnd)) - break; - - SwitchToThisWindow(hWnd, TRUE); - - // Alt+F4 - keybd_event(VK_MENU, 0x38, 0, 0); - keybd_event(VK_F4, 0x3E, 0, 0); - keybd_event(VK_F4, 0x3E, KEYEVENTF_KEYUP, 0); - keybd_event(VK_MENU, 0x38, KEYEVENTF_KEYUP, 0); - Sleep(100); - } - } - } -} - static WINDOW_LIST s_List1, s_List2; static BOOL TEST_Start(void) @@ -364,7 +305,7 @@ static BOOL TEST_Start(void) StringCchPrintfW(s_sys_test_exe_cmdline, _countof(s_sys_test_exe_cmdline), L"\"%s\" ", s_sys_test_exe); - TEST_GetWindowList(&s_List1); + GetWindowList(&s_List1); return TRUE; } @@ -372,10 +313,10 @@ static BOOL TEST_Start(void) static void TEST_End(void) { Sleep(500); - TEST_GetWindowList(&s_List2); - TEST_CloseNewWindows(&s_List1, &s_List2); - free(s_List1.m_phWnds); - free(s_List2.m_phWnds); + GetWindowList(&s_List2); + CloseNewWindows(&s_List1, &s_List2); + FreeWindowList(&s_List1); + FreeWindowList(&s_List2); DeleteFileW(s_win_test_exe); DeleteFileW(s_sys_test_exe); diff --git a/modules/rostests/apitests/shell32/closewnd.cpp b/modules/rostests/apitests/shell32/closewnd.cpp new file mode 100644 index 00000000000..d868f65fd79 --- /dev/null +++ b/modules/rostests/apitests/shell32/closewnd.cpp @@ -0,0 +1,90 @@ +/* + * 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); +} + +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; +} + +#define TRIALS_COUNT 8 + +void CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2) +{ + INT cDiff = List2->m_chWnds - List1->m_chWnds; + for (INT j = 0; j < cDiff; ++j) + { + HWND hWnd = FindNewWindow(List1, List2); + if (!hWnd) + break; + + for (INT i = 0; i < TRIALS_COUNT; ++i) + { + if (!IsWindow(hWnd)) + break; + + SwitchToThisWindow(hWnd, TRUE); + + // Alt+F4 + keybd_event(VK_MENU, 0x38, 0, 0); + keybd_event(VK_F4, 0x3E, 0, 0); + keybd_event(VK_F4, 0x3E, KEYEVENTF_KEYUP, 0); + keybd_event(VK_MENU, 0x38, KEYEVENTF_KEYUP, 0); + Sleep(100); + } + } +} diff --git a/modules/rostests/apitests/shell32/closewnd.h b/modules/rostests/apitests/shell32/closewnd.h new file mode 100644 index 00000000000..367c058b054 --- /dev/null +++ b/modules/rostests/apitests/shell32/closewnd.h @@ -0,0 +1,19 @@ +/* + * 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) + */ + +#pragma once + +typedef struct WINDOW_LIST +{ + SIZE_T m_chWnds; + HWND *m_phWnds; +} WINDOW_LIST, *PWINDOW_LIST; + +void GetWindowList(PWINDOW_LIST pList); +HWND FindNewWindow(PWINDOW_LIST List1, PWINDOW_LIST List2); +void CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2); +void FreeWindowList(PWINDOW_LIST pList); diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index 8e9d64cf5f6..e3a83c19106 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -24,6 +24,7 @@ extern void func_OpenAs_RunDLL(void); extern void func_PathIsEqualOrSubFolder(void); extern void func_PathIsTemporary(void); extern void func_PathResolve(void); +extern void func_RealShellExecuteEx(void); extern void func_SHAppBarMessage(void); extern void func_SHChangeNotify(void); extern void func_SHCreateDataObject(void); @@ -63,6 +64,7 @@ const struct test winetest_testlist[] = { "PathIsEqualOrSubFolder", func_PathIsEqualOrSubFolder }, { "PathIsTemporary", func_PathIsTemporary }, { "PathResolve", func_PathResolve }, + { "RealShellExecuteEx", func_RealShellExecuteEx }, { "SHAppBarMessage", func_SHAppBarMessage }, { "SHChangeNotify", func_SHChangeNotify }, { "SHCreateDataObject", func_SHCreateDataObject },