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 ZeroMemory(&ShellExecInfo, sizeof(ShellExecInfo)); 71 ShellExecInfo.cbSize = sizeof(ShellExecInfo); 72 ShellExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; 73 ShellExecInfo.hwnd = NULL; 74 ShellExecInfo.nShow = SW_SHOWNORMAL; 75 ShellExecInfo.lpFile = Name; 76 ShellExecInfo.lpDirectory = NULL; 77 Result = ShellExecuteExW(&ShellExecInfo); 78 ok(Result == ExpectedResult, "ShellExecuteEx lpFile %s failed. Error: %lu\n", wine_dbgstr_w(Name), GetLastError()); 79 if (ShellExecInfo.hProcess) 80 { 81 Result = TerminateProcess(ShellExecInfo.hProcess, 0); 82 if (!Result) trace("Terminate process failed. Error: %lu\n", GetLastError()); 83 WaitForSingleObject(ShellExecInfo.hProcess, INFINITE); 84 CloseHandle(ShellExecInfo.hProcess); 85 } 86 } 87 88 static void DoAppPathTest(void) 89 { 90 ok_ShellExecuteEx(L"iexplore", TRUE); 91 ok_ShellExecuteEx(L"iexplore.exe", TRUE); 92 93 if (CreateAppPathRegKey(L"iexplore.bat")) 94 { 95 ok_ShellExecuteEx(L"iexplore.bat", TRUE); 96 ok_ShellExecuteEx(L"iexplore.bat.exe", FALSE); 97 DeleteAppPathRegKey(L"iexplore.bat"); 98 } 99 100 if (CreateAppPathRegKey(L"iexplore.bat.exe")) 101 { 102 ok_ShellExecuteEx(L"iexplore.bat", FALSE); 103 ok_ShellExecuteEx(L"iexplore.bat.exe", TRUE); 104 DeleteAppPathRegKey(L"iexplore.bat.exe"); 105 } 106 } 107 108 typedef struct TEST_ENTRY 109 { 110 INT lineno; 111 BOOL ret; 112 BOOL bProcessHandle; 113 LPCSTR file; 114 LPCSTR params; 115 LPCSTR curdir; 116 } TEST_ENTRY; 117 118 static char s_sub_program[MAX_PATH]; 119 static char s_win_test_exe[MAX_PATH]; 120 static char s_sys_test_exe[MAX_PATH]; 121 static char s_win_bat_file[MAX_PATH]; 122 static char s_sys_bat_file[MAX_PATH]; 123 static char s_win_txt_file[MAX_PATH]; 124 static char s_sys_txt_file[MAX_PATH]; 125 126 #define DONT_CARE 0x0BADF00D 127 128 static const TEST_ENTRY s_entries_1[] = 129 { 130 { __LINE__, TRUE, TRUE, "test program" }, 131 { __LINE__, TRUE, TRUE, "test program.bat" }, 132 { __LINE__, TRUE, TRUE, "test program.exe" }, 133 { __LINE__, FALSE, FALSE, " test program" }, 134 { __LINE__, FALSE, FALSE, " test program.bat" }, 135 { __LINE__, FALSE, FALSE, " test program.exe" }, 136 { __LINE__, FALSE, FALSE, "test program " }, 137 { __LINE__, TRUE, TRUE, "test program.bat " }, 138 { __LINE__, TRUE, TRUE, "test program.exe " }, 139 { __LINE__, TRUE, TRUE, "test program", "TEST" }, 140 { __LINE__, TRUE, TRUE, "test program.bat", "TEST" }, 141 { __LINE__, TRUE, TRUE, "test program.exe", "TEST" }, 142 { __LINE__, FALSE, FALSE, ".\\test program.bat" }, 143 { __LINE__, FALSE, FALSE, ".\\test program.exe" }, 144 { __LINE__, TRUE, TRUE, "\"test program\"" }, 145 { __LINE__, TRUE, TRUE, "\"test program.bat\"" }, 146 { __LINE__, TRUE, TRUE, "\"test program.exe\"" }, 147 { __LINE__, FALSE, FALSE, "\"test program\" TEST" }, 148 { __LINE__, FALSE, FALSE, "\"test program.bat\" TEST" }, 149 { __LINE__, FALSE, FALSE, "\"test program.exe\" TEST" }, 150 { __LINE__, FALSE, FALSE, " \"test program\"" }, 151 { __LINE__, FALSE, FALSE, " \"test program.bat\"" }, 152 { __LINE__, FALSE, FALSE, " \"test program.exe\"" }, 153 { __LINE__, FALSE, FALSE, "\"test program\" " }, 154 { __LINE__, FALSE, FALSE, "\"test program.bat\" " }, 155 { __LINE__, FALSE, FALSE, "\"test program.exe\" " }, 156 { __LINE__, FALSE, FALSE, "\".\\test program.bat\"" }, 157 { __LINE__, FALSE, FALSE, "\".\\test program.exe\"" }, 158 { __LINE__, TRUE, TRUE, s_win_test_exe }, 159 { __LINE__, TRUE, TRUE, s_sys_test_exe }, 160 { __LINE__, TRUE, TRUE, s_win_bat_file }, 161 { __LINE__, TRUE, TRUE, s_sys_bat_file }, 162 { __LINE__, TRUE, TRUE, s_win_bat_file, "TEST" }, 163 { __LINE__, TRUE, TRUE, s_sys_bat_file, "TEST" }, 164 { __LINE__, FALSE, FALSE, "invalid program" }, 165 { __LINE__, FALSE, FALSE, "invalid program.bat" }, 166 { __LINE__, FALSE, FALSE, "invalid program.exe" }, 167 { __LINE__, TRUE, TRUE, "test_file.txt" }, 168 { __LINE__, TRUE, TRUE, "test_file.txt", "parameters parameters" }, 169 { __LINE__, TRUE, TRUE, "test_file.txt", "parameters parameters", "." }, 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, TRUE, "\".\\shell32_apitest_sub.exe\"" }, 174 { __LINE__, TRUE, DONT_CARE, "https://google.com" }, 175 { __LINE__, TRUE, FALSE, "::{450d8fba-ad25-11d0-98a8-0800361b1103}" }, 176 { __LINE__, TRUE, FALSE, "shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}" }, 177 { __LINE__, TRUE, FALSE, "shell:sendto" }, 178 }; 179 180 static const TEST_ENTRY s_entries_2[] = 181 { 182 { __LINE__, TRUE, TRUE, "test program" }, 183 { __LINE__, TRUE, TRUE, "test program", "TEST" }, 184 { __LINE__, TRUE, TRUE, "\"test program\"" }, 185 { __LINE__, TRUE, TRUE, s_win_test_exe }, 186 { __LINE__, TRUE, TRUE, s_sys_test_exe }, 187 { __LINE__, FALSE, FALSE, s_win_bat_file }, 188 { __LINE__, FALSE, FALSE, s_sys_bat_file }, 189 { __LINE__, FALSE, FALSE, s_win_bat_file, "TEST" }, 190 { __LINE__, FALSE, FALSE, s_sys_bat_file, "TEST" }, 191 }; 192 193 typedef struct OPENWNDS 194 { 195 UINT count; 196 HWND *phwnd; 197 } OPENWNDS; 198 199 static OPENWNDS s_wi0 = { 0 }, s_wi1 = { 0 }; 200 201 static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) 202 { 203 OPENWNDS *info = (OPENWNDS *)lParam; 204 info->phwnd = (HWND *)realloc(info->phwnd, (info->count + 1) * sizeof(HWND)); 205 if (!info->phwnd) 206 return FALSE; 207 info->phwnd[info->count] = hwnd; 208 ++(info->count); 209 return TRUE; 210 } 211 212 static VOID DoTestEntry(const TEST_ENTRY *pEntry) 213 { 214 SHELLEXECUTEINFOA info = { sizeof(info) }; 215 info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; 216 info.nShow = SW_SHOWNORMAL; 217 info.lpFile = pEntry->file; 218 info.lpParameters = pEntry->params; 219 info.lpDirectory = pEntry->curdir; 220 BOOL ret = ShellExecuteExA(&info); 221 ok(ret == pEntry->ret, "Line %u: ret expected %d, got %d\n", 222 pEntry->lineno, pEntry->ret, ret); 223 if (!pEntry->ret) 224 return; 225 226 if ((UINT)pEntry->bProcessHandle != DONT_CARE) 227 { 228 if (pEntry->bProcessHandle) 229 { 230 ok(!!info.hProcess, "Line %u: hProcess expected non-NULL\n", pEntry->lineno); 231 } 232 else 233 { 234 ok(!info.hProcess, "Line %u: hProcess expected NULL\n", pEntry->lineno); 235 return; 236 } 237 } 238 239 WaitForInputIdle(info.hProcess, INFINITE); 240 241 // close newly opened windows 242 EnumWindows(EnumWindowsProc, (LPARAM)&s_wi1); 243 for (UINT i1 = 0; i1 < s_wi1.count; ++i1) 244 { 245 BOOL bFound = FALSE; 246 for (UINT i0 = 0; i0 < s_wi0.count; ++i0) 247 { 248 if (s_wi1.phwnd[i1] == s_wi0.phwnd[i0]) 249 { 250 bFound = TRUE; 251 break; 252 } 253 } 254 if (!bFound) 255 PostMessageW(s_wi1.phwnd[i1], WM_CLOSE, 0, 0); 256 } 257 free(s_wi1.phwnd); 258 ZeroMemory(&s_wi1, sizeof(s_wi1)); 259 260 if (WaitForSingleObject(info.hProcess, 10 * 1000) == WAIT_TIMEOUT) 261 { 262 TerminateProcess(info.hProcess, 11); 263 ok(0, "Process %s did not quit!\n", pEntry->file); 264 } 265 CloseHandle(info.hProcess); 266 } 267 268 static BOOL 269 GetSubProgramPath(void) 270 { 271 GetModuleFileNameA(NULL, s_sub_program, _countof(s_sub_program)); 272 PathRemoveFileSpecA(s_sub_program); 273 PathAppendA(s_sub_program, "shell32_apitest_sub.exe"); 274 275 if (!PathFileExistsA(s_sub_program)) 276 { 277 PathRemoveFileSpecA(s_sub_program); 278 PathAppendA(s_sub_program, "testdata\\shell32_apitest_sub.exe"); 279 280 if (!PathFileExistsA(s_sub_program)) 281 { 282 return FALSE; 283 } 284 } 285 286 return TRUE; 287 } 288 289 static void DoTestEntries(void) 290 { 291 if (!GetSubProgramPath()) 292 { 293 skip("shell32_apitest_sub.exe is not found\n"); 294 return; 295 } 296 297 // s_win_test_exe 298 GetWindowsDirectoryA(s_win_test_exe, _countof(s_win_test_exe)); 299 PathAppendA(s_win_test_exe, "test program.exe"); 300 BOOL ret = CopyFileA(s_sub_program, s_win_test_exe, FALSE); 301 if (!ret) 302 { 303 skip("Please retry with admin rights\n"); 304 return; 305 } 306 307 // record open windows 308 if (!EnumWindows(EnumWindowsProc, (LPARAM)&s_wi0)) 309 { 310 skip("EnumWindows failed\n"); 311 DeleteFileA(s_win_test_exe); 312 free(s_wi0.phwnd); 313 return; 314 } 315 316 // s_sys_test_exe 317 GetSystemDirectoryA(s_sys_test_exe, _countof(s_sys_test_exe)); 318 PathAppendA(s_sys_test_exe, "test program.exe"); 319 ok_int(CopyFileA(s_sub_program, s_sys_test_exe, FALSE), TRUE); 320 321 // s_win_bat_file 322 GetWindowsDirectoryA(s_win_bat_file, _countof(s_win_bat_file)); 323 PathAppendA(s_win_bat_file, "test program.bat"); 324 FILE *fp = fopen(s_win_bat_file, "wb"); 325 fprintf(fp, "exit /b 3"); 326 fclose(fp); 327 ok_int(PathFileExistsA(s_win_bat_file), TRUE); 328 329 // s_sys_bat_file 330 GetSystemDirectoryA(s_sys_bat_file, _countof(s_sys_bat_file)); 331 PathAppendA(s_sys_bat_file, "test program.bat"); 332 fp = fopen(s_sys_bat_file, "wb"); 333 fprintf(fp, "exit /b 4"); 334 fclose(fp); 335 ok_int(PathFileExistsA(s_sys_bat_file), TRUE); 336 337 // s_win_txt_file 338 GetWindowsDirectoryA(s_win_txt_file, _countof(s_win_txt_file)); 339 PathAppendA(s_win_txt_file, "test_file.txt"); 340 fp = fopen(s_win_txt_file, "wb"); 341 fclose(fp); 342 ok_int(PathFileExistsA(s_win_txt_file), TRUE); 343 344 // s_sys_txt_file 345 GetSystemDirectoryA(s_sys_txt_file, _countof(s_sys_txt_file)); 346 PathAppendA(s_sys_txt_file, "test_file.txt"); 347 fp = fopen(s_sys_txt_file, "wb"); 348 fclose(fp); 349 ok_int(PathFileExistsA(s_sys_txt_file), TRUE); 350 351 for (UINT iTest = 0; iTest < _countof(s_entries_1); ++iTest) 352 { 353 DoTestEntry(&s_entries_1[iTest]); 354 } 355 356 DeleteFileA(s_win_bat_file); 357 DeleteFileA(s_sys_bat_file); 358 359 for (UINT iTest = 0; iTest < _countof(s_entries_2); ++iTest) 360 { 361 DoTestEntry(&s_entries_2[iTest]); 362 } 363 364 DeleteFileA(s_win_test_exe); 365 DeleteFileA(s_sys_test_exe); 366 DeleteFileA(s_win_txt_file); 367 DeleteFileA(s_sys_txt_file); 368 369 free(s_wi0.phwnd); 370 } 371 372 WCHAR* ExeName = NULL; 373 374 BOOL CALLBACK EnumProc(_In_ HWND hwnd, _In_ LPARAM lParam) 375 { 376 DWORD pid = 0; 377 GetWindowThreadProcessId(hwnd, &pid); 378 if (pid == GetCurrentProcessId() && 379 IsWindowVisible(hwnd)) 380 { 381 WCHAR Buffer[512] = {0}; 382 383 GetWindowTextW(hwnd, Buffer, _countof(Buffer) - 1); 384 if (Buffer[0] && StrStrIW(Buffer, ExeName)) 385 { 386 HWND* pHwnd = (HWND*)lParam; 387 *pHwnd = hwnd; 388 return FALSE; 389 } 390 } 391 return TRUE; 392 } 393 394 BOOL WaitAndCloseWindow() 395 { 396 HWND hWnd = NULL; 397 for (int n = 0; n < 100; ++n) 398 { 399 Sleep(50); 400 401 EnumWindows(EnumProc, (LPARAM)&hWnd); 402 403 if (hWnd) 404 { 405 SendMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); 406 return TRUE; 407 break; 408 } 409 } 410 return FALSE; 411 } 412 413 static void test_properties() 414 { 415 WCHAR Buffer[MAX_PATH * 4]; 416 417 CoInitialize(NULL); 418 419 GetModuleFileNameW(NULL, Buffer, _countof(Buffer)); 420 SHELLEXECUTEINFOW info = { 0 }; 421 422 info.cbSize = sizeof(SHELLEXECUTEINFOW); 423 info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI; 424 info.lpVerb = L"properties"; 425 info.lpFile = Buffer; 426 info.lpParameters = L""; 427 info.nShow = SW_SHOW; 428 429 BOOL bRet = ShellExecuteExW(&info); 430 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); 431 ok_ptr(info.hInstApp, (HINSTANCE)42); 432 433 ExeName = PathFindFileNameW(Buffer); 434 WCHAR* Extension = PathFindExtensionW(Buffer); 435 if (Extension) 436 { 437 // The inclusion of this depends on the file display settings! 438 *Extension = UNICODE_NULL; 439 } 440 441 if (bRet) 442 { 443 ok(WaitAndCloseWindow(), "Could not find properties window!\n"); 444 } 445 446 // Now retry it with the extension cut off 447 bRet = ShellExecuteExW(&info); 448 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); 449 ok_ptr(info.hInstApp, (HINSTANCE)42); 450 451 if (bRet) 452 { 453 ok(WaitAndCloseWindow(), "Could not find properties window!\n"); 454 } 455 456 info.lpFile = L"complete garbage, cannot run this!"; 457 458 // Now retry it with complete garabage 459 bRet = ShellExecuteExW(&info); 460 ok(bRet == 0, "Succeeded!\n"); 461 ok_ptr(info.hInstApp, (HINSTANCE)2); 462 } 463 464 START_TEST(ShellExecuteEx) 465 { 466 DoAppPathTest(); 467 DoTestEntries(); 468 test_properties(); 469 470 DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE); 471 Sleep(100); 472 } 473