[SHELL32_APITEST] Make ShellExecuteEx testcase stable (#6617)

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.
This commit is contained in:
Katayama Hirofumi MZ 2024-03-13 17:09:20 +09:00 committed by GitHub
parent f132020d8b
commit ab4c0d6afd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 361 additions and 384 deletions

View file

@ -56,6 +56,7 @@ set_target_properties(shell32_apitest
target_link_libraries(shell32_apitest wine uuid ${PSEH_LIB} cpprt atl_classes) target_link_libraries(shell32_apitest wine uuid ${PSEH_LIB} cpprt atl_classes)
set_module_type(shell32_apitest win32cui) 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_importlibs(shell32_apitest user32 gdi32 shell32 shlwapi ole32 oleaut32 advapi32 shlwapi msvcrt kernel32 ntdll)
add_pch(shell32_apitest shelltest.h "${PCH_SKIP_SOURCE}") add_pch(shell32_apitest shelltest.h "${PCH_SKIP_SOURCE}")
add_rostests_file(TARGET shell32_apitest) add_rostests_file(TARGET shell32_apitest)

View file

@ -7,436 +7,382 @@
*/ */
#include "shelltest.h" #include "shelltest.h"
#include <shlwapi.h> #include <pstypes.h>
#include <psfuncs.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "shell32_apitest_sub.h" #include <strsafe.h>
#include <versionhelpers.h>
#define ok_ShellExecuteEx (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : TestShellExecuteEx 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;
static #define REG_APPPATHS L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"
BOOL
CreateAppPathRegKey(const WCHAR* Name) typedef enum TEST_RESULT
{ {
HKEY RegistryKey; TEST_FAILED,
LONG Result; TEST_SUCCESS_NO_PROCESS,
WCHAR Buffer[1024]; TEST_SUCCESS_WITH_PROCESS,
WCHAR KeyValue[1024]; } TEST_RESULT;
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;
}
static
VOID
DeleteAppPathRegKey(const WCHAR* Name)
{
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);
}
static
VOID
TestShellExecuteEx(const WCHAR* Name, BOOL ExpectedResult)
{
SHELLEXECUTEINFOW ShellExecInfo;
BOOL Result;
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;
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);
}
}
static void DoAppPathTest(void)
{
ok_ShellExecuteEx(L"iexplore", TRUE);
ok_ShellExecuteEx(L"iexplore.exe", TRUE);
if (CreateAppPathRegKey(L"iexplore.bat"))
{
ok_ShellExecuteEx(L"iexplore.bat", TRUE);
ok_ShellExecuteEx(L"iexplore.bat.exe", FALSE);
DeleteAppPathRegKey(L"iexplore.bat");
}
if (CreateAppPathRegKey(L"iexplore.bat.exe"))
{
ok_ShellExecuteEx(L"iexplore.bat", FALSE);
ok_ShellExecuteEx(L"iexplore.bat.exe", TRUE);
DeleteAppPathRegKey(L"iexplore.bat.exe");
}
}
typedef struct TEST_ENTRY typedef struct TEST_ENTRY
{ {
INT lineno; INT line;
BOOL ret; TEST_RESULT result;
BOOL bProcessHandle; LPCWSTR lpFile;
LPCSTR file; LPCWSTR cmdline;
LPCSTR params; } TEST_ENTRY, *PTEST_ENTRY;
LPCSTR curdir;
} TEST_ENTRY;
static char s_sub_program[MAX_PATH]; static void
static char s_win_test_exe[MAX_PATH]; TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline = NULL);
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 void TEST_DoTestEntries(void)
static const TEST_ENTRY s_entries_1[] =
{ {
{ __LINE__, TRUE, TRUE, "test program" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, NULL);
{ __LINE__, TRUE, TRUE, "test program.bat" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"");
{ __LINE__, TRUE, TRUE, "test program.exe" }, TEST_DoTestEntry(__LINE__, TEST_FAILED, L"This is an invalid path.");
{ __LINE__, FALSE, FALSE, " test program" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_bat_file, NULL);
{ __LINE__, FALSE, FALSE, " test program.bat" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_test_exe, s_sys_test_exe_cmdline);
{ __LINE__, FALSE, FALSE, " test program.exe" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_txt_file, NULL);
{ __LINE__, FALSE, FALSE, "test program " }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_bat_file, NULL);
{ __LINE__, TRUE, TRUE, "test program.bat " }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_notepad, s_win_notepad_cmdline);
{ __LINE__, TRUE, TRUE, "test program.exe " }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_test_exe, s_win_test_exe_cmdline);
{ __LINE__, TRUE, TRUE, "test program", "TEST" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_txt_file, NULL);
{ __LINE__, TRUE, TRUE, "test program.bat", "TEST" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad", s_sys_notepad_cmdline);
{ __LINE__, TRUE, TRUE, "test program.exe", "TEST" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad.exe", s_sys_notepad_cmdline);
{ __LINE__, FALSE, FALSE, ".\\test program.bat" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"notepad.exe\"", s_sys_notepad_cmdline);
{ __LINE__, FALSE, FALSE, ".\\test program.exe" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"notepad\"", s_sys_notepad_cmdline);
{ __LINE__, TRUE, TRUE, "\"test program\"" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"test program.exe", s_sys_test_exe_cmdline);
{ __LINE__, TRUE, TRUE, "\"test program.bat\"" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"test program.exe\"", s_sys_test_exe_cmdline);
{ __LINE__, TRUE, TRUE, "\"test program.exe\"" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_win_dir);
{ __LINE__, FALSE, FALSE, "\"test program\" TEST" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_sys_dir);
{ __LINE__, FALSE, FALSE, "\"test program.bat\" TEST" }, TEST_DoTestEntry(__LINE__, TEST_FAILED, L"shell:ThisIsAnInvalidName");
{ __LINE__, FALSE, FALSE, "\"test program.exe\" TEST" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer
{ __LINE__, FALSE, FALSE, " \"test program\"" }, TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer (with shell:)
{ __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[] = if (!IsWindowsVistaOrGreater())
{
{ __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
{
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; WCHAR szCurDir[MAX_PATH];
for (UINT i0 = 0; i0 < s_wi0.count; ++i0) GetCurrentDirectoryW(_countof(szCurDir), szCurDir);
{ SetCurrentDirectoryW(s_sys_dir);
if (s_wi1.phwnd[i1] == s_wi0.phwnd[i0]) TEST_DoTestEntry(__LINE__, TEST_FAILED, L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (without path)
{ SetCurrentDirectoryW(szCurDir);
bFound = TRUE;
break;
}
}
if (!bFound)
PostMessageW(s_wi1.phwnd[i1], WM_CLOSE, 0, 0);
} }
free(s_wi1.phwnd);
ZeroMemory(&s_wi1, sizeof(s_wi1)); 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 DoTestEntry(const TEST_ENTRY *pEntry) static LPWSTR
getCommandLineFromProcess(HANDLE hProcess)
{ {
SHELLEXECUTEINFOA info = { sizeof(info) }; PEB peb;
info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; PROCESS_BASIC_INFORMATION info;
info.nShow = SW_SHOWNORMAL; RTL_USER_PROCESS_PARAMETERS Params;
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) NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), NULL);
ReadProcessMemory(hProcess, info.PebBaseAddress, &peb, sizeof(peb), NULL);
ReadProcessMemory(hProcess, peb.ProcessParameters, &Params, sizeof(Params), NULL);
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 TEST_DoTestEntryStruct(const TEST_ENTRY *pEntry)
{
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;
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;
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)
{ {
if (pEntry->bProcessHandle) LPWSTR cmdline = getCommandLineFromProcess(info.hProcess);
if (!cmdline)
{ {
ok(!!info.hProcess, "Line %u: hProcess expected non-NULL\n", pEntry->lineno); skip("!cmdline\n");
} }
else else
{ {
ok(!info.hProcess, "Line %u: hProcess expected NULL\n", pEntry->lineno); ok(lstrcmpiW(pEntry->cmdline, cmdline) == 0,
return; "Line %d: cmdline: '%ls' vs '%ls'\n", pEntry->line,
pEntry->cmdline, cmdline);
} }
TerminateProcess(info.hProcess, 0xDEADFACE);
free(cmdline);
} }
WaitForInputIdle(info.hProcess, INFINITE);
CleanupNewlyCreatedWindows();
if (WaitForSingleObject(info.hProcess, 10 * 1000) == WAIT_TIMEOUT)
{
TerminateProcess(info.hProcess, 11);
ok(0, "Process %s did not quit!\n", pEntry->file);
}
CloseHandle(info.hProcess); CloseHandle(info.hProcess);
} }
static BOOL static void
GetSubProgramPath(void) TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline)
{ {
GetModuleFileNameA(NULL, s_sub_program, _countof(s_sub_program)); TEST_ENTRY entry = { line, result, lpFile, cmdline };
PathRemoveFileSpecA(s_sub_program); TEST_DoTestEntryStruct(&entry);
PathAppendA(s_sub_program, "shell32_apitest_sub.exe"); }
if (!PathFileExistsA(s_sub_program)) static BOOL
{ enableTokenPrivilege(LPCWSTR pszPrivilege)
PathRemoveFileSpecA(s_sub_program); {
PathAppendA(s_sub_program, "testdata\\shell32_apitest_sub.exe"); HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return FALSE;
if (!PathFileExistsA(s_sub_program)) TOKEN_PRIVILEGES tkp = { 0 };
{ if (!LookupPrivilegeValueW(NULL, pszPrivilege, &tkp.Privileges[0].Luid))
return FALSE; 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)
{
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; return TRUE;
} }
static void DoTestEntries(void) static inline void TEST_GetWindowList(PWINDOW_LIST pList)
{ {
if (!GetSubProgramPath()) EnumWindows(EnumWindowsProc, (LPARAM)pList);
}
static void TEST_CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2)
{
for (SIZE_T i2 = 0; i2 < List2->m_chWnds; ++i2)
{ {
skip("shell32_apitest_sub.exe is not found\n"); BOOL bFoundInList1 = FALSE;
return; 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)
{
// Check Wow64
s_bWow64 = FALSE;
IsWow64Process(GetCurrentProcess(), &s_bWow64);
if (s_bWow64)
skip("Wow64: Command Line check is skipped\n");
// getCommandLineFromProcess needs this
enableTokenPrivilege(SE_DEBUG_NAME);
// s_win_dir
GetWindowsDirectoryW(s_win_dir, _countof(s_win_dir));
// s_sys_dir
GetSystemDirectoryW(s_sys_dir, _countof(s_sys_dir));
// 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 // s_win_test_exe
GetWindowsDirectoryA(s_win_test_exe, _countof(s_win_test_exe)); GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe));
PathAppendA(s_win_test_exe, "test program.exe"); PathAppendW(s_win_test_exe, L"test program.exe");
BOOL ret = CopyFileA(s_sub_program, s_win_test_exe, FALSE); BOOL ret = CopyFileW(s_win_notepad, s_win_test_exe, FALSE);
if (!ret) if (!ret)
{ {
skip("Please retry with admin rights\n"); skip("Please retry with admin rights\n");
return; return FALSE;
}
// record open windows
if (!EnumWindows(EnumWindowsProc, (LPARAM)&s_wi0))
{
skip("EnumWindows failed\n");
DeleteFileA(s_win_test_exe);
free(s_wi0.phwnd);
return;
} }
// s_sys_test_exe // s_sys_test_exe
GetSystemDirectoryA(s_sys_test_exe, _countof(s_sys_test_exe)); GetSystemDirectoryW(s_sys_test_exe, _countof(s_sys_test_exe));
PathAppendA(s_sys_test_exe, "test program.exe"); PathAppendW(s_sys_test_exe, L"test program.exe");
ok_int(CopyFileA(s_sub_program, s_sys_test_exe, FALSE), TRUE); ok_int(CopyFileW(s_win_notepad, s_sys_test_exe, FALSE), TRUE);
// s_win_bat_file // s_win_bat_file
GetWindowsDirectoryA(s_win_bat_file, _countof(s_win_bat_file)); GetWindowsDirectoryW(s_win_bat_file, _countof(s_win_bat_file));
PathAppendA(s_win_bat_file, "test program.bat"); PathAppendW(s_win_bat_file, L"test program.bat");
FILE *fp = fopen(s_win_bat_file, "wb"); FILE *fp = _wfopen(s_win_bat_file, L"wb");
fprintf(fp, "exit /b 3"); fprintf(fp, "exit /b 3");
fclose(fp); fclose(fp);
ok_int(PathFileExistsA(s_win_bat_file), TRUE); ok_int(PathFileExistsW(s_win_bat_file), TRUE);
// s_sys_bat_file // s_sys_bat_file
GetSystemDirectoryA(s_sys_bat_file, _countof(s_sys_bat_file)); GetSystemDirectoryW(s_sys_bat_file, _countof(s_sys_bat_file));
PathAppendA(s_sys_bat_file, "test program.bat"); PathAppendW(s_sys_bat_file, L"test program.bat");
fp = fopen(s_sys_bat_file, "wb"); fp = _wfopen(s_sys_bat_file, L"wb");
fprintf(fp, "exit /b 4"); fprintf(fp, "exit /b 4");
fclose(fp); fclose(fp);
ok_int(PathFileExistsA(s_sys_bat_file), TRUE); ok_int(PathFileExistsW(s_sys_bat_file), TRUE);
// s_win_txt_file // s_win_txt_file
GetWindowsDirectoryA(s_win_txt_file, _countof(s_win_txt_file)); GetWindowsDirectoryW(s_win_txt_file, _countof(s_win_txt_file));
PathAppendA(s_win_txt_file, "test_file.txt"); PathAppendW(s_win_txt_file, L"test_file.txt");
fp = fopen(s_win_txt_file, "wb"); fp = _wfopen(s_win_txt_file, L"wb");
fclose(fp); fclose(fp);
ok_int(PathFileExistsA(s_win_txt_file), TRUE); ok_int(PathFileExistsW(s_win_txt_file), TRUE);
// s_sys_txt_file // s_sys_txt_file
GetSystemDirectoryA(s_sys_txt_file, _countof(s_sys_txt_file)); GetSystemDirectoryW(s_sys_txt_file, _countof(s_sys_txt_file));
PathAppendA(s_sys_txt_file, "test_file.txt"); PathAppendW(s_sys_txt_file, L"test_file.txt");
fp = fopen(s_sys_txt_file, "wb"); fp = _wfopen(s_sys_txt_file, L"wb");
fclose(fp); fclose(fp);
ok_int(PathFileExistsA(s_sys_txt_file), TRUE); ok_int(PathFileExistsW(s_sys_txt_file), TRUE);
for (UINT iTest = 0; iTest < _countof(s_entries_1); ++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_1[iTest]); skip("Please associate .txt with notepad.exe before tests\n");
return FALSE;
} }
DeleteFileA(s_win_bat_file); // command lines
DeleteFileA(s_sys_bat_file); 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);
for (UINT iTest = 0; iTest < _countof(s_entries_2); ++iTest) TEST_GetWindowList(&s_List1);
{
DoTestEntry(&s_entries_2[iTest]);
}
DeleteFileA(s_win_test_exe);
DeleteFileA(s_sys_test_exe);
DeleteFileA(s_win_txt_file);
DeleteFileA(s_sys_txt_file);
free(s_wi0.phwnd);
}
WCHAR* ExeName = NULL;
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; return TRUE;
} }
BOOL WaitAndCloseWindow() static void TEST_End(void)
{ {
HWND hWnd = NULL; Sleep(500);
for (int n = 0; n < 100; ++n) TEST_GetWindowList(&s_List2);
{ TEST_CloseNewWindows(&s_List1, &s_List2);
Sleep(50); free(s_List1.m_phWnds);
free(s_List2.m_phWnds);
EnumWindows(EnumProc, (LPARAM)&hWnd); DeleteFileW(s_win_test_exe);
DeleteFileW(s_sys_test_exe);
if (hWnd) DeleteFileW(s_win_txt_file);
{ DeleteFileW(s_sys_txt_file);
SendMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); DeleteFileW(s_win_bat_file);
return TRUE; DeleteFileW(s_sys_bat_file);
break;
}
}
return FALSE;
} }
static void test_properties() static void test_properties()
{ {
WCHAR Buffer[MAX_PATH * 4]; WCHAR Buffer[MAX_PATH * 4];
CoInitialize(NULL); HRESULT hrCoInit = CoInitialize(NULL);
GetModuleFileNameW(NULL, Buffer, _countof(Buffer)); GetModuleFileNameW(NULL, Buffer, _countof(Buffer));
SHELLEXECUTEINFOW info = { 0 }; SHELLEXECUTEINFOW info = { sizeof(info) };
info.cbSize = sizeof(SHELLEXECUTEINFOW); info.cbSize = sizeof(SHELLEXECUTEINFOW);
info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI; info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI;
info.lpVerb = L"properties"; info.lpVerb = L"properties";
info.lpFile = Buffer; info.lpFile = Buffer;
info.lpParameters = L"";
info.nShow = SW_SHOW; info.nShow = SW_SHOW;
BOOL bRet = ShellExecuteExW(&info); BOOL bRet = ShellExecuteExW(&info);
ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError());
ok_ptr(info.hInstApp, (HINSTANCE)42); ok_ptr(info.hInstApp, (HINSTANCE)42);
ExeName = PathFindFileNameW(Buffer);
WCHAR* Extension = PathFindExtensionW(Buffer); WCHAR* Extension = PathFindExtensionW(Buffer);
if (Extension) if (Extension)
{ {
@ -444,80 +390,110 @@ static void test_properties()
*Extension = UNICODE_NULL; *Extension = UNICODE_NULL;
} }
if (bRet)
{
ok(WaitAndCloseWindow(), "Could not find properties window!\n");
}
// Now retry it with the extension cut off // Now retry it with the extension cut off
bRet = ShellExecuteExW(&info); bRet = ShellExecuteExW(&info);
ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError());
ok_ptr(info.hInstApp, (HINSTANCE)42); 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 // Now retry it with complete garabage
info.lpFile = L"complete garbage, cannot run this!";
bRet = ShellExecuteExW(&info); bRet = ShellExecuteExW(&info);
ok(bRet == 0, "Succeeded!\n"); ok_int(bRet, 0);
ok_ptr(info.hInstApp, (HINSTANCE)2); ok_ptr(info.hInstApp, (HINSTANCE)2);
if (SUCCEEDED(hrCoInit))
CoUninitialize();
} }
static void test_sei_lpIDList() static void test_sei_lpIDList()
{ {
if (IsWindowsVistaOrGreater())
{
skip("Vista+\n");
return;
}
/* This tests ShellExecuteEx with lpIDList for explorer C:\ */ /* This tests ShellExecuteEx with lpIDList for explorer C:\ */
/* ITEMIDLIST for CLSID of 'My Computer' followed by PIDL for 'C:\' */ /* ITEMIDLIST for CLSID of 'My Computer' followed by PIDL for 'C:\' */
BYTE lpitemidlist[30] = { 0x14, 0, 0x1f, 0, 0xe0, 0x4f, 0xd0, 0x20, 0xea, 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 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 0x8, 0, 0x23, 0x43, 0x3a, 0x5c, 0x5c, 0, 0, 0,}; // C:\\ + NUL-NUL ending
BYTE *lpBytes;
lpBytes = lpitemidlist;
SHELLEXECUTEINFOW ShellExecInfo; SHELLEXECUTEINFOW ShellExecInfo = { sizeof(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);
ShellExecInfo.fMask = SEE_MASK_IDLIST; ShellExecInfo.fMask = SEE_MASK_IDLIST;
ShellExecInfo.hwnd = NULL; ShellExecInfo.hwnd = NULL;
ShellExecInfo.nShow = SW_SHOWNORMAL; ShellExecInfo.nShow = SW_SHOWNORMAL;
ShellExecInfo.lpFile = NULL; ShellExecInfo.lpIDList = lpitemidlist;
ShellExecInfo.lpDirectory = NULL; BOOL ret = ShellExecuteExW(&ShellExecInfo);
ShellExecInfo.lpIDList = lpBytes; ok_int(ret, TRUE);
}
Result = ShellExecuteExW(&ShellExecInfo); static BOOL
ok(Result == TRUE, "ShellExecuteEx lpIDList 'C:\\' failed\n"); CreateAppPath(LPCWSTR pszName, LPCWSTR pszValue)
trace("sei_lpIDList returned: %s\n", Result ? "SUCCESS" : "FAILURE"); {
if (Result) 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))
{ {
Sleep(700); TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat");
// Terminate Window TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat.exe");
hWnd = FindWindowW(L"CabinetWClass", L"Local Disk (C:)"); DeleteAppPath(L"app_path_test.bat");
PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); }
if (CreateAppPath(L"app_path_test.bat.exe", s_sys_test_exe))
{
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) START_TEST(ShellExecuteEx)
{ {
DoAppPathTest(); #ifdef _WIN64
DoTestEntries(); skip("Win64 is not supported yet\n");
return;
#endif
if (!TEST_Start())
return;
TEST_AppPath();
TEST_DoTestEntries();
test_properties(); test_properties();
DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE);
Sleep(100);
test_sei_lpIDList(); test_sei_lpIDList();
TEST_End();
} }