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 "closewnd.h" 11 #include <pstypes.h> 12 #include <psfuncs.h> 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <strsafe.h> 16 #include <versionhelpers.h> 17 #include "shell32_apitest_sub.h" 18 19 static WCHAR s_win_dir[MAX_PATH]; 20 static WCHAR s_sys_dir[MAX_PATH]; 21 static WCHAR s_win_notepad[MAX_PATH]; 22 static WCHAR s_sys_notepad[MAX_PATH]; 23 static WCHAR s_win_test_exe[MAX_PATH]; 24 static WCHAR s_sys_test_exe[MAX_PATH]; 25 static WCHAR s_win_bat_file[MAX_PATH]; 26 static WCHAR s_sys_bat_file[MAX_PATH]; 27 static WCHAR s_win_txt_file[MAX_PATH]; 28 static WCHAR s_sys_txt_file[MAX_PATH]; 29 static WCHAR s_win_notepad_cmdline[MAX_PATH]; 30 static WCHAR s_sys_notepad_cmdline[MAX_PATH]; 31 static WCHAR s_win_test_exe_cmdline[MAX_PATH]; 32 static WCHAR s_sys_test_exe_cmdline[MAX_PATH]; 33 static BOOL s_bWow64; 34 35 #define REG_APPPATHS L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" 36 37 typedef enum TEST_RESULT 38 { 39 TEST_FAILED, 40 TEST_SUCCESS_NO_PROCESS, 41 TEST_SUCCESS_WITH_PROCESS, 42 } TEST_RESULT; 43 44 typedef struct TEST_ENTRY 45 { 46 INT line; 47 TEST_RESULT result; 48 LPCWSTR lpFile; 49 LPCWSTR cmdline; 50 } TEST_ENTRY, *PTEST_ENTRY; 51 52 static void 53 TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline = NULL); 54 55 static void TEST_DoTestEntries(void) 56 { 57 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, NULL); 58 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L""); 59 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"This is an invalid path."); 60 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_bat_file, NULL); 61 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_test_exe, s_sys_test_exe_cmdline); 62 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_txt_file, NULL); 63 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_bat_file, NULL); 64 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_notepad, s_win_notepad_cmdline); 65 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_test_exe, s_win_test_exe_cmdline); 66 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_txt_file, NULL); 67 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad", s_sys_notepad_cmdline); 68 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad.exe", s_sys_notepad_cmdline); 69 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"notepad.exe\"", s_sys_notepad_cmdline); 70 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"notepad\"", s_sys_notepad_cmdline); 71 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"test program.exe", s_sys_test_exe_cmdline); 72 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"test program.exe\"", s_sys_test_exe_cmdline); 73 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_win_dir); 74 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_sys_dir); 75 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"shell:ThisIsAnInvalidName"); 76 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer 77 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer (with shell:) 78 79 if (!IsWindowsVistaOrGreater()) 80 { 81 WCHAR szCurDir[MAX_PATH]; 82 GetCurrentDirectoryW(_countof(szCurDir), szCurDir); 83 SetCurrentDirectoryW(s_sys_dir); 84 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (without path) 85 SetCurrentDirectoryW(szCurDir); 86 } 87 88 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (with path) 89 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:) 90 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:AppData"); 91 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Desktop"); 92 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Programs"); 93 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Start Menu"); 94 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common StartUp"); 95 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:ControlPanelFolder"); 96 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Desktop"); 97 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Favorites"); 98 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Fonts"); 99 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Local AppData"); 100 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:My Pictures"); 101 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Personal"); 102 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Programs"); 103 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Recent"); 104 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:RecycleBinFolder"); 105 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:SendTo"); 106 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Start Menu"); 107 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:StartUp"); 108 } 109 110 static LPWSTR 111 getCommandLineFromProcess(HANDLE hProcess) 112 { 113 PEB peb; 114 PROCESS_BASIC_INFORMATION info; 115 RTL_USER_PROCESS_PARAMETERS Params; 116 NTSTATUS Status; 117 BOOL ret; 118 119 Status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), NULL); 120 ok_ntstatus(Status, STATUS_SUCCESS); 121 122 ret = ReadProcessMemory(hProcess, info.PebBaseAddress, &peb, sizeof(peb), NULL); 123 if (!ret) 124 trace("ReadProcessMemory failed (%ld)\n", GetLastError()); 125 126 ReadProcessMemory(hProcess, peb.ProcessParameters, &Params, sizeof(Params), NULL); 127 if (!ret) 128 trace("ReadProcessMemory failed (%ld)\n", GetLastError()); 129 130 LPWSTR cmdline = Params.CommandLine.Buffer; 131 if (!cmdline) 132 trace("!cmdline\n"); 133 134 SIZE_T cbCmdLine = Params.CommandLine.Length; 135 if (!cbCmdLine) 136 trace("!cbCmdLine\n"); 137 138 LPWSTR pszBuffer = (LPWSTR)calloc(cbCmdLine + sizeof(WCHAR), 1); 139 if (!pszBuffer) 140 trace("!pszBuffer\n"); 141 142 ret = ReadProcessMemory(hProcess, cmdline, pszBuffer, cbCmdLine, NULL); 143 if (!ret) 144 trace("ReadProcessMemory failed (%ld)\n", GetLastError()); 145 146 pszBuffer[cbCmdLine / sizeof(WCHAR)] = UNICODE_NULL; 147 148 return pszBuffer; // needs free() 149 } 150 151 static void TEST_DoTestEntryStruct(const TEST_ENTRY *pEntry) 152 { 153 SHELLEXECUTEINFOW info = { sizeof(info) }; 154 info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_WAITFORINPUTIDLE | 155 SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC; 156 info.hwnd = NULL; 157 info.lpVerb = NULL; 158 info.lpFile = pEntry->lpFile; 159 info.nShow = SW_SHOWNORMAL; 160 161 BOOL ret = ShellExecuteExW(&info); 162 163 TEST_RESULT result; 164 if (ret && info.hProcess) 165 result = TEST_SUCCESS_WITH_PROCESS; 166 else if (ret && !info.hProcess) 167 result = TEST_SUCCESS_NO_PROCESS; 168 else 169 result = TEST_FAILED; 170 171 ok(pEntry->result == result, 172 "Line %d: result: %d vs %d\n", pEntry->line, pEntry->result, result); 173 174 if (pEntry->result == TEST_SUCCESS_WITH_PROCESS && pEntry->cmdline && !s_bWow64) 175 { 176 LPWSTR cmdline = getCommandLineFromProcess(info.hProcess); 177 if (!cmdline) 178 { 179 skip("!cmdline\n"); 180 } 181 else 182 { 183 ok(lstrcmpiW(pEntry->cmdline, cmdline) == 0, 184 "Line %d: cmdline: '%ls' vs '%ls'\n", pEntry->line, 185 pEntry->cmdline, cmdline); 186 } 187 188 TerminateProcess(info.hProcess, 0xDEADFACE); 189 free(cmdline); 190 } 191 192 CloseHandle(info.hProcess); 193 } 194 195 static void 196 TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline) 197 { 198 TEST_ENTRY entry = { line, result, lpFile, cmdline }; 199 TEST_DoTestEntryStruct(&entry); 200 } 201 202 static BOOL 203 enableTokenPrivilege(LPCWSTR pszPrivilege) 204 { 205 HANDLE hToken; 206 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 207 return FALSE; 208 209 TOKEN_PRIVILEGES tkp = { 0 }; 210 if (!LookupPrivilegeValueW(NULL, pszPrivilege, &tkp.Privileges[0].Luid)) 211 return FALSE; 212 213 tkp.PrivilegeCount = 1; 214 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 215 return AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL); 216 } 217 218 static WINDOW_LIST s_List1, s_List2; 219 220 static BOOL TEST_Start(void) 221 { 222 // Check Wow64 223 s_bWow64 = FALSE; 224 IsWow64Process(GetCurrentProcess(), &s_bWow64); 225 if (s_bWow64) 226 skip("Wow64: Command Line check is skipped\n"); 227 228 // getCommandLineFromProcess needs this 229 enableTokenPrivilege(SE_DEBUG_NAME); 230 231 // s_win_dir 232 GetWindowsDirectoryW(s_win_dir, _countof(s_win_dir)); 233 234 // s_sys_dir 235 GetSystemDirectoryW(s_sys_dir, _countof(s_sys_dir)); 236 237 // s_win_notepad 238 GetWindowsDirectoryW(s_win_notepad, _countof(s_win_notepad)); 239 PathAppendW(s_win_notepad, L"notepad.exe"); 240 241 // s_sys_notepad 242 GetSystemDirectoryW(s_sys_notepad, _countof(s_sys_notepad)); 243 PathAppendW(s_sys_notepad, L"notepad.exe"); 244 245 // s_win_test_exe 246 GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe)); 247 PathAppendW(s_win_test_exe, L"test program.exe"); 248 BOOL ret = CopyFileW(s_win_notepad, s_win_test_exe, FALSE); 249 if (!ret) 250 { 251 skip("Please retry with admin rights\n"); 252 return FALSE; 253 } 254 255 // s_sys_test_exe 256 GetSystemDirectoryW(s_sys_test_exe, _countof(s_sys_test_exe)); 257 PathAppendW(s_sys_test_exe, L"test program.exe"); 258 ok_int(CopyFileW(s_win_notepad, s_sys_test_exe, FALSE), TRUE); 259 260 // s_win_bat_file 261 GetWindowsDirectoryW(s_win_bat_file, _countof(s_win_bat_file)); 262 PathAppendW(s_win_bat_file, L"test program.bat"); 263 FILE *fp = _wfopen(s_win_bat_file, L"wb"); 264 fprintf(fp, "exit /b 3"); 265 fclose(fp); 266 ok_int(PathFileExistsW(s_win_bat_file), TRUE); 267 268 // s_sys_bat_file 269 GetSystemDirectoryW(s_sys_bat_file, _countof(s_sys_bat_file)); 270 PathAppendW(s_sys_bat_file, L"test program.bat"); 271 fp = _wfopen(s_sys_bat_file, L"wb"); 272 fprintf(fp, "exit /b 4"); 273 fclose(fp); 274 ok_int(PathFileExistsW(s_sys_bat_file), TRUE); 275 276 // s_win_txt_file 277 GetWindowsDirectoryW(s_win_txt_file, _countof(s_win_txt_file)); 278 PathAppendW(s_win_txt_file, L"test_file.txt"); 279 fp = _wfopen(s_win_txt_file, L"wb"); 280 fclose(fp); 281 ok_int(PathFileExistsW(s_win_txt_file), TRUE); 282 283 // s_sys_txt_file 284 GetSystemDirectoryW(s_sys_txt_file, _countof(s_sys_txt_file)); 285 PathAppendW(s_sys_txt_file, L"test_file.txt"); 286 fp = _wfopen(s_sys_txt_file, L"wb"); 287 fclose(fp); 288 ok_int(PathFileExistsW(s_sys_txt_file), TRUE); 289 290 // Check .txt settings 291 WCHAR szPath[MAX_PATH]; 292 FindExecutableW(s_sys_txt_file, NULL, szPath); 293 if (lstrcmpiW(PathFindFileNameW(szPath), L"notepad.exe") != 0) 294 { 295 skip("Please associate .txt with notepad.exe before tests\n"); 296 return FALSE; 297 } 298 299 // command lines 300 StringCchPrintfW(s_win_notepad_cmdline, _countof(s_win_notepad_cmdline), 301 L"\"%s\" ", s_win_notepad); 302 StringCchPrintfW(s_sys_notepad_cmdline, _countof(s_sys_notepad_cmdline), 303 L"\"%s\" ", s_sys_notepad); 304 StringCchPrintfW(s_win_test_exe_cmdline, _countof(s_win_test_exe_cmdline), 305 L"\"%s\" ", s_win_test_exe); 306 StringCchPrintfW(s_sys_test_exe_cmdline, _countof(s_sys_test_exe_cmdline), 307 L"\"%s\" ", s_sys_test_exe); 308 309 GetWindowList(&s_List1); 310 311 return TRUE; 312 } 313 314 static void TEST_End(void) 315 { 316 Sleep(500); 317 GetWindowList(&s_List2); 318 CloseNewWindows(&s_List1, &s_List2); 319 FreeWindowList(&s_List1); 320 FreeWindowList(&s_List2); 321 322 DeleteFileW(s_win_test_exe); 323 DeleteFileW(s_sys_test_exe); 324 DeleteFileW(s_win_txt_file); 325 DeleteFileW(s_sys_txt_file); 326 DeleteFileW(s_win_bat_file); 327 DeleteFileW(s_sys_bat_file); 328 } 329 330 static void test_properties() 331 { 332 HRESULT hrCoInit = CoInitialize(NULL); 333 334 WCHAR Buffer[MAX_PATH * 4]; 335 GetModuleFileNameW(NULL, Buffer, _countof(Buffer)); 336 337 SHELLEXECUTEINFOW info = { sizeof(info) }; 338 info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI; 339 info.lpVerb = L"properties"; 340 info.lpFile = Buffer; 341 info.nShow = SW_SHOW; 342 343 BOOL bRet = ShellExecuteExW(&info); 344 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); 345 ok_ptr(info.hInstApp, (HINSTANCE)42); 346 347 WCHAR* Extension = PathFindExtensionW(Buffer); 348 if (Extension) 349 { 350 // The inclusion of this depends on the file display settings! 351 *Extension = UNICODE_NULL; 352 } 353 354 // Now retry it with the extension cut off 355 bRet = ShellExecuteExW(&info); 356 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError()); 357 ok_ptr(info.hInstApp, (HINSTANCE)42); 358 359 // Now retry it with complete garabage 360 info.lpFile = L"complete garbage, cannot run this!"; 361 bRet = ShellExecuteExW(&info); 362 ok_int(bRet, 0); 363 ok_ptr(info.hInstApp, (HINSTANCE)2); 364 365 if (SUCCEEDED(hrCoInit)) 366 CoUninitialize(); 367 } 368 369 static void test_sei_lpIDList() 370 { 371 if (IsWindowsVistaOrGreater()) 372 { 373 skip("Vista+\n"); 374 return; 375 } 376 377 /* This tests ShellExecuteEx with lpIDList for explorer C:\ */ 378 379 /* ITEMIDLIST for CLSID of 'My Computer' followed by PIDL for 'C:\' */ 380 BYTE lpitemidlist[30] = { 0x14, 0, 0x1f, 0, 0xe0, 0x4f, 0xd0, 0x20, 0xea, 381 0x3a, 0x69, 0x10, 0xa2, 0xd8, 0x08, 0, 0x2b, 0x30, 0x30, 0x9d, // My Computer 382 0x8, 0, 0x23, 0x43, 0x3a, 0x5c, 0x5c, 0, 0, 0,}; // C:\\ + NUL-NUL ending 383 384 SHELLEXECUTEINFOW ShellExecInfo = { sizeof(ShellExecInfo) }; 385 ShellExecInfo.fMask = SEE_MASK_IDLIST; 386 ShellExecInfo.hwnd = NULL; 387 ShellExecInfo.nShow = SW_SHOWNORMAL; 388 ShellExecInfo.lpIDList = lpitemidlist; 389 BOOL ret = ShellExecuteExW(&ShellExecInfo); 390 ok_int(ret, TRUE); 391 } 392 393 static BOOL 394 CreateAppPath(LPCWSTR pszName, LPCWSTR pszValue) 395 { 396 WCHAR szSubKey[MAX_PATH]; 397 StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName); 398 399 LSTATUS error; 400 HKEY hKey; 401 error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szSubKey, 0, NULL, 0, KEY_WRITE, NULL, 402 &hKey, NULL); 403 if (error != ERROR_SUCCESS) 404 trace("Could not create test key (%lu)\n", error); 405 406 DWORD cbValue = (lstrlenW(pszValue) + 1) * sizeof(WCHAR); 407 error = RegSetValueExW(hKey, NULL, 0, REG_SZ, (LPBYTE)pszValue, cbValue); 408 if (error != ERROR_SUCCESS) 409 trace("Could not set value of the test key (%lu)\n", error); 410 411 RegCloseKey(hKey); 412 413 return error == ERROR_SUCCESS; 414 } 415 416 static VOID 417 DeleteAppPath(LPCWSTR pszName) 418 { 419 WCHAR szSubKey[MAX_PATH]; 420 StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName); 421 422 LSTATUS error = RegDeleteKeyW(HKEY_LOCAL_MACHINE, szSubKey); 423 if (error != ERROR_SUCCESS) 424 trace("Could not remove the test key (%lu)\n", error); 425 } 426 427 static void TEST_AppPath(void) 428 { 429 if (CreateAppPath(L"app_path_test.bat", s_win_test_exe)) 430 { 431 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat"); 432 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat.exe"); 433 DeleteAppPath(L"app_path_test.bat"); 434 } 435 436 if (CreateAppPath(L"app_path_test.bat.exe", s_sys_test_exe)) 437 { 438 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat"); 439 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat.exe"); 440 DeleteAppPath(L"app_path_test.bat.exe"); 441 } 442 } 443 444 static void test_DoInvalidDir(void) 445 { 446 WCHAR szSubProgram[MAX_PATH]; 447 if (!FindSubProgram(szSubProgram, _countof(szSubProgram))) 448 { 449 skip("shell32_apitest_sub.exe not found\n"); 450 return; 451 } 452 453 DWORD dwExitCode; 454 SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS }; 455 sei.lpFile = szSubProgram; 456 sei.lpParameters = L"TEST"; 457 sei.nShow = SW_SHOWNORMAL; 458 459 // Test invalid path on sei.lpDirectory 460 WCHAR szInvalidPath[MAX_PATH] = L"M:\\This is an invalid path\n"; 461 sei.lpDirectory = szInvalidPath; 462 ok_int(ShellExecuteExW(&sei), TRUE); 463 WaitForSingleObject(sei.hProcess, 20 * 1000); 464 GetExitCodeProcess(sei.hProcess, &dwExitCode); 465 ok_long(dwExitCode, 0); 466 CloseHandle(sei.hProcess); 467 } 468 469 START_TEST(ShellExecuteEx) 470 { 471 #ifdef _WIN64 472 skip("Win64 is not supported yet\n"); 473 return; 474 #endif 475 476 if (!TEST_Start()) 477 return; 478 479 TEST_AppPath(); 480 TEST_DoTestEntries(); 481 test_properties(); 482 test_sei_lpIDList(); 483 test_DoInvalidDir(); 484 485 TEST_End(); 486 } 487