Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532/* * PROJECT: ReactOS api tests * LICENSE: GPLv2+ - See COPYING in the top level directory * PURPOSE: Testing ShellExecuteEx * PROGRAMMER: Yaroslav Veremenko <yaroslav@veremenko.info> * Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#include "shelltest.h"#include "closewnd.h"#include <pstypes.h>#include <psfuncs.h>#include <stdlib.h>#include <stdio.h>#include <strsafe.h>#include <versionhelpers.h>#include <shellutils.h>#include "shell32_apitest_sub.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;
typedef struct TEST_ENTRY{ INT line; TEST_RESULT result; LPCWSTR lpFile; LPCWSTR cmdline;} TEST_ENTRY, *PTEST_ENTRY;
static voidTEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline = NULL);
static void TEST_DoTestEntries(void){ 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 LPWSTRgetCommandLineFromProcess(HANDLE hProcess){ PEB peb; PROCESS_BASIC_INFORMATION info; RTL_USER_PROCESS_PARAMETERS Params; NTSTATUS Status; BOOL ret;
Status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), NULL); ok_ntstatus(Status, STATUS_SUCCESS);
ret = ReadProcessMemory(hProcess, info.PebBaseAddress, &peb, sizeof(peb), NULL); if (!ret) trace("ReadProcessMemory failed (%ld)\n", GetLastError());
ReadProcessMemory(hProcess, peb.ProcessParameters, &Params, sizeof(Params), NULL); if (!ret) trace("ReadProcessMemory failed (%ld)\n", GetLastError());
LPWSTR cmdline = Params.CommandLine.Buffer; if (!cmdline) trace("!cmdline\n");
SIZE_T cbCmdLine = Params.CommandLine.Length; if (!cbCmdLine) trace("!cbCmdLine\n");
LPWSTR pszBuffer = (LPWSTR)calloc(cbCmdLine + sizeof(WCHAR), 1); if (!pszBuffer) trace("!pszBuffer\n");
ret = ReadProcessMemory(hProcess, cmdline, pszBuffer, cbCmdLine, NULL); if (!ret) trace("ReadProcessMemory failed (%ld)\n", GetLastError());
pszBuffer[cbCmdLine / sizeof(WCHAR)] = UNICODE_NULL;
return pszBuffer; // needs free()}
static TEST_RESULT TEST_DoTestEntryStruct(const TEST_ENTRY *pEntry){ SHELLEXECUTEINFOW info = { sizeof(info) }; 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; 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 (result == TEST_SUCCESS_WITH_PROCESS) WaitForInputIdle(info.hProcess, 2000);
if (pEntry->result == TEST_SUCCESS_WITH_PROCESS && pEntry->cmdline && !s_bWow64) { 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); return result;}
static voidTEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline){ WINDOW_LIST existingwindows; GetWindowList(&existingwindows); HWND hWndForeground = GetForegroundWindow();
TEST_ENTRY entry = { line, result, lpFile, cmdline }; result = TEST_DoTestEntryStruct(&entry);
if (result == TEST_SUCCESS_NO_PROCESS) { // Wait a bit for Explorer to open its window for (UINT i = 0; i < 2000 && hWndForeground == GetForegroundWindow(); i += 250) Sleep(250); }
CloseNewWindows(&existingwindows); FreeWindowList(&existingwindows);}
static BOOLenableTokenPrivilege(LPCWSTR pszPrivilege){ HANDLE hToken; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return FALSE;
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);}
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 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 FALSE; }
// s_sys_test_exe 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 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(PathFileExistsW(s_win_bat_file), TRUE);
// s_sys_bat_file 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(PathFileExistsW(s_sys_bat_file), TRUE);
// s_win_txt_file 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(PathFileExistsW(s_win_txt_file), TRUE);
// s_sys_txt_file 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(PathFileExistsW(s_sys_txt_file), TRUE);
// Check .txt settings WCHAR szPath[MAX_PATH]; FindExecutableW(s_sys_txt_file, NULL, szPath); if (lstrcmpiW(PathFindFileNameW(szPath), L"notepad.exe") != 0) { skip("Please associate .txt with notepad.exe before tests\n"); return FALSE; }
// 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);
GetWindowList(&s_List1);
return TRUE;}
static void TEST_End(void){ 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(){ HRESULT hrCoInit = CoInitialize(NULL);
WCHAR Buffer[MAX_PATH * 4]; GetModuleFileNameW(NULL, Buffer, _countof(Buffer));
SHELLEXECUTEINFOW info = { sizeof(info) }; info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI; info.lpVerb = L"properties"; info.lpFile = Buffer; info.nShow = SW_SHOW;
BOOL bRet = ShellExecuteExW(&info); ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); ok_ptr(info.hInstApp, (HINSTANCE)42);
WCHAR* Extension = PathFindExtensionW(Buffer); if (Extension) { // The inclusion of this depends on the file display settings! *Extension = UNICODE_NULL; }
// 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);
// Now retry it with complete garabage info.lpFile = L"complete garbage, cannot run this!"; bRet = ShellExecuteExW(&info); ok_int(bRet, 0); ok_ptr(info.hInstApp, (HINSTANCE)2);
if (SUCCEEDED(hrCoInit)) CoUninitialize();}
static void test_sei_lpIDList(){ // Note: SEE_MASK_FLAG_NO_UI prevents the test from blocking with a MessageBox WCHAR path[MAX_PATH];
/* This tests ShellExecuteEx with lpIDList for explorer C:\ */ GetSystemDirectoryW(path, _countof(path)); PathStripToRootW(path); LPITEMIDLIST pidl = ILCreateFromPathW(path); if (!pidl) { skip("Unable to initialize test\n"); return; }
SHELLEXECUTEINFOW ShellExecInfo = { sizeof(ShellExecInfo) }; ShellExecInfo.nShow = SW_SHOWNORMAL; ShellExecInfo.fMask = SEE_MASK_IDLIST | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT; ShellExecInfo.lpIDList = pidl; BOOL ret = ShellExecuteExW(&ShellExecInfo); ok_int(ret, TRUE); ILFree(pidl);
/* This tests ShellExecuteEx with lpIDList going through IContextMenu */ CCoInit ComInit; pidl = SHCloneSpecialIDList(NULL, CSIDL_PROFILE, TRUE); if (!pidl) { skip("Unable to initialize test\n"); return; } ShellExecInfo.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT; ShellExecInfo.lpIDList = pidl; ret = ShellExecuteExW(&ShellExecInfo); ok_int(ret, TRUE); ILFree(pidl);}
static BOOLCreateAppPath(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 VOIDDeleteAppPath(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)) { 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"); }}
static void test_DoInvalidDir(void){ WCHAR szSubProgram[MAX_PATH]; if (!FindSubProgram(szSubProgram, _countof(szSubProgram))) { skip("shell32_apitest_sub.exe not found\n"); return; }
DWORD dwExitCode; SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS }; sei.lpFile = szSubProgram; sei.lpParameters = L"TEST"; sei.nShow = SW_SHOWNORMAL;
// Test invalid path on sei.lpDirectory WCHAR szInvalidPath[MAX_PATH] = L"M:\\This is an invalid path\n"; sei.lpDirectory = szInvalidPath; ok_int(ShellExecuteExW(&sei), TRUE); WaitForSingleObject(sei.hProcess, 20 * 1000); GetExitCodeProcess(sei.hProcess, &dwExitCode); ok_long(dwExitCode, 0); CloseHandle(sei.hProcess);}
START_TEST(ShellExecuteEx){#ifdef _WIN64 skip("Win64 is not supported yet\n"); return;#endif
if (!TEST_Start()) return;
TEST_AppPath(); TEST_DoTestEntries(); test_properties(); test_sei_lpIDList(); test_DoInvalidDir();
TEST_End();}