[SHELL32_APITEST] Make ShellExecuteEx testcase stable (#6617)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Wed, 13 Mar 2024 08:09:20 +0000 (17:09 +0900)
committerGitHub <noreply@github.com>
Wed, 13 Mar 2024 08:09:20 +0000 (17:09 +0900)
Making the tests trustworthy.
JIRA issue: ROSTESTS-389
- Enable command line checking.
- Rewrite the tests for better tests.
- Remove dependency with shell32_apitest_sub.
- Close the newly opened windows by Alt+F4 at ending.

modules/rostests/apitests/shell32/CMakeLists.txt
modules/rostests/apitests/shell32/ShellExecuteEx.cpp

index da5c512..642f398 100644 (file)
@@ -56,6 +56,7 @@ set_target_properties(shell32_apitest
 
 target_link_libraries(shell32_apitest wine uuid ${PSEH_LIB} cpprt atl_classes)
 set_module_type(shell32_apitest win32cui)
+target_compile_definitions(shell32_apitest PRIVATE UNICODE _UNICODE)
 add_importlibs(shell32_apitest user32 gdi32 shell32 shlwapi ole32 oleaut32 advapi32 shlwapi msvcrt kernel32 ntdll)
 add_pch(shell32_apitest shelltest.h "${PCH_SKIP_SOURCE}")
 add_rostests_file(TARGET shell32_apitest)
index 29f03be..30b9080 100644 (file)
  */
 
 #include "shelltest.h"
-#include <shlwapi.h>
+#include <pstypes.h>
+#include <psfuncs.h>
+#include <stdlib.h>
 #include <stdio.h>
-#include "shell32_apitest_sub.h"
-
-#define ok_ShellExecuteEx (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : TestShellExecuteEx
+#include <strsafe.h>
+#include <versionhelpers.h>
+
+static WCHAR s_win_dir[MAX_PATH];
+static WCHAR s_sys_dir[MAX_PATH];
+static WCHAR s_win_notepad[MAX_PATH];
+static WCHAR s_sys_notepad[MAX_PATH];
+static WCHAR s_win_test_exe[MAX_PATH];
+static WCHAR s_sys_test_exe[MAX_PATH];
+static WCHAR s_win_bat_file[MAX_PATH];
+static WCHAR s_sys_bat_file[MAX_PATH];
+static WCHAR s_win_txt_file[MAX_PATH];
+static WCHAR s_sys_txt_file[MAX_PATH];
+static WCHAR s_win_notepad_cmdline[MAX_PATH];
+static WCHAR s_sys_notepad_cmdline[MAX_PATH];
+static WCHAR s_win_test_exe_cmdline[MAX_PATH];
+static WCHAR s_sys_test_exe_cmdline[MAX_PATH];
+static BOOL s_bWow64;
+
+#define REG_APPPATHS L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"
+
+typedef enum TEST_RESULT
+{
+    TEST_FAILED,
+    TEST_SUCCESS_NO_PROCESS,
+    TEST_SUCCESS_WITH_PROCESS,
+} TEST_RESULT;
 
-static
-BOOL
-CreateAppPathRegKey(const WCHAR* Name)
+typedef struct TEST_ENTRY
 {
-    HKEY RegistryKey;
-    LONG Result;
-    WCHAR Buffer[1024];
-    WCHAR KeyValue[1024];
-    DWORD Length = sizeof(KeyValue);
-    DWORD Disposition;
-
-    wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\");
-    wcscat(Buffer, L"IEXPLORE.EXE");
-    Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, Buffer, 0, KEY_READ, &RegistryKey);
-    if (Result != ERROR_SUCCESS) trace("Could not open iexplore.exe key. Status: %lu\n", Result);
-    if (Result) goto end;
-    Result = RegQueryValueExW(RegistryKey, NULL, NULL, NULL, (LPBYTE)KeyValue, &Length);
-    if (Result != ERROR_SUCCESS) trace("Could not read iexplore.exe key. Status: %lu\n", Result);
-    if (Result) goto end;
-    RegCloseKey(RegistryKey);
-
-    wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\");
-    wcscat(Buffer, Name);
-    Result = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Buffer, 0, NULL,
-        0, KEY_WRITE, NULL, &RegistryKey, &Disposition);
-    if (Result != ERROR_SUCCESS) trace("Could not create test key. Status: %lu\n", Result);
-    if (Result) goto end;
-    Result = RegSetValueW(RegistryKey, NULL, REG_SZ, KeyValue, 0);
-    if (Result != ERROR_SUCCESS) trace("Could not set value of the test key. Status: %lu\n", Result);
-    if (Result) goto end;
-    RegCloseKey(RegistryKey);
-end:
-    if (RegistryKey) RegCloseKey(RegistryKey);
-    return Result == ERROR_SUCCESS;
-}
+    INT line;
+    TEST_RESULT result;
+    LPCWSTR lpFile;
+    LPCWSTR cmdline;
+} TEST_ENTRY, *PTEST_ENTRY;
+
+static void
+TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline = NULL);
 
-static
-VOID
-DeleteAppPathRegKey(const WCHAR* Name)
+static void TEST_DoTestEntries(void)
 {
-    LONG Result;
-    WCHAR Buffer[1024];
-    wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\");
-    wcscat(Buffer, Name);
-    Result = RegDeleteKeyW(HKEY_LOCAL_MACHINE, Buffer);
-    if (Result != ERROR_SUCCESS) trace("Could not remove the test key. Status: %lu\n", Result);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, NULL);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"");
+    TEST_DoTestEntry(__LINE__, TEST_FAILED, L"This is an invalid path.");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_bat_file, NULL);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_test_exe, s_sys_test_exe_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_txt_file, NULL);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_bat_file, NULL);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_notepad, s_win_notepad_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_test_exe, s_win_test_exe_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_txt_file, NULL);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad", s_sys_notepad_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad.exe", s_sys_notepad_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"notepad.exe\"", s_sys_notepad_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"notepad\"", s_sys_notepad_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"test program.exe", s_sys_test_exe_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"test program.exe\"", s_sys_test_exe_cmdline);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_win_dir);
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_sys_dir);
+    TEST_DoTestEntry(__LINE__, TEST_FAILED, L"shell:ThisIsAnInvalidName");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer (with shell:)
+
+    if (!IsWindowsVistaOrGreater())
+    {
+        WCHAR szCurDir[MAX_PATH];
+        GetCurrentDirectoryW(_countof(szCurDir), szCurDir);
+        SetCurrentDirectoryW(s_sys_dir);
+        TEST_DoTestEntry(__LINE__, TEST_FAILED, L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (without path)
+        SetCurrentDirectoryW(szCurDir);
+    }
+
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (with path)
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (with path and shell:)
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:AppData");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Desktop");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Programs");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Start Menu");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common StartUp");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:ControlPanelFolder");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Desktop");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Favorites");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Fonts");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Local AppData");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:My Pictures");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Personal");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Programs");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Recent");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:RecycleBinFolder");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:SendTo");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Start Menu");
+    TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:StartUp");
 }
 
-static
-VOID
-TestShellExecuteEx(const WCHAR* Name, BOOL ExpectedResult)
+static LPWSTR
+getCommandLineFromProcess(HANDLE hProcess)
 {
-    SHELLEXECUTEINFOW ShellExecInfo;
-    BOOL Result;
+    PEB peb;
+    PROCESS_BASIC_INFORMATION info;
+    RTL_USER_PROCESS_PARAMETERS Params;
 
-    ZeroMemory(&ShellExecInfo, sizeof(ShellExecInfo));
-    ShellExecInfo.cbSize = sizeof(ShellExecInfo);
-    ShellExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
-    ShellExecInfo.hwnd = NULL;
-    ShellExecInfo.nShow = SW_SHOWNORMAL;
-    ShellExecInfo.lpFile = Name;
-    ShellExecInfo.lpDirectory = NULL;
+    NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), NULL);
+    ReadProcessMemory(hProcess, info.PebBaseAddress, &peb, sizeof(peb), NULL);
+    ReadProcessMemory(hProcess, peb.ProcessParameters, &Params, sizeof(Params), NULL);
 
-    Result = ShellExecuteExW(&ShellExecInfo);
-    ok(Result == ExpectedResult, "ShellExecuteEx lpFile %s failed. Error: %lu\n", wine_dbgstr_w(Name), GetLastError());
-    if (ShellExecInfo.hProcess)
-    {
-        Result = TerminateProcess(ShellExecInfo.hProcess, 0);
-        if (!Result) trace("Terminate process failed. Error: %lu\n", GetLastError());
-        WaitForSingleObject(ShellExecInfo.hProcess, INFINITE);
-        CloseHandle(ShellExecInfo.hProcess);
-    }
+    LPWSTR cmdline = Params.CommandLine.Buffer;
+    SIZE_T cchCmdLine = Params.CommandLine.Length;
+    LPWSTR pszBuffer = (LPWSTR)calloc(cchCmdLine + 1, sizeof(WCHAR));
+    ReadProcessMemory(hProcess, cmdline, pszBuffer, cchCmdLine, NULL);
+    pszBuffer[cchCmdLine] = UNICODE_NULL;
+
+    return pszBuffer; // needs free()
 }
 
-static void DoAppPathTest(void)
+static void TEST_DoTestEntryStruct(const TEST_ENTRY *pEntry)
 {
-    ok_ShellExecuteEx(L"iexplore", TRUE);
-    ok_ShellExecuteEx(L"iexplore.exe", TRUE);
+    SHELLEXECUTEINFOW info = { sizeof(info) };
+    info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_WAITFORINPUTIDLE | SEE_MASK_FLAG_NO_UI;
+    info.hwnd = NULL;
+    info.lpVerb = NULL;
+    info.lpFile = pEntry->lpFile;
+    info.nShow = SW_SHOWNORMAL;
 
-    if (CreateAppPathRegKey(L"iexplore.bat"))
-    {
-        ok_ShellExecuteEx(L"iexplore.bat", TRUE);
-        ok_ShellExecuteEx(L"iexplore.bat.exe", FALSE);
-        DeleteAppPathRegKey(L"iexplore.bat");
-    }
+    BOOL ret = ShellExecuteExW(&info);
+
+    TEST_RESULT result;
+    if (ret && info.hProcess)
+        result = TEST_SUCCESS_WITH_PROCESS;
+    else if (ret && !info.hProcess)
+        result = TEST_SUCCESS_NO_PROCESS;
+    else
+        result = TEST_FAILED;
 
-    if (CreateAppPathRegKey(L"iexplore.bat.exe"))
+    ok(pEntry->result == result,
+       "Line %d: result: %d vs %d\n", pEntry->line, pEntry->result, result);
+
+    if (pEntry->result == TEST_SUCCESS_WITH_PROCESS && pEntry->cmdline && !s_bWow64)
     {
-        ok_ShellExecuteEx(L"iexplore.bat", FALSE);
-        ok_ShellExecuteEx(L"iexplore.bat.exe", TRUE);
-        DeleteAppPathRegKey(L"iexplore.bat.exe");
+        LPWSTR cmdline = getCommandLineFromProcess(info.hProcess);
+        if (!cmdline)
+        {
+            skip("!cmdline\n");
+        }
+        else
+        {
+            ok(lstrcmpiW(pEntry->cmdline, cmdline) == 0,
+               "Line %d: cmdline: '%ls' vs '%ls'\n", pEntry->line,
+               pEntry->cmdline, cmdline);
+        }
+
+        TerminateProcess(info.hProcess, 0xDEADFACE);
+        free(cmdline);
     }
+
+    CloseHandle(info.hProcess);
 }
 
-typedef struct TEST_ENTRY
-{
-    INT lineno;
-    BOOL ret;
-    BOOL bProcessHandle;
-    LPCSTR file;
-    LPCSTR params;
-    LPCSTR curdir;
-} TEST_ENTRY;
-
-static char s_sub_program[MAX_PATH];
-static char s_win_test_exe[MAX_PATH];
-static char s_sys_test_exe[MAX_PATH];
-static char s_win_bat_file[MAX_PATH];
-static char s_sys_bat_file[MAX_PATH];
-static char s_win_txt_file[MAX_PATH];
-static char s_sys_txt_file[MAX_PATH];
-
-#define DONT_CARE 0x0BADF00D
-
-static const TEST_ENTRY s_entries_1[] =
+static void
+TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline)
 {
-    { __LINE__, TRUE, TRUE, "test program" },
-    { __LINE__, TRUE, TRUE, "test program.bat" },
-    { __LINE__, TRUE, TRUE, "test program.exe" },
-    { __LINE__, FALSE, FALSE, "  test program" },
-    { __LINE__, FALSE, FALSE, "  test program.bat" },
-    { __LINE__, FALSE, FALSE, "  test program.exe" },
-    { __LINE__, FALSE, FALSE, "test program  " },
-    { __LINE__, TRUE, TRUE, "test program.bat  " },
-    { __LINE__, TRUE, TRUE, "test program.exe  " },
-    { __LINE__, TRUE, TRUE, "test program", "TEST" },
-    { __LINE__, TRUE, TRUE, "test program.bat", "TEST" },
-    { __LINE__, TRUE, TRUE, "test program.exe", "TEST" },
-    { __LINE__, FALSE, FALSE, ".\\test program.bat" },
-    { __LINE__, FALSE, FALSE, ".\\test program.exe" },
-    { __LINE__, TRUE, TRUE, "\"test program\"" },
-    { __LINE__, TRUE, TRUE, "\"test program.bat\"" },
-    { __LINE__, TRUE, TRUE, "\"test program.exe\"" },
-    { __LINE__, FALSE, FALSE, "\"test program\" TEST" },
-    { __LINE__, FALSE, FALSE, "\"test program.bat\" TEST" },
-    { __LINE__, FALSE, FALSE, "\"test program.exe\" TEST" },
-    { __LINE__, FALSE, FALSE, "  \"test program\"" },
-    { __LINE__, FALSE, FALSE, "  \"test program.bat\"" },
-    { __LINE__, FALSE, FALSE, "  \"test program.exe\"" },
-    { __LINE__, FALSE, FALSE, "\"test program\"  " },
-    { __LINE__, FALSE, FALSE, "\"test program.bat\"  " },
-    { __LINE__, FALSE, FALSE, "\"test program.exe\"  " },
-    { __LINE__, FALSE, FALSE, "\".\\test program.bat\"" },
-    { __LINE__, FALSE, FALSE, "\".\\test program.exe\"" },
-    { __LINE__, TRUE, TRUE, s_win_test_exe },
-    { __LINE__, TRUE, TRUE, s_sys_test_exe },
-    { __LINE__, TRUE, TRUE, s_win_bat_file },
-    { __LINE__, TRUE, TRUE, s_sys_bat_file },
-    { __LINE__, TRUE, TRUE, s_win_bat_file, "TEST" },
-    { __LINE__, TRUE, TRUE, s_sys_bat_file, "TEST" },
-    { __LINE__, FALSE, FALSE, "invalid program" },
-    { __LINE__, FALSE, FALSE, "invalid program.bat" },
-    { __LINE__, FALSE, FALSE, "invalid program.exe" },
-    { __LINE__, TRUE, TRUE, "test_file.txt" },
-    { __LINE__, TRUE, TRUE, "test_file.txt", "parameters parameters" },
-    { __LINE__, TRUE, TRUE, "test_file.txt", "parameters parameters", "." },
-    { __LINE__, TRUE, TRUE, "shell32_apitest_sub.exe" },
-    { __LINE__, TRUE, TRUE, ".\\shell32_apitest_sub.exe" },
-    { __LINE__, TRUE, TRUE, "\"shell32_apitest_sub.exe\"" },
-    { __LINE__, TRUE, TRUE, "\".\\shell32_apitest_sub.exe\"" },
-    { __LINE__, TRUE, DONT_CARE, "https://google.com" },
-    { __LINE__, TRUE, FALSE, "::{450d8fba-ad25-11d0-98a8-0800361b1103}" },
-    { __LINE__, TRUE, FALSE, "shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}" },
-    { __LINE__, TRUE, FALSE, "shell:sendto" },
-};
-
-static const TEST_ENTRY s_entries_2[] =
-{
-    { __LINE__, TRUE, TRUE, "test program" },
-    { __LINE__, TRUE, TRUE, "test program", "TEST" },
-    { __LINE__, TRUE, TRUE, "\"test program\"" },
-    { __LINE__, TRUE, TRUE, s_win_test_exe },
-    { __LINE__, TRUE, TRUE, s_sys_test_exe },
-    { __LINE__, FALSE, FALSE, s_win_bat_file },
-    { __LINE__, FALSE, FALSE, s_sys_bat_file },
-    { __LINE__, FALSE, FALSE, s_win_bat_file, "TEST" },
-    { __LINE__, FALSE, FALSE, s_sys_bat_file, "TEST" },
-};
-
-typedef struct OPENWNDS
+    TEST_ENTRY entry = { line, result, lpFile, cmdline };
+    TEST_DoTestEntryStruct(&entry);
+}
+
+static BOOL
+enableTokenPrivilege(LPCWSTR pszPrivilege)
 {
-    UINT count;
-    HWND *phwnd;
-} OPENWNDS;
+    HANDLE hToken;
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
+        return FALSE;
 
-static OPENWNDS s_wi0 = { 0 }, s_wi1 = { 0 };
+    TOKEN_PRIVILEGES tkp = { 0 };
+    if (!LookupPrivilegeValueW(NULL, pszPrivilege, &tkp.Privileges[0].Luid))
+        return FALSE;
+
+    tkp.PrivilegeCount = 1;
+    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+    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)
 {
-    OPENWNDS *info = (OPENWNDS *)lParam;
-    info->phwnd = (HWND *)realloc(info->phwnd, (info->count + 1) * sizeof(HWND));
-    if (!info->phwnd)
+    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;
-    info->phwnd[info->count] = hwnd;
-    ++(info->count);
+    phWnds[pList->m_chWnds++] = hwnd;
+    pList->m_phWnds = phWnds;
     return TRUE;
 }
 
-static void CleanupNewlyCreatedWindows(void)
+static inline void TEST_GetWindowList(PWINDOW_LIST pList)
 {
-    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));
+    EnumWindows(EnumWindowsProc, (LPARAM)pList);
 }
 
-static VOID DoTestEntry(const TEST_ENTRY *pEntry)
+static void TEST_CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2)
 {
-    SHELLEXECUTEINFOA info = { sizeof(info) };
-    info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
-    info.nShow = SW_SHOWNORMAL;
-    info.lpFile = pEntry->file;
-    info.lpParameters = pEntry->params;
-    info.lpDirectory = pEntry->curdir;
-    BOOL ret = ShellExecuteExA(&info);
-    ok(ret == pEntry->ret, "Line %u: ret expected %d, got %d\n",
-       pEntry->lineno, pEntry->ret, ret);
-    if (!pEntry->ret)
-        return;
-
-    if ((UINT)pEntry->bProcessHandle != DONT_CARE)
+    for (SIZE_T i2 = 0; i2 < List2->m_chWnds; ++i2)
     {
-        if (pEntry->bProcessHandle)
+        BOOL bFoundInList1 = FALSE;
+        HWND hWnd = List2->m_phWnds[i2];
+        for (SIZE_T i1 = 0; i1 < List1->m_chWnds; ++i1)
         {
-            ok(!!info.hProcess, "Line %u: hProcess expected non-NULL\n", pEntry->lineno);
+            if (hWnd == List1->m_phWnds[i1])
+            {
+                bFoundInList1 = TRUE;
+                goto Escape;
+            }
         }
-        else
+Escape:
+        if (!bFoundInList1)
         {
-            ok(!info.hProcess, "Line %u: hProcess expected NULL\n", pEntry->lineno);
-            return;
-        }
-    }
-
-    WaitForInputIdle(info.hProcess, INFINITE);
+            for (INT i = 0; i < 5; ++i)
+            {
+                if (!IsWindow(hWnd))
+                    break;
 
-    CleanupNewlyCreatedWindows();
+                SwitchToThisWindow(hWnd, TRUE);
 
-    if (WaitForSingleObject(info.hProcess, 10 * 1000) == WAIT_TIMEOUT)
-    {
-        TerminateProcess(info.hProcess, 11);
-        ok(0, "Process %s did not quit!\n", pEntry->file);
+                // 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);
+            }
+        }
     }
-    CloseHandle(info.hProcess);
 }
 
-static BOOL
-GetSubProgramPath(void)
+static WINDOW_LIST s_List1, s_List2;
+
+static BOOL TEST_Start(void)
 {
-    GetModuleFileNameA(NULL, s_sub_program, _countof(s_sub_program));
-    PathRemoveFileSpecA(s_sub_program);
-    PathAppendA(s_sub_program, "shell32_apitest_sub.exe");
+    // Check Wow64
+    s_bWow64 = FALSE;
+    IsWow64Process(GetCurrentProcess(), &s_bWow64);
+    if (s_bWow64)
+        skip("Wow64: Command Line check is skipped\n");
 
-    if (!PathFileExistsA(s_sub_program))
-    {
-        PathRemoveFileSpecA(s_sub_program);
-        PathAppendA(s_sub_program, "testdata\\shell32_apitest_sub.exe");
+    // getCommandLineFromProcess needs this
+    enableTokenPrivilege(SE_DEBUG_NAME);
 
-        if (!PathFileExistsA(s_sub_program))
-        {
-            return FALSE;
-        }
-    }
+    // s_win_dir
+    GetWindowsDirectoryW(s_win_dir, _countof(s_win_dir));
 
-    return TRUE;
-}
+    // s_sys_dir
+    GetSystemDirectoryW(s_sys_dir, _countof(s_sys_dir));
 
-static void DoTestEntries(void)
-{
-    if (!GetSubProgramPath())
-    {
-        skip("shell32_apitest_sub.exe is not found\n");
-        return;
-    }
+    // s_win_notepad
+    GetWindowsDirectoryW(s_win_notepad, _countof(s_win_notepad));
+    PathAppendW(s_win_notepad, L"notepad.exe");
+
+    // s_sys_notepad
+    GetSystemDirectoryW(s_sys_notepad, _countof(s_sys_notepad));
+    PathAppendW(s_sys_notepad, L"notepad.exe");
 
     // s_win_test_exe
-    GetWindowsDirectoryA(s_win_test_exe, _countof(s_win_test_exe));
-    PathAppendA(s_win_test_exe, "test program.exe");
-    BOOL ret = CopyFileA(s_sub_program, s_win_test_exe, FALSE);
+    GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe));
+    PathAppendW(s_win_test_exe, L"test program.exe");
+    BOOL ret = CopyFileW(s_win_notepad, s_win_test_exe, FALSE);
     if (!ret)
     {
         skip("Please retry with admin rights\n");
-        return;
-    }
-
-    // record open windows
-    if (!EnumWindows(EnumWindowsProc, (LPARAM)&s_wi0))
-    {
-        skip("EnumWindows failed\n");
-        DeleteFileA(s_win_test_exe);
-        free(s_wi0.phwnd);
-        return;
+        return FALSE;
     }
 
     // s_sys_test_exe
-    GetSystemDirectoryA(s_sys_test_exe, _countof(s_sys_test_exe));
-    PathAppendA(s_sys_test_exe, "test program.exe");
-    ok_int(CopyFileA(s_sub_program, s_sys_test_exe, FALSE), TRUE);
+    GetSystemDirectoryW(s_sys_test_exe, _countof(s_sys_test_exe));
+    PathAppendW(s_sys_test_exe, L"test program.exe");
+    ok_int(CopyFileW(s_win_notepad, s_sys_test_exe, FALSE), TRUE);
 
     // s_win_bat_file
-    GetWindowsDirectoryA(s_win_bat_file, _countof(s_win_bat_file));
-    PathAppendA(s_win_bat_file, "test program.bat");
-    FILE *fp = fopen(s_win_bat_file, "wb");
+    GetWindowsDirectoryW(s_win_bat_file, _countof(s_win_bat_file));
+    PathAppendW(s_win_bat_file, L"test program.bat");
+    FILE *fp = _wfopen(s_win_bat_file, L"wb");
     fprintf(fp, "exit /b 3");
     fclose(fp);
-    ok_int(PathFileExistsA(s_win_bat_file), TRUE);
+    ok_int(PathFileExistsW(s_win_bat_file), TRUE);
 
     // s_sys_bat_file
-    GetSystemDirectoryA(s_sys_bat_file, _countof(s_sys_bat_file));
-    PathAppendA(s_sys_bat_file, "test program.bat");
-    fp = fopen(s_sys_bat_file, "wb");
+    GetSystemDirectoryW(s_sys_bat_file, _countof(s_sys_bat_file));
+    PathAppendW(s_sys_bat_file, L"test program.bat");
+    fp = _wfopen(s_sys_bat_file, L"wb");
     fprintf(fp, "exit /b 4");
     fclose(fp);
-    ok_int(PathFileExistsA(s_sys_bat_file), TRUE);
+    ok_int(PathFileExistsW(s_sys_bat_file), TRUE);
 
     // s_win_txt_file
-    GetWindowsDirectoryA(s_win_txt_file, _countof(s_win_txt_file));
-    PathAppendA(s_win_txt_file, "test_file.txt");
-    fp = fopen(s_win_txt_file, "wb");
+    GetWindowsDirectoryW(s_win_txt_file, _countof(s_win_txt_file));
+    PathAppendW(s_win_txt_file, L"test_file.txt");
+    fp = _wfopen(s_win_txt_file, L"wb");
     fclose(fp);
-    ok_int(PathFileExistsA(s_win_txt_file), TRUE);
+    ok_int(PathFileExistsW(s_win_txt_file), TRUE);
 
     // s_sys_txt_file
-    GetSystemDirectoryA(s_sys_txt_file, _countof(s_sys_txt_file));
-    PathAppendA(s_sys_txt_file, "test_file.txt");
-    fp = fopen(s_sys_txt_file, "wb");
+    GetSystemDirectoryW(s_sys_txt_file, _countof(s_sys_txt_file));
+    PathAppendW(s_sys_txt_file, L"test_file.txt");
+    fp = _wfopen(s_sys_txt_file, L"wb");
     fclose(fp);
-    ok_int(PathFileExistsA(s_sys_txt_file), TRUE);
-
-    for (UINT iTest = 0; iTest < _countof(s_entries_1); ++iTest)
-    {
-        DoTestEntry(&s_entries_1[iTest]);
-    }
-
-    DeleteFileA(s_win_bat_file);
-    DeleteFileA(s_sys_bat_file);
+    ok_int(PathFileExistsW(s_sys_txt_file), TRUE);
 
-    for (UINT iTest = 0; iTest < _countof(s_entries_2); ++iTest)
+    // Check .txt settings
+    WCHAR szPath[MAX_PATH];
+    FindExecutableW(s_sys_txt_file, NULL, szPath);
+    if (lstrcmpiW(PathFindFileNameW(szPath), L"notepad.exe") != 0)
     {
-        DoTestEntry(&s_entries_2[iTest]);
+        skip("Please associate .txt with notepad.exe before tests\n");
+        return FALSE;
     }
 
-    DeleteFileA(s_win_test_exe);
-    DeleteFileA(s_sys_test_exe);
-    DeleteFileA(s_win_txt_file);
-    DeleteFileA(s_sys_txt_file);
-
-    free(s_wi0.phwnd);
-}
+    // command lines
+    StringCchPrintfW(s_win_notepad_cmdline, _countof(s_win_notepad_cmdline),
+                     L"\"%s\" ", s_win_notepad);
+    StringCchPrintfW(s_sys_notepad_cmdline, _countof(s_sys_notepad_cmdline),
+                     L"\"%s\" ", s_sys_notepad);
+    StringCchPrintfW(s_win_test_exe_cmdline, _countof(s_win_test_exe_cmdline),
+                     L"\"%s\" ", s_win_test_exe);
+    StringCchPrintfW(s_sys_test_exe_cmdline, _countof(s_sys_test_exe_cmdline),
+                     L"\"%s\" ", s_sys_test_exe);
 
-WCHAR* ExeName = NULL;
+    TEST_GetWindowList(&s_List1);
 
-BOOL CALLBACK EnumProc(_In_ HWND hwnd, _In_ LPARAM lParam)
-{
-    DWORD pid = 0;
-    GetWindowThreadProcessId(hwnd, &pid);
-    if (pid == GetCurrentProcessId() &&
-        IsWindowVisible(hwnd))
-    {
-        WCHAR Buffer[512] = {0};
-
-        GetWindowTextW(hwnd, Buffer, _countof(Buffer) - 1);
-        if (Buffer[0] && StrStrIW(Buffer, ExeName))
-        {
-            HWND* pHwnd = (HWND*)lParam;
-            *pHwnd = hwnd;
-            return FALSE;
-        }
-    }
     return TRUE;
 }
 
-BOOL WaitAndCloseWindow()
+static void TEST_End(void)
 {
-    HWND hWnd = NULL;
-    for (int n = 0; n < 100; ++n)
-    {
-        Sleep(50);
-
-        EnumWindows(EnumProc, (LPARAM)&hWnd);
-
-        if (hWnd)
-        {
-            SendMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
-            return TRUE;
-            break;
-        }
-    }
-    return FALSE;
+    Sleep(500);
+    TEST_GetWindowList(&s_List2);
+    TEST_CloseNewWindows(&s_List1, &s_List2);
+    free(s_List1.m_phWnds);
+    free(s_List2.m_phWnds);
+
+    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);
 }
 
 static void test_properties()
 {
     WCHAR Buffer[MAX_PATH * 4];
 
-    CoInitialize(NULL);
+    HRESULT hrCoInit = CoInitialize(NULL);
 
     GetModuleFileNameW(NULL, Buffer, _countof(Buffer));
-    SHELLEXECUTEINFOW info = { 0 };
+    SHELLEXECUTEINFOW info = { sizeof(info) };
 
     info.cbSize = sizeof(SHELLEXECUTEINFOW);
     info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI;
     info.lpVerb = L"properties";
     info.lpFile = Buffer;
-    info.lpParameters = L"";
     info.nShow = SW_SHOW;
 
     BOOL bRet = ShellExecuteExW(&info);
     ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError());
     ok_ptr(info.hInstApp, (HINSTANCE)42);
 
-    ExeName = PathFindFileNameW(Buffer);
     WCHAR* Extension = PathFindExtensionW(Buffer);
     if (Extension)
     {
@@ -444,80 +390,110 @@ static void test_properties()
         *Extension = UNICODE_NULL;
     }
 
-    if (bRet)
-    {
-        ok(WaitAndCloseWindow(), "Could not find properties window!\n");
-    }
-
     // Now retry it with the extension cut off
     bRet = ShellExecuteExW(&info);
     ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError());
     ok_ptr(info.hInstApp, (HINSTANCE)42);
 
-    if (bRet)
-    {
-        ok(WaitAndCloseWindow(), "Could not find properties window!\n");
-    }
-
-    info.lpFile = L"complete garbage, cannot run this!";
-
     // Now retry it with complete garabage
+    info.lpFile = L"complete garbage, cannot run this!";
     bRet = ShellExecuteExW(&info);
-    ok(bRet == 0, "Succeeded!\n");
+    ok_int(bRet, 0);
     ok_ptr(info.hInstApp, (HINSTANCE)2);
+
+    if (SUCCEEDED(hrCoInit))
+        CoUninitialize();
 }
 
 static void test_sei_lpIDList()
 {
+    if (IsWindowsVistaOrGreater())
+    {
+        skip("Vista+\n");
+        return;
+    }
+
     /* This tests ShellExecuteEx with lpIDList for explorer C:\ */
 
     /* ITEMIDLIST for CLSID of 'My Computer' followed by PIDL for 'C:\' */
     BYTE lpitemidlist[30] = { 0x14, 0, 0x1f, 0, 0xe0, 0x4f, 0xd0, 0x20, 0xea,
     0x3a, 0x69, 0x10, 0xa2, 0xd8, 0x08, 0, 0x2b, 0x30, 0x30, 0x9d, // My Computer
     0x8, 0, 0x23, 0x43, 0x3a, 0x5c, 0x5c, 0, 0, 0,}; // C:\\ + NUL-NUL ending
-    BYTE *lpBytes;
-    lpBytes = lpitemidlist;
-
-    SHELLEXECUTEINFOW ShellExecInfo;
-    BOOL Result;
-    STARTUPINFOW si;
-    PROCESS_INFORMATION pi;
-    HWND hWnd;
 
-    ZeroMemory( &si, sizeof(si) );
-    si.cb = sizeof(si);
-    ZeroMemory( &pi, sizeof(pi) );
-
-    ZeroMemory(&ShellExecInfo, sizeof(ShellExecInfo));
-    ShellExecInfo.cbSize = sizeof(ShellExecInfo);
+    SHELLEXECUTEINFOW ShellExecInfo = { sizeof(ShellExecInfo) };
     ShellExecInfo.fMask = SEE_MASK_IDLIST;
     ShellExecInfo.hwnd = NULL;
     ShellExecInfo.nShow = SW_SHOWNORMAL;
-    ShellExecInfo.lpFile = NULL;
-    ShellExecInfo.lpDirectory = NULL;
-    ShellExecInfo.lpIDList = lpBytes;
-
-    Result = ShellExecuteExW(&ShellExecInfo);
-    ok(Result == TRUE, "ShellExecuteEx lpIDList 'C:\\' failed\n");
-    trace("sei_lpIDList returned: %s\n", Result ? "SUCCESS" : "FAILURE");
-    if (Result)
+    ShellExecInfo.lpIDList = lpitemidlist;
+    BOOL ret = ShellExecuteExW(&ShellExecInfo);
+    ok_int(ret, TRUE);
+}
+
+static BOOL
+CreateAppPath(LPCWSTR pszName, LPCWSTR pszValue)
+{
+    WCHAR szSubKey[MAX_PATH];
+    StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName);
+
+    LSTATUS error;
+    HKEY hKey;
+    error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szSubKey, 0, NULL, 0, KEY_WRITE, NULL,
+                            &hKey, NULL);
+    if (error != ERROR_SUCCESS)
+        trace("Could not create test key (%lu)\n", error);
+
+    DWORD cbValue = (lstrlenW(pszValue) + 1) * sizeof(WCHAR);
+    error = RegSetValueExW(hKey, NULL, 0, REG_SZ, (LPBYTE)pszValue, cbValue);
+    if (error != ERROR_SUCCESS)
+        trace("Could not set value of the test key (%lu)\n", error);
+
+    RegCloseKey(hKey);
+
+    return error == ERROR_SUCCESS;
+}
+
+static VOID
+DeleteAppPath(LPCWSTR pszName)
+{
+    WCHAR szSubKey[MAX_PATH];
+    StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName);
+
+    LSTATUS error = RegDeleteKeyW(HKEY_LOCAL_MACHINE, szSubKey);
+    if (error != ERROR_SUCCESS)
+        trace("Could not remove the test key (%lu)\n", error);
+}
+
+static void TEST_AppPath(void)
+{
+    if (CreateAppPath(L"app_path_test.bat", s_win_test_exe))
+    {
+        TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat");
+        TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat.exe");
+        DeleteAppPath(L"app_path_test.bat");
+    }
+
+    if (CreateAppPath(L"app_path_test.bat.exe", s_sys_test_exe))
     {
-        Sleep(700);
-        // Terminate Window
-        hWnd = FindWindowW(L"CabinetWClass", L"Local Disk (C:)");
-        PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
+        TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat");
+        TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat.exe");
+        DeleteAppPath(L"app_path_test.bat.exe");
     }
 }
 
 START_TEST(ShellExecuteEx)
 {
-    DoAppPathTest();
-    DoTestEntries();
-    test_properties();
+#ifdef _WIN64
+    skip("Win64 is not supported yet\n");
+    return;
+#endif
 
-    DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE);
-    Sleep(100);
+    if (!TEST_Start())
+        return;
 
+    TEST_AppPath();
+    TEST_DoTestEntries();
+    test_properties();
     test_sei_lpIDList();
 
+    TEST_End();
 }