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 #include "shell32_apitest_sub.h" 13 14 #define ok_ShellExecuteEx (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : TestShellExecuteEx 15 16 static 17 BOOL 18 CreateAppPathRegKey(const WCHAR* Name) 19 { 20 HKEY RegistryKey; 21 LONG Result; 22 WCHAR Buffer[1024]; 23 WCHAR KeyValue[1024]; 24 DWORD Length = sizeof(KeyValue); 25 DWORD Disposition; 26 27 wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); 28 wcscat(Buffer, L"IEXPLORE.EXE"); 29 Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, Buffer, 0, KEY_READ, &RegistryKey); 30 if (Result != ERROR_SUCCESS) trace("Could not open iexplore.exe key. Status: %lu\n", Result); 31 if (Result) goto end; 32 Result = RegQueryValueExW(RegistryKey, NULL, NULL, NULL, (LPBYTE)KeyValue, &Length); 33 if (Result != ERROR_SUCCESS) trace("Could not read iexplore.exe key. Status: %lu\n", Result); 34 if (Result) goto end; 35 RegCloseKey(RegistryKey); 36 37 wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); 38 wcscat(Buffer, Name); 39 Result = RegCreateKeyExW(HKEY_LOCAL_MACHINE, Buffer, 0, NULL, 40 0, KEY_WRITE, NULL, &RegistryKey, &Disposition); 41 if (Result != ERROR_SUCCESS) trace("Could not create test key. Status: %lu\n", Result); 42 if (Result) goto end; 43 Result = RegSetValueW(RegistryKey, NULL, REG_SZ, KeyValue, 0); 44 if (Result != ERROR_SUCCESS) trace("Could not set value of the test key. Status: %lu\n", Result); 45 if (Result) goto end; 46 RegCloseKey(RegistryKey); 47 end: 48 if (RegistryKey) RegCloseKey(RegistryKey); 49 return Result == ERROR_SUCCESS; 50 } 51 52 static 53 VOID 54 DeleteAppPathRegKey(const WCHAR* Name) 55 { 56 LONG Result; 57 WCHAR Buffer[1024]; 58 wcscpy(Buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); 59 wcscat(Buffer, Name); 60 Result = RegDeleteKeyW(HKEY_LOCAL_MACHINE, Buffer); 61 if (Result != ERROR_SUCCESS) trace("Could not remove the test key. Status: %lu\n", Result); 62 } 63 64 static 65 VOID 66 TestShellExecuteEx(const WCHAR* Name, BOOL ExpectedResult) 67 { 68 SHELLEXECUTEINFOW ShellExecInfo; 69 BOOL Result; 70 71 ZeroMemory(&ShellExecInfo, sizeof(ShellExecInfo)); 72 ShellExecInfo.cbSize = sizeof(ShellExecInfo); 73 ShellExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; 74 ShellExecInfo.hwnd = NULL; 75 ShellExecInfo.nShow = SW_SHOWNORMAL; 76 ShellExecInfo.lpFile = Name; 77 ShellExecInfo.lpDirectory = NULL; 78 79 Result = ShellExecuteExW(&ShellExecInfo); 80 ok(Result == ExpectedResult, "ShellExecuteEx lpFile %s failed. Error: %lu\n", wine_dbgstr_w(Name), GetLastError()); 81 if (ShellExecInfo.hProcess) 82 { 83 Result = TerminateProcess(ShellExecInfo.hProcess, 0); 84 if (!Result) trace("Terminate process failed. Error: %lu\n", GetLastError()); 85 WaitForSingleObject(ShellExecInfo.hProcess, INFINITE); 86 CloseHandle(ShellExecInfo.hProcess); 87 } 88 } 89 90 static void DoAppPathTest(void) 91 { 92 ok_ShellExecuteEx(L"iexplore", TRUE); 93 ok_ShellExecuteEx(L"iexplore.exe", TRUE); 94 95 if (CreateAppPathRegKey(L"iexplore.bat")) 96 { 97 ok_ShellExecuteEx(L"iexplore.bat", TRUE); 98 ok_ShellExecuteEx(L"iexplore.bat.exe", FALSE); 99 DeleteAppPathRegKey(L"iexplore.bat"); 100 } 101 102 if (CreateAppPathRegKey(L"iexplore.bat.exe")) 103 { 104 ok_ShellExecuteEx(L"iexplore.bat", FALSE); 105 ok_ShellExecuteEx(L"iexplore.bat.exe", TRUE); 106 DeleteAppPathRegKey(L"iexplore.bat.exe"); 107 } 108 } 109 110 typedef struct TEST_ENTRY 111 { 112 INT lineno; 113 BOOL ret; 114 BOOL bProcessHandle; 115 LPCSTR file; 116 LPCSTR params; 117 LPCSTR curdir; 118 } TEST_ENTRY; 119 120 static char s_sub_program[MAX_PATH]; 121 static char s_win_test_exe[MAX_PATH]; 122 static char s_sys_test_exe[MAX_PATH]; 123 static char s_win_bat_file[MAX_PATH]; 124 static char s_sys_bat_file[MAX_PATH]; 125 static char s_win_txt_file[MAX_PATH]; 126 static char s_sys_txt_file[MAX_PATH]; 127 128 #define DONT_CARE 0x0BADF00D 129 130 static const TEST_ENTRY s_entries_1[] = 131 { 132 { __LINE__, TRUE, TRUE, "test program" }, 133 { __LINE__, TRUE, TRUE, "test program.bat" }, 134 { __LINE__, TRUE, TRUE, "test program.exe" }, 135 { __LINE__, FALSE, FALSE, " test program" }, 136 { __LINE__, FALSE, FALSE, " test program.bat" }, 137 { __LINE__, FALSE, FALSE, " test program.exe" }, 138 { __LINE__, FALSE, FALSE, "test program " }, 139 { __LINE__, TRUE, TRUE, "test program.bat " }, 140 { __LINE__, TRUE, TRUE, "test program.exe " }, 141 { __LINE__, TRUE, TRUE, "test program", "TEST" }, 142 { __LINE__, TRUE, TRUE, "test program.bat", "TEST" }, 143 { __LINE__, TRUE, TRUE, "test program.exe", "TEST" }, 144 { __LINE__, FALSE, FALSE, ".\\test program.bat" }, 145 { __LINE__, FALSE, FALSE, ".\\test program.exe" }, 146 { __LINE__, TRUE, TRUE, "\"test program\"" }, 147 { __LINE__, TRUE, TRUE, "\"test program.bat\"" }, 148 { __LINE__, TRUE, TRUE, "\"test program.exe\"" }, 149 { __LINE__, FALSE, FALSE, "\"test program\" TEST" }, 150 { __LINE__, FALSE, FALSE, "\"test program.bat\" TEST" }, 151 { __LINE__, FALSE, FALSE, "\"test program.exe\" TEST" }, 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\" " }, 156 { __LINE__, FALSE, FALSE, "\"test program.bat\" " }, 157 { __LINE__, FALSE, FALSE, "\"test program.exe\" " }, 158 { __LINE__, FALSE, FALSE, "\".\\test program.bat\"" }, 159 { __LINE__, FALSE, FALSE, "\".\\test program.exe\"" }, 160 { __LINE__, TRUE, TRUE, s_win_test_exe }, 161 { __LINE__, TRUE, TRUE, s_sys_test_exe }, 162 { __LINE__, TRUE, TRUE, s_win_bat_file }, 163 { __LINE__, TRUE, TRUE, s_sys_bat_file }, 164 { __LINE__, TRUE, TRUE, s_win_bat_file, "TEST" }, 165 { __LINE__, TRUE, TRUE, s_sys_bat_file, "TEST" }, 166 { __LINE__, FALSE, FALSE, "invalid program" }, 167 { __LINE__, FALSE, FALSE, "invalid program.bat" }, 168 { __LINE__, FALSE, FALSE, "invalid program.exe" }, 169 { __LINE__, TRUE, TRUE, "test_file.txt" }, 170 { __LINE__, TRUE, TRUE, "test_file.txt", "parameters parameters" }, 171 { __LINE__, TRUE, TRUE, "test_file.txt", "parameters parameters", "." }, 172 { __LINE__, TRUE, TRUE, "shell32_apitest_sub.exe" }, 173 { __LINE__, TRUE, TRUE, ".\\shell32_apitest_sub.exe" }, 174 { __LINE__, TRUE, TRUE, "\"shell32_apitest_sub.exe\"" }, 175 { __LINE__, TRUE, TRUE, "\".\\shell32_apitest_sub.exe\"" }, 176 { __LINE__, TRUE, DONT_CARE, "https://google.com" }, 177 { __LINE__, TRUE, FALSE, "::{450d8fba-ad25-11d0-98a8-0800361b1103}" }, 178 { __LINE__, TRUE, FALSE, "shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}" }, 179 { __LINE__, TRUE, FALSE, "shell:sendto" }, 180 }; 181 182 static const TEST_ENTRY s_entries_2[] = 183 { 184 { __LINE__, TRUE, TRUE, "test program" }, 185 { __LINE__, TRUE, TRUE, "test program", "TEST" }, 186 { __LINE__, TRUE, TRUE, "\"test program\"" }, 187 { __LINE__, TRUE, TRUE, s_win_test_exe }, 188 { __LINE__, TRUE, TRUE, s_sys_test_exe }, 189 { __LINE__, FALSE, FALSE, s_win_bat_file }, 190 { __LINE__, FALSE, FALSE, s_sys_bat_file }, 191 { __LINE__, FALSE, FALSE, s_win_bat_file, "TEST" }, 192 { __LINE__, FALSE, FALSE, s_sys_bat_file, "TEST" }, 193 }; 194 195 typedef struct OPENWNDS 196 { 197 UINT count; 198 HWND *phwnd; 199 } OPENWNDS; 200 201 static OPENWNDS s_wi0 = { 0 }, s_wi1 = { 0 }; 202 203 static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) 204 { 205 OPENWNDS *info = (OPENWNDS *)lParam; 206 info->phwnd = (HWND *)realloc(info->phwnd, (info->count + 1) * sizeof(HWND)); 207 if (!info->phwnd) 208 return FALSE; 209 info->phwnd[info->count] = hwnd; 210 ++(info->count); 211 return TRUE; 212 } 213 214 static void CleanupNewlyCreatedWindows(void) 215 { 216 EnumWindows(EnumWindowsProc, (LPARAM)&s_wi1); 217 for (UINT i1 = 0; i1 < s_wi1.count; ++i1) 218 { 219 BOOL bFound = FALSE; 220 for (UINT i0 = 0; i0 < s_wi0.count; ++i0) 221 { 222 if (s_wi1.phwnd[i1] == s_wi0.phwnd[i0]) 223 { 224 bFound = TRUE; 225 break; 226 } 227 } 228 if (!bFound) 229 PostMessageW(s_wi1.phwnd[i1], WM_CLOSE, 0, 0); 230 } 231 free(s_wi1.phwnd); 232 ZeroMemory(&s_wi1, sizeof(s_wi1)); 233 } 234 235 static VOID DoTestEntry(const TEST_ENTRY *pEntry) 236 { 237 SHELLEXECUTEINFOA info = { sizeof(info) }; 238 info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; 239 info.nShow = SW_SHOWNORMAL; 240 info.lpFile = pEntry->file; 241 info.lpParameters = pEntry->params; 242 info.lpDirectory = pEntry->curdir; 243 BOOL ret = ShellExecuteExA(&info); 244 ok(ret == pEntry->ret, "Line %u: ret expected %d, got %d\n", 245 pEntry->lineno, pEntry->ret, ret); 246 if (!pEntry->ret) 247 return; 248 249 if ((UINT)pEntry->bProcessHandle != DONT_CARE) 250 { 251 if (pEntry->bProcessHandle) 252 { 253 ok(!!info.hProcess, "Line %u: hProcess expected non-NULL\n", pEntry->lineno); 254 } 255 else 256 { 257 ok(!info.hProcess, "Line %u: hProcess expected NULL\n", pEntry->lineno); 258 return; 259 } 260 } 261 262 WaitForInputIdle(info.hProcess, INFINITE); 263 264 CleanupNewlyCreatedWindows(); 265 266 if (WaitForSingleObject(info.hProcess, 10 * 1000) == WAIT_TIMEOUT) 267 { 268 TerminateProcess(info.hProcess, 11); 269 ok(0, "Process %s did not quit!\n", pEntry->file); 270 } 271 CloseHandle(info.hProcess); 272 } 273 274 static BOOL 275 GetSubProgramPath(void) 276 { 277 GetModuleFileNameA(NULL, s_sub_program, _countof(s_sub_program)); 278 PathRemoveFileSpecA(s_sub_program); 279 PathAppendA(s_sub_program, "shell32_apitest_sub.exe"); 280 281 if (!PathFileExistsA(s_sub_program)) 282 { 283 PathRemoveFileSpecA(s_sub_program); 284 PathAppendA(s_sub_program, "testdata\\shell32_apitest_sub.exe"); 285 286 if (!PathFileExistsA(s_sub_program)) 287 { 288 return FALSE; 289 } 290 } 291 292 return TRUE; 293 } 294 295 static void DoTestEntries(void) 296 { 297 if (!GetSubProgramPath()) 298 { 299 skip("shell32_apitest_sub.exe is not found\n"); 300 return; 301 } 302 303 // s_win_test_exe 304 GetWindowsDirectoryA(s_win_test_exe, _countof(s_win_test_exe)); 305 PathAppendA(s_win_test_exe, "test program.exe"); 306 BOOL ret = CopyFileA(s_sub_program, s_win_test_exe, FALSE); 307 if (!ret) 308 { 309 skip("Please retry with admin rights\n"); 310 return; 311 } 312 313 // record open windows 314 if (!EnumWindows(EnumWindowsProc, (LPARAM)&s_wi0)) 315 { 316 skip("EnumWindows failed\n"); 317 DeleteFileA(s_win_test_exe); 318 free(s_wi0.phwnd); 319 return; 320 } 321 322 // s_sys_test_exe 323 GetSystemDirectoryA(s_sys_test_exe, _countof(s_sys_test_exe)); 324 PathAppendA(s_sys_test_exe, "test program.exe"); 325 ok_int(CopyFileA(s_sub_program, s_sys_test_exe, FALSE), TRUE); 326 327 // s_win_bat_file 328 GetWindowsDirectoryA(s_win_bat_file, _countof(s_win_bat_file)); 329 PathAppendA(s_win_bat_file, "test program.bat"); 330 FILE *fp = fopen(s_win_bat_file, "wb"); 331 fprintf(fp, "exit /b 3"); 332 fclose(fp); 333 ok_int(PathFileExistsA(s_win_bat_file), TRUE); 334 335 // s_sys_bat_file 336 GetSystemDirectoryA(s_sys_bat_file, _countof(s_sys_bat_file)); 337 PathAppendA(s_sys_bat_file, "test program.bat"); 338 fp = fopen(s_sys_bat_file, "wb"); 339 fprintf(fp, "exit /b 4"); 340 fclose(fp); 341 ok_int(PathFileExistsA(s_sys_bat_file), TRUE); 342 343 // s_win_txt_file 344 GetWindowsDirectoryA(s_win_txt_file, _countof(s_win_txt_file)); 345 PathAppendA(s_win_txt_file, "test_file.txt"); 346 fp = fopen(s_win_txt_file, "wb"); 347 fclose(fp); 348 ok_int(PathFileExistsA(s_win_txt_file), TRUE); 349 350 // s_sys_txt_file 351 GetSystemDirectoryA(s_sys_txt_file, _countof(s_sys_txt_file)); 352 PathAppendA(s_sys_txt_file, "test_file.txt"); 353 fp = fopen(s_sys_txt_file, "wb"); 354 fclose(fp); 355 ok_int(PathFileExistsA(s_sys_txt_file), TRUE); 356 357 for (UINT iTest = 0; iTest < _countof(s_entries_1); ++iTest) 358 { 359 DoTestEntry(&s_entries_1[iTest]); 360 } 361 362 DeleteFileA(s_win_bat_file); 363 DeleteFileA(s_sys_bat_file); 364 365 for (UINT iTest = 0; iTest < _countof(s_entries_2); ++iTest) 366 { 367 DoTestEntry(&s_entries_2[iTest]); 368 } 369 370 DeleteFileA(s_win_test_exe); 371 DeleteFileA(s_sys_test_exe); 372 DeleteFileA(s_win_txt_file); 373 DeleteFileA(s_sys_txt_file); 374 375 free(s_wi0.phwnd); 376 } 377 378 WCHAR* ExeName = NULL; 379 380 BOOL CALLBACK EnumProc(_In_ HWND hwnd, _In_ LPARAM lParam) 381 { 382 DWORD pid = 0; 383 GetWindowThreadProcessId(hwnd, &pid); 384 if (pid == GetCurrentProcessId() && 385 IsWindowVisible(hwnd)) 386 { 387 WCHAR Buffer[512] = {0}; 388 389 GetWindowTextW(hwnd, Buffer, _countof(Buffer) - 1); 390 if (Buffer[0] && StrStrIW(Buffer, ExeName)) 391 { 392 HWND* pHwnd = (HWND*)lParam; 393 *pHwnd = hwnd; 394 return FALSE; 395 } 396 } 397 return TRUE; 398 } 399 400 BOOL WaitAndCloseWindow() 401 { 402 HWND hWnd = NULL; 403 for (int n = 0; n < 100; ++n) 404 { 405 Sleep(50); 406 407 EnumWindows(EnumProc, (LPARAM)&hWnd); 408 409 if (hWnd) 410 { 411 SendMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); 412 return TRUE; 413 break; 414 } 415 } 416 return FALSE; 417 } 418 419 static void test_properties() 420 { 421 WCHAR Buffer[MAX_PATH * 4]; 422 423 CoInitialize(NULL); 424 425 GetModuleFileNameW(NULL, Buffer, _countof(Buffer)); 426 SHELLEXECUTEINFOW info = { 0 }; 427 428 info.cbSize = sizeof(SHELLEXECUTEINFOW); 429 info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI; 430 info.lpVerb = L"properties"; 431 info.lpFile = Buffer; 432 info.lpParameters = L""; 433 info.nShow = SW_SHOW; 434 435 BOOL bRet = ShellExecuteExW(&info); 436 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); 437 ok_ptr(info.hInstApp, (HINSTANCE)42); 438 439 ExeName = PathFindFileNameW(Buffer); 440 WCHAR* Extension = PathFindExtensionW(Buffer); 441 if (Extension) 442 { 443 // The inclusion of this depends on the file display settings! 444 *Extension = UNICODE_NULL; 445 } 446 447 if (bRet) 448 { 449 ok(WaitAndCloseWindow(), "Could not find properties window!\n"); 450 } 451 452 // Now retry it with the extension cut off 453 bRet = ShellExecuteExW(&info); 454 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); 455 ok_ptr(info.hInstApp, (HINSTANCE)42); 456 457 if (bRet) 458 { 459 ok(WaitAndCloseWindow(), "Could not find properties window!\n"); 460 } 461 462 info.lpFile = L"complete garbage, cannot run this!"; 463 464 // Now retry it with complete garabage 465 bRet = ShellExecuteExW(&info); 466 ok(bRet == 0, "Succeeded!\n"); 467 ok_ptr(info.hInstApp, (HINSTANCE)2); 468 } 469 470 static void test_sei_lpIDList() 471 { 472 /* This tests ShellExecuteEx with lpIDList for explorer C:\ */ 473 474 /* ITEMIDLIST for CLSID of 'My Computer' followed by PIDL for 'C:\' */ 475 BYTE lpitemidlist[30] = { 0x14, 0, 0x1f, 0, 0xe0, 0x4f, 0xd0, 0x20, 0xea, 476 0x3a, 0x69, 0x10, 0xa2, 0xd8, 0x08, 0, 0x2b, 0x30, 0x30, 0x9d, // My Computer 477 0x8, 0, 0x23, 0x43, 0x3a, 0x5c, 0x5c, 0, 0, 0,}; // C:\\ + NUL-NUL ending 478 BYTE *lpBytes; 479 lpBytes = lpitemidlist; 480 481 SHELLEXECUTEINFOW ShellExecInfo; 482 BOOL Result; 483 STARTUPINFOW si; 484 PROCESS_INFORMATION pi; 485 HWND hWnd; 486 487 ZeroMemory( &si, sizeof(si) ); 488 si.cb = sizeof(si); 489 ZeroMemory( &pi, sizeof(pi) ); 490 491 ZeroMemory(&ShellExecInfo, sizeof(ShellExecInfo)); 492 ShellExecInfo.cbSize = sizeof(ShellExecInfo); 493 ShellExecInfo.fMask = SEE_MASK_IDLIST; 494 ShellExecInfo.hwnd = NULL; 495 ShellExecInfo.nShow = SW_SHOWNORMAL; 496 ShellExecInfo.lpFile = NULL; 497 ShellExecInfo.lpDirectory = NULL; 498 ShellExecInfo.lpIDList = lpBytes; 499 500 Result = ShellExecuteExW(&ShellExecInfo); 501 ok(Result == TRUE, "ShellExecuteEx lpIDList 'C:\\' failed\n"); 502 trace("sei_lpIDList returned: %s\n", Result ? "SUCCESS" : "FAILURE"); 503 if (Result) 504 { 505 Sleep(700); 506 // Terminate Window 507 hWnd = FindWindowW(L"CabinetWClass", L"Local Disk (C:)"); 508 PostMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); 509 } 510 } 511 512 START_TEST(ShellExecuteEx) 513 { 514 DoAppPathTest(); 515 DoTestEntries(); 516 test_properties(); 517 518 DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE); 519 Sleep(100); 520 521 test_sei_lpIDList(); 522 523 } 524