1 /* 2 * PROJECT: ReactOS API tests 3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory 4 * PURPOSE: Test for ShellExecCmdLine 5 * PROGRAMMERS: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 #include "shelltest.h" 8 #include <shlwapi.h> 9 #include <strsafe.h> 10 #include <versionhelpers.h> 11 #include "shell32_apitest_sub.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 #include <stdio.h> 16 17 #ifndef SECL_NO_UI 18 #define SECL_NO_UI 0x2 19 #define SECL_LOG_USAGE 0x8 20 #define SECL_USE_IDLIST 0x10 21 #define SECL_ALLOW_NONEXE 0x20 22 #define SECL_RUNAS 0x40 23 #endif 24 25 #define ShellExecCmdLine proxy_ShellExecCmdLine 26 27 #define shell32_hInstance GetModuleHandle(NULL) 28 #define IDS_FILE_NOT_FOUND (-1) 29 30 static const WCHAR wszOpen[] = L"open"; 31 static const WCHAR wszExe[] = L".exe"; 32 static const WCHAR wszCom[] = L".com"; 33 34 static __inline void __SHCloneStrW(WCHAR **target, const WCHAR *source) 35 { 36 *target = (WCHAR *)SHAlloc((lstrlenW(source) + 1) * sizeof(WCHAR) ); 37 lstrcpyW(*target, source); 38 } 39 40 // NOTE: You have to sync the following code to dll/win32/shell32/shlexec.cpp. 41 static LPCWSTR 42 SplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0) 43 { 44 LPCWSTR pch; 45 size_t ich = 0; 46 if (*psz == L'"') 47 { 48 // 1st argument is quoted. the string in quotes is quoted 1st argument. 49 // [pch] --> [pszArg0+ich] 50 for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch) 51 { 52 if (*pch == L'"' && pch[1] == L'"') 53 { 54 // doubled double quotations found! 55 pszArg0[ich] = L'"'; 56 } 57 else if (*pch == L'"') 58 { 59 // single double quotation found! 60 ++pch; 61 break; 62 } 63 else 64 { 65 // otherwise 66 pszArg0[ich] = *pch; 67 } 68 } 69 } 70 else 71 { 72 // 1st argument is unquoted. non-space sequence is 1st argument. 73 // [pch] --> [pszArg0+ich] 74 for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch) 75 { 76 pszArg0[ich] = *pch; 77 } 78 } 79 pszArg0[ich] = 0; 80 81 // skip space 82 while (iswspace(*pch)) 83 ++pch; 84 85 return pch; 86 } 87 88 HRESULT WINAPI ShellExecCmdLine( 89 HWND hwnd, 90 LPCWSTR pwszCommand, 91 LPCWSTR pwszStartDir, 92 int nShow, 93 LPVOID pUnused, 94 DWORD dwSeclFlags) 95 { 96 SHELLEXECUTEINFOW info; 97 DWORD dwSize, dwError, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC; 98 LPCWSTR pszVerb = NULL; 99 WCHAR szFile[MAX_PATH], szFile2[MAX_PATH]; 100 HRESULT hr; 101 LPCWSTR pchParams; 102 LPWSTR lpCommand = NULL; 103 104 if (pwszCommand == NULL) 105 RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 106 1, (ULONG_PTR*)pwszCommand); 107 108 __SHCloneStrW(&lpCommand, pwszCommand); 109 StrTrimW(lpCommand, L" \t"); 110 111 if (dwSeclFlags & SECL_NO_UI) 112 dwFlags |= SEE_MASK_FLAG_NO_UI; 113 if (dwSeclFlags & SECL_LOG_USAGE) 114 dwFlags |= SEE_MASK_FLAG_LOG_USAGE; 115 if (dwSeclFlags & SECL_USE_IDLIST) 116 dwFlags |= SEE_MASK_INVOKEIDLIST; 117 118 if (dwSeclFlags & SECL_RUNAS) 119 { 120 dwSize = 0; 121 hr = AssocQueryStringW(0, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize); 122 if (SUCCEEDED(hr) && dwSize != 0) 123 { 124 pszVerb = L"runas"; 125 } 126 } 127 128 if (UrlIsFileUrlW(lpCommand)) 129 { 130 StringCchCopyW(szFile, _countof(szFile), lpCommand); 131 pchParams = NULL; 132 } 133 else 134 { 135 pchParams = SplitParams(lpCommand, szFile, _countof(szFile)); 136 if (szFile[0] != UNICODE_NULL && szFile[1] == L':' && 137 szFile[2] == UNICODE_NULL) 138 { 139 PathAddBackslashW(szFile); 140 } 141 142 WCHAR szCurDir[MAX_PATH]; 143 GetCurrentDirectoryW(_countof(szCurDir), szCurDir); 144 if (pwszStartDir) 145 { 146 SetCurrentDirectoryW(pwszStartDir); 147 } 148 149 if (PathIsRelativeW(szFile) && 150 GetFullPathNameW(szFile, _countof(szFile2), szFile2, NULL) && 151 PathFileExistsW(szFile2)) 152 { 153 StringCchCopyW(szFile, _countof(szFile), szFile2); 154 } 155 else if (SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL) || 156 SearchPathW(NULL, szFile, wszExe, _countof(szFile2), szFile2, NULL) || 157 SearchPathW(NULL, szFile, wszCom, _countof(szFile2), szFile2, NULL) || 158 SearchPathW(pwszStartDir, szFile, NULL, _countof(szFile2), szFile2, NULL) || 159 SearchPathW(pwszStartDir, szFile, wszExe, _countof(szFile2), szFile2, NULL) || 160 SearchPathW(pwszStartDir, szFile, wszCom, _countof(szFile2), szFile2, NULL)) 161 { 162 StringCchCopyW(szFile, _countof(szFile), szFile2); 163 } 164 else if (SearchPathW(NULL, lpCommand, NULL, _countof(szFile2), szFile2, NULL) || 165 SearchPathW(NULL, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) || 166 SearchPathW(NULL, lpCommand, wszCom, _countof(szFile2), szFile2, NULL) || 167 SearchPathW(pwszStartDir, lpCommand, NULL, _countof(szFile2), szFile2, NULL) || 168 SearchPathW(pwszStartDir, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) || 169 SearchPathW(pwszStartDir, lpCommand, wszCom, _countof(szFile2), szFile2, NULL)) 170 { 171 StringCchCopyW(szFile, _countof(szFile), szFile2); 172 pchParams = NULL; 173 } 174 175 if (pwszStartDir) 176 { 177 SetCurrentDirectoryW(szCurDir); 178 } 179 180 if (!(dwSeclFlags & SECL_ALLOW_NONEXE)) 181 { 182 if (!GetBinaryTypeW(szFile, &dwType)) 183 { 184 SHFree(lpCommand); 185 186 if (!(dwSeclFlags & SECL_NO_UI)) 187 { 188 WCHAR szText[128 + MAX_PATH], szFormat[128]; 189 LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); 190 StringCchPrintfW(szText, _countof(szText), szFormat, szFile); 191 MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); 192 } 193 return CO_E_APPNOTFOUND; 194 } 195 } 196 else 197 { 198 if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES) 199 { 200 SHFree(lpCommand); 201 202 if (!(dwSeclFlags & SECL_NO_UI)) 203 { 204 WCHAR szText[128 + MAX_PATH], szFormat[128]; 205 LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); 206 StringCchPrintfW(szText, _countof(szText), szFormat, szFile); 207 MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); 208 } 209 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 210 } 211 } 212 } 213 214 ZeroMemory(&info, sizeof(info)); 215 info.cbSize = sizeof(info); 216 info.fMask = dwFlags; 217 info.hwnd = hwnd; 218 info.lpVerb = pszVerb; 219 info.lpFile = szFile; 220 info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL; 221 info.lpDirectory = pwszStartDir; 222 info.nShow = nShow; 223 if (ShellExecuteExW(&info)) 224 { 225 if (info.lpIDList) 226 CoTaskMemFree(info.lpIDList); 227 228 SHFree(lpCommand); 229 230 return S_OK; 231 } 232 233 dwError = GetLastError(); 234 235 SHFree(lpCommand); 236 237 return HRESULT_FROM_WIN32(dwError); 238 } 239 240 #undef ShellExecCmdLine 241 242 typedef HRESULT (WINAPI *SHELLEXECCMDLINE)(HWND, LPCWSTR, LPCWSTR, INT, LPVOID, DWORD); 243 SHELLEXECCMDLINE g_pShellExecCmdLine = NULL; 244 245 typedef struct TEST_ENTRY 246 { 247 INT lineno; 248 BOOL result; 249 BOOL bAllowNonExe; 250 LPCWSTR pwszCommand; 251 LPCWSTR pwszStartDir; 252 } TEST_ENTRY; 253 254 static WCHAR s_sub_program[MAX_PATH]; 255 static WCHAR s_win_test_exe[MAX_PATH]; 256 static WCHAR s_sys_bat_file[MAX_PATH]; 257 static WCHAR s_cur_dir[MAX_PATH]; 258 259 static BOOL 260 GetSubProgramPath(void) 261 { 262 GetModuleFileNameW(NULL, s_sub_program, _countof(s_sub_program)); 263 PathRemoveFileSpecW(s_sub_program); 264 PathAppendW(s_sub_program, L"shell32_apitest_sub.exe"); 265 266 if (!PathFileExistsW(s_sub_program)) 267 { 268 PathRemoveFileSpecW(s_sub_program); 269 PathAppendW(s_sub_program, L"testdata\\shell32_apitest_sub.exe"); 270 271 if (!PathFileExistsW(s_sub_program)) 272 { 273 return FALSE; 274 } 275 } 276 277 return TRUE; 278 } 279 280 static const TEST_ENTRY s_entries_1[] = 281 { 282 // NULL 283 { __LINE__, 0xBADFACE, FALSE, NULL, NULL }, 284 { __LINE__, 0xBADFACE, FALSE, NULL, L"." }, 285 { __LINE__, 0xBADFACE, FALSE, NULL, L"system32" }, 286 { __LINE__, 0xBADFACE, FALSE, NULL, L"C:\\Program Files" }, 287 { __LINE__, 0xBADFACE, TRUE, NULL, NULL }, 288 { __LINE__, 0xBADFACE, TRUE, NULL, L"." }, 289 { __LINE__, 0xBADFACE, TRUE, NULL, L"system32" }, 290 { __LINE__, 0xBADFACE, TRUE, NULL, L"C:\\Program Files" }, 291 // notepad 292 { __LINE__, TRUE, FALSE, L"notepad", NULL }, 293 { __LINE__, TRUE, FALSE, L"notepad", L"." }, 294 { __LINE__, TRUE, FALSE, L"notepad", L"system32" }, 295 { __LINE__, TRUE, FALSE, L"notepad", L"C:\\Program Files" }, 296 { __LINE__, TRUE, FALSE, L"notepad \"Test File.txt\"", NULL }, 297 { __LINE__, TRUE, FALSE, L"notepad \"Test File.txt\"", L"." }, 298 { __LINE__, TRUE, TRUE, L"notepad", NULL }, 299 { __LINE__, TRUE, TRUE, L"notepad", L"." }, 300 { __LINE__, TRUE, TRUE, L"notepad", L"system32" }, 301 { __LINE__, TRUE, TRUE, L"notepad", L"C:\\Program Files" }, 302 { __LINE__, TRUE, TRUE, L"notepad \"Test File.txt\"", NULL }, 303 { __LINE__, TRUE, TRUE, L"notepad \"Test File.txt\"", L"." }, 304 // notepad.exe 305 { __LINE__, TRUE, FALSE, L"notepad.exe", NULL }, 306 { __LINE__, TRUE, FALSE, L"notepad.exe", L"." }, 307 { __LINE__, TRUE, FALSE, L"notepad.exe", L"system32" }, 308 { __LINE__, TRUE, FALSE, L"notepad.exe", L"C:\\Program Files" }, 309 { __LINE__, TRUE, FALSE, L"notepad.exe \"Test File.txt\"", NULL }, 310 { __LINE__, TRUE, FALSE, L"notepad.exe \"Test File.txt\"", L"." }, 311 { __LINE__, TRUE, TRUE, L"notepad.exe", NULL }, 312 { __LINE__, TRUE, TRUE, L"notepad.exe", L"." }, 313 { __LINE__, TRUE, TRUE, L"notepad.exe", L"system32" }, 314 { __LINE__, TRUE, TRUE, L"notepad.exe", L"C:\\Program Files" }, 315 { __LINE__, TRUE, TRUE, L"notepad.exe \"Test File.txt\"", NULL }, 316 { __LINE__, TRUE, TRUE, L"notepad.exe \"Test File.txt\"", L"." }, 317 // C:\notepad.exe 318 { __LINE__, FALSE, FALSE, L"C:\\notepad.exe", NULL }, 319 { __LINE__, FALSE, FALSE, L"C:\\notepad.exe", L"." }, 320 { __LINE__, FALSE, FALSE, L"C:\\notepad.exe", L"system32" }, 321 { __LINE__, FALSE, FALSE, L"C:\\notepad.exe", L"C:\\Program Files" }, 322 { __LINE__, FALSE, FALSE, L"C:\\notepad.exe \"Test File.txt\"", NULL }, 323 { __LINE__, FALSE, FALSE, L"C:\\notepad.exe \"Test File.txt\"", L"." }, 324 { __LINE__, FALSE, TRUE, L"C:\\notepad.exe", NULL }, 325 { __LINE__, FALSE, TRUE, L"C:\\notepad.exe", L"." }, 326 { __LINE__, FALSE, TRUE, L"C:\\notepad.exe", L"system32" }, 327 { __LINE__, FALSE, TRUE, L"C:\\notepad.exe", L"C:\\Program Files" }, 328 { __LINE__, FALSE, TRUE, L"C:\\notepad.exe \"Test File.txt\"", NULL }, 329 { __LINE__, FALSE, TRUE, L"C:\\notepad.exe \"Test File.txt\"", L"." }, 330 // "notepad" 331 { __LINE__, TRUE, FALSE, L"\"notepad\"", NULL }, 332 { __LINE__, TRUE, FALSE, L"\"notepad\"", L"." }, 333 { __LINE__, TRUE, FALSE, L"\"notepad\"", L"system32" }, 334 { __LINE__, TRUE, FALSE, L"\"notepad\"", L"C:\\Program Files" }, 335 { __LINE__, TRUE, FALSE, L"\"notepad\" \"Test File.txt\"", NULL }, 336 { __LINE__, TRUE, FALSE, L"\"notepad\" \"Test File.txt\"", L"." }, 337 { __LINE__, TRUE, TRUE, L"\"notepad\"", NULL }, 338 { __LINE__, TRUE, TRUE, L"\"notepad\"", L"." }, 339 { __LINE__, TRUE, TRUE, L"\"notepad\"", L"system32" }, 340 { __LINE__, TRUE, TRUE, L"\"notepad\"", L"C:\\Program Files" }, 341 { __LINE__, TRUE, TRUE, L"\"notepad\" \"Test File.txt\"", NULL }, 342 { __LINE__, TRUE, TRUE, L"\"notepad\" \"Test File.txt\"", L"." }, 343 // "notepad.exe" 344 { __LINE__, TRUE, FALSE, L"\"notepad.exe\"", NULL }, 345 { __LINE__, TRUE, FALSE, L"\"notepad.exe\"", L"." }, 346 { __LINE__, TRUE, FALSE, L"\"notepad.exe\"", L"system32" }, 347 { __LINE__, TRUE, FALSE, L"\"notepad.exe\"", L"C:\\Program Files" }, 348 { __LINE__, TRUE, FALSE, L"\"notepad.exe\" \"Test File.txt\"", NULL }, 349 { __LINE__, TRUE, FALSE, L"\"notepad.exe\" \"Test File.txt\"", L"." }, 350 { __LINE__, TRUE, TRUE, L"\"notepad.exe\"", NULL }, 351 { __LINE__, TRUE, TRUE, L"\"notepad.exe\"", L"." }, 352 { __LINE__, TRUE, TRUE, L"\"notepad.exe\"", L"system32" }, 353 { __LINE__, TRUE, TRUE, L"\"notepad.exe\"", L"C:\\Program Files" }, 354 { __LINE__, TRUE, TRUE, L"\"notepad.exe\" \"Test File.txt\"", NULL }, 355 { __LINE__, TRUE, TRUE, L"\"notepad.exe\" \"Test File.txt\"", L"." }, 356 // test program 357 { __LINE__, FALSE, FALSE, L"test program", NULL }, 358 { __LINE__, FALSE, FALSE, L"test program", L"." }, 359 { __LINE__, FALSE, FALSE, L"test program", L"system32" }, 360 { __LINE__, FALSE, FALSE, L"test program", L"C:\\Program Files" }, 361 { __LINE__, FALSE, FALSE, L"test program \"Test File.txt\"", NULL }, 362 { __LINE__, FALSE, FALSE, L"test program \"Test File.txt\"", L"." }, 363 { __LINE__, FALSE, TRUE, L"test program", NULL }, 364 { __LINE__, FALSE, TRUE, L"test program", L"." }, 365 { __LINE__, FALSE, TRUE, L"test program", L"system32" }, 366 { __LINE__, FALSE, TRUE, L"test program", L"C:\\Program Files" }, 367 { __LINE__, FALSE, TRUE, L"test program \"Test File.txt\"", NULL }, 368 { __LINE__, FALSE, TRUE, L"test program \"Test File.txt\"", L"." }, 369 // test program.exe 370 { __LINE__, FALSE, FALSE, L"test program.exe", NULL }, 371 { __LINE__, FALSE, FALSE, L"test program.exe", L"." }, 372 { __LINE__, FALSE, FALSE, L"test program.exe", L"system32" }, 373 { __LINE__, FALSE, FALSE, L"test program.exe", L"C:\\Program Files" }, 374 { __LINE__, FALSE, FALSE, L"test program.exe \"Test File.txt\"", NULL }, 375 { __LINE__, FALSE, FALSE, L"test program.exe \"Test File.txt\"", L"." }, 376 { __LINE__, FALSE, TRUE, L"test program.exe", NULL }, 377 { __LINE__, FALSE, TRUE, L"test program.exe", L"." }, 378 { __LINE__, FALSE, TRUE, L"test program.exe", L"system32" }, 379 { __LINE__, FALSE, TRUE, L"test program.exe", L"C:\\Program Files" }, 380 { __LINE__, FALSE, TRUE, L"test program.exe \"Test File.txt\"", NULL }, 381 { __LINE__, FALSE, TRUE, L"test program.exe \"Test File.txt\"", L"." }, 382 // test program.bat 383 { __LINE__, FALSE, FALSE, L"test program.bat", NULL }, 384 { __LINE__, FALSE, FALSE, L"test program.bat", L"." }, 385 { __LINE__, FALSE, FALSE, L"test program.bat", L"system32" }, 386 { __LINE__, FALSE, FALSE, L"test program.bat", L"C:\\Program Files" }, 387 { __LINE__, FALSE, FALSE, L"test program.bat \"Test File.txt\"", NULL }, 388 { __LINE__, FALSE, FALSE, L"test program.bat \"Test File.txt\"", L"." }, 389 { __LINE__, FALSE, TRUE, L"test program.bat", NULL }, 390 { __LINE__, FALSE, TRUE, L"test program.bat", L"." }, 391 { __LINE__, FALSE, TRUE, L"test program.bat", L"system32" }, 392 { __LINE__, FALSE, TRUE, L"test program.bat", L"C:\\Program Files" }, 393 { __LINE__, FALSE, TRUE, L"test program.bat \"Test File.txt\"", NULL }, 394 { __LINE__, FALSE, TRUE, L"test program.bat \"Test File.txt\"", L"." }, 395 // "test program" 396 { __LINE__, FALSE, FALSE, L"\"test program\"", NULL }, 397 { __LINE__, FALSE, FALSE, L"\"test program\"", L"." }, 398 { __LINE__, FALSE, FALSE, L"\"test program\"", L"system32" }, 399 { __LINE__, FALSE, FALSE, L"\"test program\"", L"C:\\Program Files" }, 400 { __LINE__, FALSE, FALSE, L"\"test program\" \"Test File.txt\"", NULL }, 401 { __LINE__, FALSE, FALSE, L"\"test program\" \"Test File.txt\"", L"." }, 402 { __LINE__, FALSE, TRUE, L"\"test program\"", NULL }, 403 { __LINE__, FALSE, TRUE, L"\"test program\"", L"." }, 404 { __LINE__, FALSE, TRUE, L"\"test program\"", L"system32" }, 405 { __LINE__, FALSE, TRUE, L"\"test program\"", L"C:\\Program Files" }, 406 { __LINE__, FALSE, TRUE, L"\"test program\" \"Test File.txt\"", NULL }, 407 { __LINE__, FALSE, TRUE, L"\"test program\" \"Test File.txt\"", L"." }, 408 // "test program.exe" 409 { __LINE__, TRUE, FALSE, L"\"test program.exe\"", NULL }, 410 { __LINE__, TRUE, FALSE, L"\"test program.exe\"", L"." }, 411 { __LINE__, TRUE, FALSE, L"\"test program.exe\"", L"system32" }, 412 { __LINE__, TRUE, FALSE, L"\"test program.exe\"", L"C:\\Program Files" }, 413 { __LINE__, TRUE, FALSE, L"\"test program.exe\" \"Test File.txt\"", NULL }, 414 { __LINE__, TRUE, FALSE, L"\"test program.exe\" \"Test File.txt\"", L"." }, 415 { __LINE__, TRUE, TRUE, L"\"test program.exe\"", NULL }, 416 { __LINE__, TRUE, TRUE, L"\"test program.exe\"", L"." }, 417 { __LINE__, TRUE, TRUE, L"\"test program.exe\"", L"system32" }, 418 { __LINE__, TRUE, TRUE, L"\"test program.exe\"", L"C:\\Program Files" }, 419 { __LINE__, TRUE, TRUE, L"\"test program.exe\" \"Test File.txt\"", NULL }, 420 { __LINE__, TRUE, TRUE, L"\"test program.exe\" \"Test File.txt\"", L"." }, 421 // "test program.bat" 422 { __LINE__, FALSE, FALSE, L"\"test program.bat\"", NULL }, 423 { __LINE__, FALSE, FALSE, L"\"test program.bat\"", L"." }, 424 { __LINE__, FALSE, FALSE, L"\"test program.bat\"", L"system32" }, 425 { __LINE__, FALSE, FALSE, L"\"test program.bat\"", L"C:\\Program Files" }, 426 { __LINE__, FALSE, FALSE, L"\"test program.bat\" \"Test File.txt\"", NULL }, 427 { __LINE__, FALSE, FALSE, L"\"test program.bat\" \"Test File.txt\"", L"." }, 428 { __LINE__, FALSE, TRUE, L"\"test program.bat\"", NULL }, 429 { __LINE__, FALSE, TRUE, L"\"test program.bat\"", L"." }, 430 { __LINE__, FALSE, TRUE, L"\"test program.bat\"", L"system32" }, 431 { __LINE__, FALSE, TRUE, L"\"test program.bat\"", L"C:\\Program Files" }, 432 { __LINE__, FALSE, TRUE, L"\"test program.bat\" \"Test File.txt\"", NULL }, 433 { __LINE__, FALSE, TRUE, L"\"test program.bat\" \"Test File.txt\"", L"." }, 434 // invalid program 435 { __LINE__, FALSE, FALSE, L"invalid program", NULL }, 436 { __LINE__, FALSE, FALSE, L"invalid program", L"." }, 437 { __LINE__, FALSE, FALSE, L"invalid program", L"system32" }, 438 { __LINE__, FALSE, FALSE, L"invalid program", L"C:\\Program Files" }, 439 { __LINE__, FALSE, FALSE, L"invalid program \"Test File.txt\"", NULL }, 440 { __LINE__, FALSE, FALSE, L"invalid program \"Test File.txt\"", L"." }, 441 { __LINE__, FALSE, TRUE, L"invalid program", NULL }, 442 { __LINE__, FALSE, TRUE, L"invalid program", L"." }, 443 { __LINE__, FALSE, TRUE, L"invalid program", L"system32" }, 444 { __LINE__, FALSE, TRUE, L"invalid program", L"C:\\Program Files" }, 445 { __LINE__, FALSE, TRUE, L"invalid program \"Test File.txt\"", NULL }, 446 { __LINE__, FALSE, TRUE, L"invalid program \"Test File.txt\"", L"." }, 447 // \"invalid program.exe\" 448 { __LINE__, FALSE, FALSE, L"\"invalid program.exe\"", NULL }, 449 { __LINE__, FALSE, FALSE, L"\"invalid program.exe\"", L"." }, 450 { __LINE__, FALSE, FALSE, L"\"invalid program.exe\"", L"system32" }, 451 { __LINE__, FALSE, FALSE, L"\"invalid program.exe\"", L"C:\\Program Files" }, 452 { __LINE__, FALSE, FALSE, L"\"invalid program.exe\" \"Test File.txt\"", NULL }, 453 { __LINE__, FALSE, FALSE, L"\"invalid program.exe\" \"Test File.txt\"", L"." }, 454 { __LINE__, FALSE, TRUE, L"\"invalid program.exe\"", NULL }, 455 { __LINE__, FALSE, TRUE, L"\"invalid program.exe\"", L"." }, 456 { __LINE__, FALSE, TRUE, L"\"invalid program.exe\"", L"system32" }, 457 { __LINE__, FALSE, TRUE, L"\"invalid program.exe\"", L"C:\\Program Files" }, 458 { __LINE__, FALSE, TRUE, L"\"invalid program.exe\" \"Test File.txt\"", NULL }, 459 { __LINE__, FALSE, TRUE, L"\"invalid program.exe\" \"Test File.txt\"", L"." }, 460 // My Documents 461 { __LINE__, TRUE, TRUE, L"::{450d8fba-ad25-11d0-98a8-0800361b1103}", NULL }, 462 { __LINE__, TRUE, TRUE, L"shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}", NULL }, 463 // shell:sendto 464 { __LINE__, TRUE, TRUE, L"shell:sendto", NULL }, 465 // iexplore.exe 466 { __LINE__, TRUE, FALSE, L"iexplore", NULL }, 467 { __LINE__, TRUE, FALSE, L"iexplore.exe", NULL }, 468 { __LINE__, TRUE, TRUE, L"iexplore", NULL }, 469 { __LINE__, TRUE, TRUE, L"iexplore.exe", NULL }, 470 // https://google.com 471 { __LINE__, TRUE, FALSE, L"https://google.com", NULL }, 472 { __LINE__, TRUE, TRUE, L"https://google.com", NULL }, 473 // Test File 1.txt 474 { __LINE__, FALSE, FALSE, L"Test File 1.txt", NULL }, 475 { __LINE__, FALSE, FALSE, L"Test File 1.txt", L"." }, 476 { __LINE__, FALSE, FALSE, L"Test File 1.txt", L"system32" }, 477 { __LINE__, FALSE, FALSE, L"Test File 1.txt", s_cur_dir }, 478 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", NULL }, 479 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", L"." }, 480 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", L"system32" }, 481 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", s_cur_dir }, 482 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", NULL }, 483 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", L"." }, 484 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", L"system32" }, 485 { __LINE__, FALSE, TRUE, L"Test File 1.txt", NULL }, 486 { __LINE__, TRUE, TRUE, L"Test File 1.txt", L"." }, 487 { __LINE__, FALSE, TRUE, L"Test File 1.txt", L"system32" }, 488 { __LINE__, TRUE, TRUE, L"Test File 1.txt", s_cur_dir }, 489 { __LINE__, FALSE, TRUE, L"\"Test File 1.txt\"", NULL }, 490 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", L"." }, 491 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", L"system32" }, 492 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", s_cur_dir }, 493 { __LINE__, FALSE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", NULL }, 494 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", L"." }, 495 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", L"system32" }, 496 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", s_cur_dir }, 497 // Test File 2.bat 498 { __LINE__, FALSE, FALSE, L"Test File 2.bat", NULL }, 499 { __LINE__, FALSE, FALSE, L"Test File 2.bat", L"." }, 500 { __LINE__, FALSE, FALSE, L"Test File 2.bat", L"system32" }, 501 { __LINE__, FALSE, FALSE, L"Test File 2.bat", s_cur_dir }, 502 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", NULL }, 503 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", L"." }, 504 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", L"system32" }, 505 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", s_cur_dir }, 506 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", NULL }, 507 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", L"." }, 508 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", L"system32" }, 509 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir }, 510 { __LINE__, FALSE, TRUE, L"Test File 2.bat", NULL }, 511 { __LINE__, FALSE, TRUE, L"Test File 2.bat", L"." }, 512 { __LINE__, FALSE, TRUE, L"Test File 2.bat", L"system32" }, 513 { __LINE__, FALSE, TRUE, L"Test File 2.bat", s_cur_dir }, 514 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", NULL }, 515 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", L"." }, 516 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", L"system32" }, 517 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", s_cur_dir }, 518 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", NULL }, 519 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", L"." }, 520 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", L"system32" }, 521 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir }, 522 }; 523 524 static const TEST_ENTRY s_entries_2[] = 525 { 526 // Test File 1.txt (with setting path) 527 { __LINE__, FALSE, FALSE, L"Test File 1.txt", NULL }, 528 { __LINE__, FALSE, FALSE, L"Test File 1.txt", L"." }, 529 { __LINE__, FALSE, FALSE, L"Test File 1.txt", L"system32" }, 530 { __LINE__, FALSE, FALSE, L"Test File 1.txt", s_cur_dir }, 531 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", NULL }, 532 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", L"." }, 533 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", L"system32" }, 534 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", s_cur_dir }, 535 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", NULL }, 536 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", L"." }, 537 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", L"system32" }, 538 { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", s_cur_dir }, 539 { __LINE__, FALSE, TRUE, L"Test File 1.txt", NULL }, 540 { __LINE__, TRUE, TRUE, L"Test File 1.txt", L"." }, 541 { __LINE__, FALSE, TRUE, L"Test File 1.txt", L"system32" }, 542 { __LINE__, TRUE, TRUE, L"Test File 1.txt", s_cur_dir }, 543 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", NULL }, 544 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", L"." }, 545 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", L"system32" }, 546 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", s_cur_dir }, 547 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", NULL }, 548 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", L"." }, 549 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", L"system32" }, 550 { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", s_cur_dir }, 551 // Test File 2.bat (with setting path) 552 { __LINE__, FALSE, FALSE, L"Test File 2.bat", NULL }, 553 { __LINE__, FALSE, FALSE, L"Test File 2.bat", L"." }, 554 { __LINE__, FALSE, FALSE, L"Test File 2.bat", L"system32" }, 555 { __LINE__, FALSE, FALSE, L"Test File 2.bat", s_cur_dir }, 556 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", NULL }, 557 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", L"." }, 558 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", L"system32" }, 559 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", s_cur_dir }, 560 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", NULL }, 561 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", L"." }, 562 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", L"system32" }, 563 { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir }, 564 { __LINE__, FALSE, TRUE, L"Test File 2.bat", NULL }, 565 { __LINE__, FALSE, TRUE, L"Test File 2.bat", L"." }, 566 { __LINE__, FALSE, TRUE, L"Test File 2.bat", L"system32" }, 567 { __LINE__, FALSE, TRUE, L"Test File 2.bat", s_cur_dir }, 568 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", NULL }, 569 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", L"." }, 570 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", L"system32" }, 571 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", s_cur_dir }, 572 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", NULL }, 573 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", L"." }, 574 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", L"system32" }, 575 { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir }, 576 }; 577 578 typedef struct OPENWNDS 579 { 580 UINT count; 581 HWND *phwnd; 582 } OPENWNDS; 583 584 static OPENWNDS s_wi0 = { 0 }, s_wi1 = { 0 }; 585 586 static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) 587 { 588 OPENWNDS *info = (OPENWNDS *)lParam; 589 info->phwnd = (HWND *)realloc(info->phwnd, (info->count + 1) * sizeof(HWND)); 590 if (!info->phwnd) 591 return FALSE; 592 info->phwnd[info->count] = hwnd; 593 ++(info->count); 594 return TRUE; 595 } 596 597 static void CleanupNewlyCreatedWindows(void) 598 { 599 EnumWindows(EnumWindowsProc, (LPARAM)&s_wi1); 600 for (UINT i1 = 0; i1 < s_wi1.count; ++i1) 601 { 602 BOOL bFound = FALSE; 603 for (UINT i0 = 0; i0 < s_wi0.count; ++i0) 604 { 605 if (s_wi1.phwnd[i1] == s_wi0.phwnd[i0]) 606 { 607 bFound = TRUE; 608 break; 609 } 610 } 611 if (!bFound) 612 { 613 DWORD dwPID; 614 GetWindowThreadProcessId(s_wi1.phwnd[i1], &dwPID); 615 HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, TRUE, dwPID); 616 TerminateProcess(hProcess, -1); 617 CloseHandle(hProcess); 618 } 619 } 620 free(s_wi1.phwnd); 621 ZeroMemory(&s_wi1, sizeof(s_wi1)); 622 } 623 624 static void DoEntry(const TEST_ENTRY *pEntry) 625 { 626 HRESULT hr; 627 DWORD dwSeclFlags; 628 BOOL result; 629 630 if (pEntry->bAllowNonExe) 631 dwSeclFlags = SECL_NO_UI | SECL_ALLOW_NONEXE; 632 else 633 dwSeclFlags = SECL_NO_UI; 634 635 _SEH2_TRY 636 { 637 if (IsReactOS()) 638 { 639 hr = proxy_ShellExecCmdLine(NULL, pEntry->pwszCommand, pEntry->pwszStartDir, 640 SW_SHOWNORMAL, NULL, dwSeclFlags); 641 } 642 else 643 { 644 hr = (*g_pShellExecCmdLine)(NULL, pEntry->pwszCommand, pEntry->pwszStartDir, 645 SW_SHOWNORMAL, NULL, dwSeclFlags); 646 } 647 } 648 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 649 { 650 hr = 0xBADFACE; 651 } 652 _SEH2_END; 653 654 if (hr == 0xBADFACE) 655 result = hr; 656 else 657 result = (hr == S_OK); 658 659 ok(result == pEntry->result, "Line %d: result expected %d, was %d\n", 660 pEntry->lineno, pEntry->result, result); 661 662 CleanupNewlyCreatedWindows(); 663 } 664 665 START_TEST(ShellExecCmdLine) 666 { 667 using namespace std; 668 669 if (!IsReactOS()) 670 { 671 if (!IsWindowsVistaOrGreater()) 672 { 673 skip("ShellExecCmdLine is not available on this platform\n"); 674 return; 675 } 676 677 HMODULE hShell32 = GetModuleHandleA("shell32"); 678 g_pShellExecCmdLine = (SHELLEXECCMDLINE)GetProcAddress(hShell32, (LPCSTR)(INT_PTR)265); 679 if (!g_pShellExecCmdLine) 680 { 681 skip("ShellExecCmdLine is not found\n"); 682 return; 683 } 684 } 685 686 if (!GetSubProgramPath()) 687 { 688 skip("shell32_apitest_sub.exe is not found\n"); 689 return; 690 } 691 692 // record open windows 693 if (!EnumWindows(EnumWindowsProc, (LPARAM)&s_wi0)) 694 { 695 skip("EnumWindows failed\n"); 696 free(s_wi0.phwnd); 697 return; 698 } 699 700 // s_win_test_exe 701 GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe)); 702 PathAppendW(s_win_test_exe, L"test program.exe"); 703 BOOL ret = CopyFileW(s_sub_program, s_win_test_exe, FALSE); 704 if (!ret) 705 { 706 skip("Please retry with admin rights\n"); 707 free(s_wi0.phwnd); 708 return; 709 } 710 711 FILE *fp; 712 713 // s_sys_bat_file 714 GetSystemDirectoryW(s_sys_bat_file, _countof(s_sys_bat_file)); 715 PathAppendW(s_sys_bat_file, L"test program.bat"); 716 fp = _wfopen(s_sys_bat_file, L"wb"); 717 fclose(fp); 718 ok_int(PathFileExistsW(s_sys_bat_file), TRUE); 719 720 // "Test File 1.txt" 721 fp = fopen("Test File 1.txt", "wb"); 722 ok(fp != NULL, "failed to create a test file\n"); 723 fclose(fp); 724 ok_int(PathFileExistsA("Test File 1.txt"), TRUE); 725 726 // "Test File 2.bat" 727 fp = fopen("Test File 2.bat", "wb"); 728 ok(fp != NULL, "failed to create a test file\n"); 729 fclose(fp); 730 ok_int(PathFileExistsA("Test File 2.bat"), TRUE); 731 732 // s_cur_dir 733 GetCurrentDirectoryW(_countof(s_cur_dir), s_cur_dir); 734 735 // do tests 736 for (size_t i = 0; i < _countof(s_entries_1); ++i) 737 { 738 DoEntry(&s_entries_1[i]); 739 } 740 SetEnvironmentVariableW(L"PATH", s_cur_dir); 741 for (size_t i = 0; i < _countof(s_entries_2); ++i) 742 { 743 DoEntry(&s_entries_2[i]); 744 } 745 746 Sleep(2000); 747 CleanupNewlyCreatedWindows(); 748 749 // clean up 750 ok(DeleteFileW(s_win_test_exe), "failed to delete the test file\n"); 751 ok(DeleteFileW(s_sys_bat_file), "failed to delete the test file\n"); 752 ok(DeleteFileA("Test File 1.txt"), "failed to delete the test file\n"); 753 ok(DeleteFileA("Test File 2.bat"), "failed to delete the test file\n"); 754 free(s_wi0.phwnd); 755 756 DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE); 757 Sleep(100); 758 } 759