1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: GPLv2+ - See COPYING in the top level directory 4 * PURPOSE: Testing ShellExecuteEx 5 * PROGRAMMER: Yaroslav Veremenko <yaroslav@veremenko.info> 6 * Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 7 */ 8 9 #include "shelltest.h" 10 #include <shlwapi.h> 11 #include <stdio.h> 12 13 #define ok_ShellExecuteEx (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : TestShellExecuteEx 14 15 static 16 BOOL 17 CreateAppPathRegKey(const WCHAR* Name) 18 { 19 HKEY RegistryKey; 20 LONG Result; 21 WCHAR Buffer[1024]; 22 WCHAR KeyValue[1024]; 23 DWORD Length = sizeof(KeyValue); 24 DWORD Disposition; 25 26 wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); 27 wcscat(Buffer, L"IEXPLORE.EXE"); 28 Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, Buffer, 0, KEY_READ, &RegistryKey); 29 if (Result != ERROR_SUCCESS) trace("Could not open iexplore.exe key. Status: %lu\n", Result); 30 if (Result) goto end; 31 Result = RegQueryValueExW(RegistryKey, NULL, NULL, NULL, (LPBYTE)KeyValue, &Length); 32 if (Result != ERROR_SUCCESS) trace("Could not read iexplore.exe key. Status: %lu\n", Result); 33 if (Result) goto end; 34 RegCloseKey(RegistryKey); 35 36 wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); 37 wcscat(Buffer, Name); 38 Result = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Buffer, 0, NULL, 39 0, KEY_WRITE, NULL, &RegistryKey, &Disposition); 40 if (Result != ERROR_SUCCESS) trace("Could not create test key. Status: %lu\n", Result); 41 if (Result) goto end; 42 Result = RegSetValueW(RegistryKey, NULL, REG_SZ, KeyValue, 0); 43 if (Result != ERROR_SUCCESS) trace("Could not set value of the test key. Status: %lu\n", Result); 44 if (Result) goto end; 45 RegCloseKey(RegistryKey); 46 end: 47 if (RegistryKey) RegCloseKey(RegistryKey); 48 return Result == ERROR_SUCCESS; 49 } 50 51 static 52 VOID 53 DeleteAppPathRegKey(const WCHAR* Name) 54 { 55 LONG Result; 56 WCHAR Buffer[1024]; 57 wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); 58 wcscat(Buffer, Name); 59 Result = RegDeleteKeyW(HKEY_LOCAL_MACHINE, Buffer); 60 if (Result != ERROR_SUCCESS) trace("Could not remove the test key. Status: %lu\n", Result); 61 } 62 63 static 64 VOID 65 TestShellExecuteEx(const WCHAR* Name, BOOL ExpectedResult) 66 { 67 SHELLEXECUTEINFOW ShellExecInfo; 68 BOOL Result; 69 ZeroMemory(&ShellExecInfo, sizeof(ShellExecInfo)); 70 ShellExecInfo.cbSize = sizeof(ShellExecInfo); 71 ShellExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; 72 ShellExecInfo.hwnd = NULL; 73 ShellExecInfo.nShow = SW_SHOWNORMAL; 74 ShellExecInfo.lpFile = Name; 75 ShellExecInfo.lpDirectory = NULL; 76 Result = ShellExecuteExW(&ShellExecInfo); 77 ok(Result == ExpectedResult, "ShellExecuteEx lpFile %s failed. Error: %lu\n", wine_dbgstr_w(Name), GetLastError()); 78 if (ShellExecInfo.hProcess) 79 { 80 Result = TerminateProcess(ShellExecInfo.hProcess, 0); 81 if (!Result) trace("Terminate process failed. Error: %lu\n", GetLastError()); 82 WaitForSingleObject(ShellExecInfo.hProcess, INFINITE); 83 CloseHandle(ShellExecInfo.hProcess); 84 } 85 } 86 87 static void DoAppPathTest(void) 88 { 89 ok_ShellExecuteEx(L"iexplore", TRUE); 90 ok_ShellExecuteEx(L"iexplore.exe", TRUE); 91 92 if (CreateAppPathRegKey(L"iexplore.bat")) 93 { 94 ok_ShellExecuteEx(L"iexplore.bat", TRUE); 95 ok_ShellExecuteEx(L"iexplore.bat.exe", FALSE); 96 DeleteAppPathRegKey(L"iexplore.bat"); 97 } 98 99 if (CreateAppPathRegKey(L"iexplore.bat.exe")) 100 { 101 ok_ShellExecuteEx(L"iexplore.bat", FALSE); 102 ok_ShellExecuteEx(L"iexplore.bat.exe", TRUE); 103 DeleteAppPathRegKey(L"iexplore.bat.exe"); 104 } 105 } 106 107 typedef struct TEST_ENTRY 108 { 109 INT lineno; 110 BOOL ret; 111 BOOL bProcessHandle; 112 LPCSTR file; 113 LPCSTR params; 114 LPCSTR curdir; 115 } TEST_ENTRY; 116 117 static char s_sub_program[MAX_PATH]; 118 static char s_win_test_exe[MAX_PATH]; 119 static char s_sys_test_exe[MAX_PATH]; 120 static char s_win_bat_file[MAX_PATH]; 121 static char s_sys_bat_file[MAX_PATH]; 122 static char s_win_txt_file[MAX_PATH]; 123 static char s_sys_txt_file[MAX_PATH]; 124 125 #define DONT_CARE 0x0BADF00D 126 127 static const TEST_ENTRY s_entries_1[] = 128 { 129 { __LINE__, TRUE, TRUE, "test program" }, 130 { __LINE__, TRUE, TRUE, "test program.bat" }, 131 { __LINE__, TRUE, TRUE, "test program.exe" }, 132 { __LINE__, FALSE, FALSE, " test program" }, 133 { __LINE__, FALSE, FALSE, " test program.bat" }, 134 { __LINE__, FALSE, FALSE, " test program.exe" }, 135 { __LINE__, FALSE, FALSE, "test program " }, 136 { __LINE__, TRUE, TRUE, "test program.bat " }, 137 { __LINE__, TRUE, TRUE, "test program.exe " }, 138 { __LINE__, TRUE, TRUE, "test program", "TEST" }, 139 { __LINE__, TRUE, TRUE, "test program.bat", "TEST" }, 140 { __LINE__, TRUE, TRUE, "test program.exe", "TEST" }, 141 { __LINE__, FALSE, FALSE, ".\\test program.bat" }, 142 { __LINE__, FALSE, FALSE, ".\\test program.exe" }, 143 { __LINE__, TRUE, TRUE, "\"test program\"" }, 144 { __LINE__, TRUE, TRUE, "\"test program.bat\"" }, 145 { __LINE__, TRUE, TRUE, "\"test program.exe\"" }, 146 { __LINE__, FALSE, FALSE, "\"test program\" TEST" }, 147 { __LINE__, FALSE, FALSE, "\"test program.bat\" TEST" }, 148 { __LINE__, FALSE, FALSE, "\"test program.exe\" TEST" }, 149 { __LINE__, FALSE, FALSE, " \"test program\"" }, 150 { __LINE__, FALSE, FALSE, " \"test program.bat\"" }, 151 { __LINE__, FALSE, FALSE, " \"test program.exe\"" }, 152 { __LINE__, FALSE, FALSE, "\"test program\" " }, 153 { __LINE__, FALSE, FALSE, "\"test program.bat\" " }, 154 { __LINE__, FALSE, FALSE, "\"test program.exe\" " }, 155 { __LINE__, FALSE, FALSE, "\".\\test program.bat\"" }, 156 { __LINE__, FALSE, FALSE, "\".\\test program.exe\"" }, 157 { __LINE__, TRUE, TRUE, s_win_test_exe }, 158 { __LINE__, TRUE, TRUE, s_sys_test_exe }, 159 { __LINE__, TRUE, TRUE, s_win_bat_file }, 160 { __LINE__, TRUE, TRUE, s_sys_bat_file }, 161 { __LINE__, TRUE, TRUE, s_win_bat_file, "TEST" }, 162 { __LINE__, TRUE, TRUE, s_sys_bat_file, "TEST" }, 163 { __LINE__, FALSE, FALSE, "invalid program" }, 164 { __LINE__, FALSE, FALSE, "invalid program.bat" }, 165 { __LINE__, FALSE, FALSE, "invalid program.exe" }, 166 { __LINE__, TRUE, TRUE, "test_file.txt" }, 167 { __LINE__, TRUE, TRUE, "test_file.txt", "parameters parameters" }, 168 { __LINE__, TRUE, TRUE, "test_file.txt", "parameters parameters", "." }, 169 { __LINE__, TRUE, TRUE, "shell32_apitest_sub.exe" }, 170 { __LINE__, TRUE, TRUE, ".\\shell32_apitest_sub.exe" }, 171 { __LINE__, TRUE, TRUE, "\"shell32_apitest_sub.exe\"" }, 172 { __LINE__, TRUE, TRUE, "\".\\shell32_apitest_sub.exe\"" }, 173 { __LINE__, TRUE, DONT_CARE, "https://google.com" }, 174 { __LINE__, TRUE, FALSE, "::{450d8fba-ad25-11d0-98a8-0800361b1103}" }, 175 { __LINE__, TRUE, FALSE, "shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}" }, 176 { __LINE__, TRUE, FALSE, "shell:sendto" }, 177 }; 178 179 static const TEST_ENTRY s_entries_2[] = 180 { 181 { __LINE__, TRUE, TRUE, "test program" }, 182 { __LINE__, TRUE, TRUE, "test program", "TEST" }, 183 { __LINE__, TRUE, TRUE, "\"test program\"" }, 184 { __LINE__, TRUE, TRUE, s_win_test_exe }, 185 { __LINE__, TRUE, TRUE, s_sys_test_exe }, 186 { __LINE__, FALSE, FALSE, s_win_bat_file }, 187 { __LINE__, FALSE, FALSE, s_sys_bat_file }, 188 { __LINE__, FALSE, FALSE, s_win_bat_file, "TEST" }, 189 { __LINE__, FALSE, FALSE, s_sys_bat_file, "TEST" }, 190 }; 191 192 typedef struct OPENWNDS 193 { 194 UINT count; 195 HWND *phwnd; 196 } OPENWNDS; 197 198 static OPENWNDS s_wi0 = { 0 }, s_wi1 = { 0 }; 199 200 static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) 201 { 202 OPENWNDS *info = (OPENWNDS *)lParam; 203 info->phwnd = (HWND *)realloc(info->phwnd, (info->count + 1) * sizeof(HWND)); 204 if (!info->phwnd) 205 return FALSE; 206 info->phwnd[info->count] = hwnd; 207 ++(info->count); 208 return TRUE; 209 } 210 211 static VOID DoTestEntry(const TEST_ENTRY *pEntry) 212 { 213 SHELLEXECUTEINFOA info = { sizeof(info) }; 214 info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; 215 info.nShow = SW_SHOWNORMAL; 216 info.lpFile = pEntry->file; 217 info.lpParameters = pEntry->params; 218 info.lpDirectory = pEntry->curdir; 219 BOOL ret = ShellExecuteExA(&info); 220 ok(ret == pEntry->ret, "Line %u: ret expected %d, got %d\n", 221 pEntry->lineno, pEntry->ret, ret); 222 if (!pEntry->ret) 223 return; 224 225 if ((UINT)pEntry->bProcessHandle != DONT_CARE) 226 { 227 if (pEntry->bProcessHandle) 228 { 229 ok(!!info.hProcess, "Line %u: hProcess expected non-NULL\n", pEntry->lineno); 230 } 231 else 232 { 233 ok(!info.hProcess, "Line %u: hProcess expected NULL\n", pEntry->lineno); 234 return; 235 } 236 } 237 238 WaitForInputIdle(info.hProcess, INFINITE); 239 240 // close newly opened windows 241 EnumWindows(EnumWindowsProc, (LPARAM)&s_wi1); 242 for (UINT i1 = 0; i1 < s_wi1.count; ++i1) 243 { 244 BOOL bFound = FALSE; 245 for (UINT i0 = 0; i0 < s_wi0.count; ++i0) 246 { 247 if (s_wi1.phwnd[i1] == s_wi0.phwnd[i0]) 248 { 249 bFound = TRUE; 250 break; 251 } 252 } 253 if (!bFound) 254 PostMessageW(s_wi1.phwnd[i1], WM_CLOSE, 0, 0); 255 } 256 free(s_wi1.phwnd); 257 ZeroMemory(&s_wi1, sizeof(s_wi1)); 258 259 WaitForSingleObject(info.hProcess, INFINITE); 260 CloseHandle(info.hProcess); 261 } 262 263 static BOOL 264 GetSubProgramPath(void) 265 { 266 GetModuleFileNameA(NULL, s_sub_program, _countof(s_sub_program)); 267 PathRemoveFileSpecA(s_sub_program); 268 PathAppendA(s_sub_program, "shell32_apitest_sub.exe"); 269 270 if (!PathFileExistsA(s_sub_program)) 271 { 272 PathRemoveFileSpecA(s_sub_program); 273 PathAppendA(s_sub_program, "testdata\\shell32_apitest_sub.exe"); 274 275 if (!PathFileExistsA(s_sub_program)) 276 { 277 return FALSE; 278 } 279 } 280 281 return TRUE; 282 } 283 284 static void DoTestEntries(void) 285 { 286 if (!GetSubProgramPath()) 287 { 288 skip("shell32_apitest_sub.exe is not found\n"); 289 return; 290 } 291 292 // s_win_test_exe 293 GetWindowsDirectoryA(s_win_test_exe, _countof(s_win_test_exe)); 294 PathAppendA(s_win_test_exe, "test program.exe"); 295 BOOL ret = CopyFileA(s_sub_program, s_win_test_exe, FALSE); 296 if (!ret) 297 { 298 skip("Please retry with admin rights\n"); 299 return; 300 } 301 302 // record open windows 303 if (!EnumWindows(EnumWindowsProc, (LPARAM)&s_wi0)) 304 { 305 skip("EnumWindows failed\n"); 306 DeleteFileA(s_win_test_exe); 307 free(s_wi0.phwnd); 308 return; 309 } 310 311 // s_sys_test_exe 312 GetSystemDirectoryA(s_sys_test_exe, _countof(s_sys_test_exe)); 313 PathAppendA(s_sys_test_exe, "test program.exe"); 314 ok_int(CopyFileA(s_sub_program, s_sys_test_exe, FALSE), TRUE); 315 316 // s_win_bat_file 317 GetWindowsDirectoryA(s_win_bat_file, _countof(s_win_bat_file)); 318 PathAppendA(s_win_bat_file, "test program.bat"); 319 FILE *fp = fopen(s_win_bat_file, "wb"); 320 fprintf(fp, "exit /b 3"); 321 fclose(fp); 322 ok_int(PathFileExistsA(s_win_bat_file), TRUE); 323 324 // s_sys_bat_file 325 GetSystemDirectoryA(s_sys_bat_file, _countof(s_sys_bat_file)); 326 PathAppendA(s_sys_bat_file, "test program.bat"); 327 fp = fopen(s_sys_bat_file, "wb"); 328 fprintf(fp, "exit /b 4"); 329 fclose(fp); 330 ok_int(PathFileExistsA(s_sys_bat_file), TRUE); 331 332 // s_win_txt_file 333 GetWindowsDirectoryA(s_win_txt_file, _countof(s_win_txt_file)); 334 PathAppendA(s_win_txt_file, "test_file.txt"); 335 fp = fopen(s_win_txt_file, "wb"); 336 fclose(fp); 337 ok_int(PathFileExistsA(s_win_txt_file), TRUE); 338 339 // s_sys_txt_file 340 GetSystemDirectoryA(s_sys_txt_file, _countof(s_sys_txt_file)); 341 PathAppendA(s_sys_txt_file, "test_file.txt"); 342 fp = fopen(s_sys_txt_file, "wb"); 343 fclose(fp); 344 ok_int(PathFileExistsA(s_sys_txt_file), TRUE); 345 346 for (UINT iTest = 0; iTest < _countof(s_entries_1); ++iTest) 347 { 348 DoTestEntry(&s_entries_1[iTest]); 349 } 350 351 DeleteFileA(s_win_bat_file); 352 DeleteFileA(s_sys_bat_file); 353 354 for (UINT iTest = 0; iTest < _countof(s_entries_2); ++iTest) 355 { 356 DoTestEntry(&s_entries_2[iTest]); 357 } 358 359 DeleteFileA(s_win_test_exe); 360 DeleteFileA(s_sys_test_exe); 361 DeleteFileA(s_win_txt_file); 362 DeleteFileA(s_sys_txt_file); 363 364 free(s_wi0.phwnd); 365 } 366 367 START_TEST(ShellExecuteEx) 368 { 369 DoAppPathTest(); 370 DoTestEntries(); 371 } 372