1 /* 2 * Shell Library Functions 3 * 4 * Copyright 1998 Marcus Meissner 5 * Copyright 2002 Eric Pouech 6 * Copyright 2018-2024 Katayama Hirofumi MZ 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 */ 22 23 #include "precomp.h" 24 #include <undocshell.h> 25 26 WINE_DEFAULT_DEBUG_CHANNEL(exec); 27 28 EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath); 29 30 #define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY) 31 32 typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, 33 const SHELLEXECUTEINFOW *sei, LPSHELLEXECUTEINFOW sei_out); 34 35 // Is the current process a rundll32.exe? 36 static BOOL SHELL_InRunDllProcess(VOID) 37 { 38 WCHAR szModule[MAX_PATH]; 39 static INT s_bInDllProcess = -1; 40 41 if (s_bInDllProcess != -1) 42 return s_bInDllProcess; 43 44 s_bInDllProcess = GetModuleFileNameW(NULL, szModule, _countof(szModule)) && 45 (StrStrIW(PathFindFileNameW(szModule), L"rundll") != NULL); 46 return s_bInDllProcess; 47 } 48 49 static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum) 50 { 51 bool firstCharQuote = false; 52 bool quotes_opened = false; 53 bool backslash_encountered = false; 54 55 for (int curArg = 0; curArg <= argNum && *args; ++curArg) 56 { 57 firstCharQuote = false; 58 if (*args == '"') 59 { 60 quotes_opened = true; 61 firstCharQuote = true; 62 args++; 63 } 64 65 while(*args) 66 { 67 if (*args == '\\') 68 { 69 // if we found a backslash then flip the variable 70 backslash_encountered = !backslash_encountered; 71 } 72 else if (*args == '"') 73 { 74 if (quotes_opened) 75 { 76 if (*(args + 1) != '"') 77 { 78 quotes_opened = false; 79 args++; 80 break; 81 } 82 else 83 { 84 args++; 85 } 86 } 87 else 88 { 89 quotes_opened = true; 90 } 91 92 backslash_encountered = false; 93 } 94 else 95 { 96 backslash_encountered = false; 97 if (*args == ' ' && !firstCharQuote) 98 break; 99 } 100 101 if (curArg == argNum) 102 { 103 used++; 104 if (used < len) 105 *res++ = *args; 106 } 107 108 args++; 109 } 110 111 while(*args == ' ') 112 ++args; 113 } 114 } 115 116 static void ParseTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum) 117 { 118 bool quotes_opened = false; 119 bool backslash_encountered = false; 120 121 for (int curArg = 0; curArg <= argNum && *args; ++curArg) 122 { 123 while(*args) 124 { 125 if (*args == '\\') 126 { 127 // if we found a backslash then flip the variable 128 backslash_encountered = !backslash_encountered; 129 } 130 else if (*args == '"') 131 { 132 if (quotes_opened) 133 { 134 if (*(args + 1) != '"') 135 { 136 quotes_opened = false; 137 } 138 else 139 { 140 args++; 141 } 142 } 143 else 144 { 145 quotes_opened = true; 146 } 147 148 backslash_encountered = false; 149 } 150 else 151 { 152 backslash_encountered = false; 153 if (*args == ' ' && !quotes_opened && curArg != argNum) 154 break; 155 } 156 157 if (curArg == argNum) 158 { 159 used++; 160 if (used < len) 161 *res++ = *args; 162 } 163 164 args++; 165 } 166 } 167 } 168 169 /*********************************************************************** 170 * SHELL_ArgifyW [Internal] 171 * 172 * this function is supposed to expand the escape sequences found in the registry 173 * some diving reported that the following were used: 174 * + %1, %2... seem to report to parameter of index N in ShellExecute pmts 175 * %1 file 176 * %2 printer 177 * %3 driver 178 * %4 port 179 * %I address of a global item ID (explorer switch /idlist) 180 * %L seems to be %1 as long filename followed by the 8+3 variation 181 * %S ??? 182 * %W Working directory 183 * %V Use either %L or %W 184 * %* all following parameters (see batfile) 185 * 186 * The way we parse the command line arguments was determined through extensive 187 * testing and can be summed up by the following rules" 188 * 189 * - %2 190 * - if first letter is " break on first non literal " and include any white spaces 191 * - if first letter is NOT " break on first " or white space 192 * - if " is opened any pair of consecutive " results in ONE literal " 193 * 194 * - %~2 195 * - use rules from here http://www.autohotkey.net/~deleyd/parameters/parameters.htm 196 */ 197 198 static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len, const WCHAR* lpDir) 199 { 200 WCHAR xlpFile[1024]; 201 BOOL done = FALSE; 202 BOOL found_p1 = FALSE; 203 PWSTR res = out; 204 PCWSTR cmd; 205 DWORD used = 0; 206 bool tildeEffect = false; 207 208 TRACE("Before parsing: %p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt), 209 debugstr_w(lpFile), pidl, args); 210 211 while (*fmt) 212 { 213 if (*fmt == '%') 214 { 215 switch (*++fmt) 216 { 217 case '\0': 218 case '%': 219 { 220 used++; 221 if (used < len) 222 *res++ = '%'; 223 }; 224 break; 225 226 case '*': 227 { 228 if (args) 229 { 230 if (*fmt == '*') 231 { 232 used++; 233 while(*args) 234 { 235 used++; 236 if (used < len) 237 *res++ = *args++; 238 else 239 args++; 240 } 241 used++; 242 break; 243 } 244 } 245 }; 246 break; 247 248 case '~': 249 250 case '2': 251 case '3': 252 case '4': 253 case '5': 254 case '6': 255 case '7': 256 case '8': 257 case '9': 258 //case '0': 259 { 260 if (*fmt == '~') 261 { 262 fmt++; 263 tildeEffect = true; 264 } 265 266 if (args) 267 { 268 if (tildeEffect) 269 { 270 ParseTildeEffect(res, args, len, used, *fmt - '2'); 271 tildeEffect = false; 272 } 273 else 274 { 275 ParseNoTildeEffect(res, args, len, used, *fmt - '2'); 276 } 277 } 278 }; 279 break; 280 281 case '1': 282 if (!done || (*fmt == '1')) 283 { 284 /*FIXME Is the call to SearchPathW() really needed? We already have separated out the parameter string in args. */ 285 if (SearchPathW(lpDir, lpFile, L".exe", ARRAY_SIZE(xlpFile), xlpFile, NULL)) 286 cmd = xlpFile; 287 else 288 cmd = lpFile; 289 290 SIZE_T cmdlen = wcslen(cmd); 291 used += cmdlen; 292 if (used < len) 293 { 294 wcscpy(res, cmd); 295 res += cmdlen; 296 } 297 } 298 found_p1 = TRUE; 299 break; 300 301 /* 302 * IE uses this a lot for activating things such as windows media 303 * player. This is not verified to be fully correct but it appears 304 * to work just fine. 305 */ 306 case 'l': 307 case 'L': 308 if (lpFile) 309 { 310 used += wcslen(lpFile); 311 if (used < len) 312 { 313 wcscpy(res, lpFile); 314 res += wcslen(lpFile); 315 } 316 } 317 found_p1 = TRUE; 318 break; 319 320 case 'w': 321 case 'W': 322 if (lpDir) 323 { 324 used += wcslen(lpDir); 325 if (used < len) 326 { 327 wcscpy(res, lpDir); 328 res += wcslen(lpDir); 329 } 330 } 331 break; 332 333 case 'v': 334 case 'V': 335 if (lpFile) 336 { 337 used += wcslen(lpFile); 338 if (used < len) 339 { 340 wcscpy(res, lpFile); 341 res += wcslen(lpFile); 342 } 343 found_p1 = TRUE; 344 } 345 else if (lpDir) 346 { 347 used += wcslen(lpDir); 348 if (used < len) 349 { 350 wcscpy(res, lpDir); 351 res += wcslen(lpDir); 352 } 353 } 354 break; 355 356 case 'i': 357 case 'I': 358 if (pidl) 359 { 360 DWORD chars = 0; 361 /* %p should not exceed 8, maybe 16 when looking forward to 64bit. 362 * allowing a buffer of 100 should more than exceed all needs */ 363 WCHAR buf[100]; 364 LPVOID pv; 365 HGLOBAL hmem = SHAllocShared(pidl, ILGetSize(pidl), 0); 366 pv = SHLockShared(hmem, 0); 367 chars = swprintf(buf, L":%p", pv); 368 369 if (chars >= ARRAY_SIZE(buf)) 370 ERR("pidl format buffer too small!\n"); 371 372 used += chars; 373 374 if (used < len) 375 { 376 wcscpy(res, buf); 377 res += chars; 378 } 379 SHUnlockShared(pv); 380 } 381 found_p1 = TRUE; 382 break; 383 384 default: 385 /* 386 * Check if this is an env-variable here... 387 */ 388 389 /* Make sure that we have at least one more %.*/ 390 if (strchrW(fmt, '%')) 391 { 392 WCHAR tmpBuffer[1024]; 393 PWSTR tmpB = tmpBuffer; 394 WCHAR tmpEnvBuff[MAX_PATH]; 395 DWORD envRet; 396 397 while (*fmt != '%') 398 *tmpB++ = *fmt++; 399 *tmpB++ = 0; 400 401 TRACE("Checking %s to be an env-var\n", debugstr_w(tmpBuffer)); 402 403 envRet = GetEnvironmentVariableW(tmpBuffer, tmpEnvBuff, MAX_PATH); 404 if (envRet == 0 || envRet > MAX_PATH) 405 { 406 used += wcslen(tmpBuffer); 407 if (used < len) 408 { 409 wcscpy( res, tmpBuffer ); 410 res += wcslen(tmpBuffer); 411 } 412 } 413 else 414 { 415 used += wcslen(tmpEnvBuff); 416 if (used < len) 417 { 418 wcscpy( res, tmpEnvBuff ); 419 res += wcslen(tmpEnvBuff); 420 } 421 } 422 } 423 done = TRUE; 424 break; 425 } 426 /* Don't skip past terminator (catch a single '%' at the end) */ 427 if (*fmt != '\0') 428 { 429 fmt++; 430 } 431 } 432 else 433 { 434 used ++; 435 if (used < len) 436 *res++ = *fmt++; 437 else 438 fmt++; 439 } 440 } 441 442 used++; 443 if (res - out < static_cast<int>(len)) 444 *res = '\0'; 445 else 446 out[len-1] = '\0'; 447 448 TRACE("used %i of %i space\n", used, len); 449 if (out_len) 450 *out_len = used; 451 452 TRACE("After parsing: %p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt), 453 debugstr_w(lpFile), pidl, args); 454 455 return found_p1; 456 } 457 458 static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize) 459 { 460 STRRET strret; 461 CComPtr<IShellFolder> desktop; 462 463 HRESULT hr = SHGetDesktopFolder(&desktop); 464 465 if (SUCCEEDED(hr)) 466 { 467 hr = desktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strret); 468 469 if (SUCCEEDED(hr)) 470 StrRetToStrNW(pszPath, uOutSize, &strret, pidl); 471 } 472 473 return hr; 474 } 475 476 /************************************************************************* 477 * SHELL_ExecuteW [Internal] 478 * 479 */ 480 static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, 481 const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out) 482 { 483 STARTUPINFOW startup; 484 PROCESS_INFORMATION info; 485 UINT_PTR retval = SE_ERR_NOASSOC; 486 UINT gcdret = 0; 487 WCHAR curdir[MAX_PATH]; 488 DWORD dwCreationFlags; 489 const WCHAR *lpDirectory = NULL; 490 491 TRACE("Execute %s from directory %s\n", debugstr_w(lpCmd), debugstr_w(psei->lpDirectory)); 492 493 /* make sure we don't fail the CreateProcess if the calling app passes in 494 * a bad working directory */ 495 if (!StrIsNullOrEmpty(psei->lpDirectory)) 496 { 497 DWORD attr = GetFileAttributesW(psei->lpDirectory); 498 if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY) 499 lpDirectory = psei->lpDirectory; 500 } 501 502 /* ShellExecute specifies the command from psei->lpDirectory 503 * if present. Not from the current dir as CreateProcess does */ 504 if (lpDirectory) 505 if ((gcdret = GetCurrentDirectoryW( MAX_PATH, curdir))) 506 if (!SetCurrentDirectoryW( lpDirectory)) 507 ERR("cannot set directory %s\n", debugstr_w(lpDirectory)); 508 509 ZeroMemory(&startup, sizeof(STARTUPINFOW)); 510 startup.cb = sizeof(STARTUPINFOW); 511 startup.dwFlags = STARTF_USESHOWWINDOW; 512 startup.wShowWindow = psei->nShow; 513 dwCreationFlags = CREATE_UNICODE_ENVIRONMENT; 514 if (!(psei->fMask & SEE_MASK_NO_CONSOLE)) 515 dwCreationFlags |= CREATE_NEW_CONSOLE; 516 if (psei->fMask & SEE_MASK_FLAG_SEPVDM) 517 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; 518 startup.lpTitle = (LPWSTR)(psei->fMask & (SEE_MASK_HASLINKNAME | SEE_MASK_HASTITLE) ? psei->lpClass : NULL); 519 520 if (psei->fMask & SEE_MASK_HASLINKNAME) 521 startup.dwFlags |= STARTF_TITLEISLINKNAME; 522 523 if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env, 524 lpDirectory, &startup, &info)) 525 { 526 /* Give 30 seconds to the app to come up, if desired. Probably only needed 527 when starting app immediately before making a DDE connection. */ 528 if (shWait) 529 if (WaitForInputIdle(info.hProcess, 30000) == WAIT_FAILED) 530 WARN("WaitForInputIdle failed: Error %d\n", GetLastError() ); 531 retval = 33; 532 533 if (psei->fMask & SEE_MASK_NOCLOSEPROCESS) 534 psei_out->hProcess = info.hProcess; 535 else 536 CloseHandle( info.hProcess ); 537 CloseHandle( info.hThread ); 538 } 539 else if ((retval = GetLastError()) >= 32) 540 { 541 WARN("CreateProcess returned error %ld\n", retval); 542 retval = ERROR_BAD_FORMAT; 543 } 544 545 TRACE("returning %lu\n", retval); 546 547 psei_out->hInstApp = (HINSTANCE)retval; 548 549 if (gcdret) 550 if (!SetCurrentDirectoryW(curdir)) 551 ERR("cannot return to directory %s\n", debugstr_w(curdir)); 552 553 return retval; 554 } 555 556 557 /*********************************************************************** 558 * SHELL_BuildEnvW [Internal] 559 * 560 * Build the environment for the new process, adding the specified 561 * path to the PATH variable. Returned pointer must be freed by caller. 562 */ 563 static LPWSTR SHELL_BuildEnvW( const WCHAR *path ) 564 { 565 CHeapPtr<WCHAR, CLocalAllocator> new_env; 566 WCHAR *strings, *p, *p2; 567 int total = wcslen(path) + 1; 568 BOOL got_path = FALSE; 569 570 if (!(strings = GetEnvironmentStringsW())) return NULL; 571 p = strings; 572 while (*p) 573 { 574 int len = wcslen(p) + 1; 575 if (!_wcsnicmp( p, L"PATH=", 5 )) got_path = TRUE; 576 total += len; 577 p += len; 578 } 579 if (!got_path) total += 5; /* we need to create PATH */ 580 total++; /* terminating null */ 581 582 if (!new_env.Allocate(total)) 583 { 584 FreeEnvironmentStringsW(strings); 585 return NULL; 586 } 587 p = strings; 588 p2 = new_env; 589 while (*p) 590 { 591 int len = wcslen(p) + 1; 592 memcpy(p2, p, len * sizeof(WCHAR)); 593 if (!_wcsnicmp( p, L"PATH=", 5 )) 594 { 595 p2[len - 1] = ';'; 596 wcscpy( p2 + len, path ); 597 p2 += wcslen(path) + 1; 598 } 599 p += len; 600 p2 += len; 601 } 602 if (!got_path) 603 { 604 wcscpy(p2, L"PATH="); 605 wcscat(p2, path); 606 p2 += wcslen(p2) + 1; 607 } 608 *p2 = 0; 609 FreeEnvironmentStringsW(strings); 610 return new_env.Detach(); 611 } 612 613 /*********************************************************************** 614 * SHELL_TryAppPathW [Internal] 615 * 616 * Helper function for SHELL_FindExecutable 617 * @param lpResult - pointer to a buffer of size MAX_PATH 618 * On entry: szName is a filename (probably without path separators). 619 * On exit: if szName found in "App Path", place full path in lpResult, and return true 620 */ 621 static BOOL SHELL_TryAppPathW( LPCWSTR szName, LPWSTR lpResult, WCHAR **env) 622 { 623 HKEY hkApp = NULL; 624 WCHAR buffer[1024]; 625 DWORD len, dwType; 626 LONG res; 627 BOOL found = FALSE; 628 629 if (env) *env = NULL; 630 wcscpy(buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); 631 wcscat(buffer, szName); 632 res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); 633 if (res) 634 { 635 // Add ".exe" extension, if extension does not exists 636 if (PathAddExtensionW(buffer, L".exe")) 637 { 638 res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); 639 } 640 if (res) goto end; 641 } 642 643 len = MAX_PATH * sizeof(WCHAR); 644 res = SHRegQueryValueExW(hkApp, NULL, NULL, &dwType, (LPBYTE)lpResult, &len); 645 if (res != ERROR_SUCCESS || dwType != REG_SZ) 646 goto end; 647 648 found = TRUE; 649 650 if (env) 651 { 652 len = sizeof(buffer); 653 res = SHRegQueryValueExW(hkApp, L"Path", NULL, &dwType, (LPBYTE)buffer, &len); 654 if (res == ERROR_SUCCESS && dwType == REG_SZ && buffer[0]) 655 *env = SHELL_BuildEnvW(buffer); 656 } 657 658 end: 659 if (hkApp) RegCloseKey(hkApp); 660 return found; 661 } 662 663 /************************************************************************* 664 * SHELL_FindExecutableByVerb [Internal] 665 * 666 * called from SHELL_FindExecutable or SHELL_execute_class 667 * in/out: 668 * classname a buffer, big enough, to get the key name to do actually the 669 * command "WordPad.Document.1\\shell\\open\\command" 670 * passed as "WordPad.Document.1" 671 * in: 672 * lpVerb the operation on it (open) 673 * commandlen the size of command buffer (in bytes) 674 * out: 675 * command a buffer, to store the command to do the 676 * operation on the file 677 * key a buffer, big enough, to get the key name to do actually the 678 * command "WordPad.Document.1\\shell\\open\\command" 679 * Can be NULL 680 */ 681 static UINT SHELL_FindExecutableByVerb(LPCWSTR lpVerb, LPWSTR key, LPWSTR classname, LPWSTR command, LONG commandlen) 682 { 683 HKEY hkeyClass; 684 WCHAR verb[MAX_PATH]; 685 686 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, 0x02000000, &hkeyClass)) 687 return SE_ERR_NOASSOC; 688 if (!HCR_GetDefaultVerbW(hkeyClass, lpVerb, verb, ARRAY_SIZE(verb))) 689 return SE_ERR_NOASSOC; 690 RegCloseKey(hkeyClass); 691 692 /* Looking for ...buffer\shell\<verb>\command */ 693 wcscat(classname, L"\\shell\\"); 694 wcscat(classname, verb); 695 wcscat(classname, L"\\command"); 696 697 if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, command, 698 &commandlen) == ERROR_SUCCESS) 699 { 700 commandlen /= sizeof(WCHAR); 701 if (key) wcscpy(key, classname); 702 #if 0 703 LPWSTR tmp; 704 WCHAR param[256]; 705 LONG paramlen = sizeof(param); 706 707 /* FIXME: it seems all Windows version don't behave the same here. 708 * the doc states that this ddeexec information can be found after 709 * the exec names. 710 * on Win98, it doesn't appear, but I think it does on Win2k 711 */ 712 /* Get the parameters needed by the application 713 from the associated ddeexec key */ 714 tmp = strstrW(classname, L"\\command"); 715 tmp[0] = '\0'; 716 wcscat(classname, wDdeexec); 717 if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, param, 718 ¶mlen) == ERROR_SUCCESS) 719 { 720 paramlen /= sizeof(WCHAR); 721 wcscat(command, L" "); 722 wcscat(command, param); 723 commandlen += paramlen; 724 } 725 #endif 726 727 command[commandlen] = '\0'; 728 729 return 33; /* FIXME see SHELL_FindExecutable() */ 730 } 731 732 return SE_ERR_NOASSOC; 733 } 734 735 /************************************************************************* 736 * SHELL_FindExecutable [Internal] 737 * 738 * Utility for code sharing between FindExecutable and ShellExecute 739 * in: 740 * lpFile the name of a file 741 * lpVerb the operation on it (open) 742 * out: 743 * lpResult a buffer, big enough :-(, to store the command to do the 744 * operation on the file 745 * key a buffer, big enough, to get the key name to do actually the 746 * command (it'll be used afterwards for more information 747 * on the operation) 748 */ 749 static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, 750 LPWSTR lpResult, DWORD resultLen, LPWSTR key, WCHAR **env, LPITEMIDLIST pidl, LPCWSTR args) 751 { 752 WCHAR *extension = NULL; /* pointer to file extension */ 753 WCHAR classname[256]; /* registry name for this file type */ 754 LONG classnamelen = sizeof(classname); /* length of above */ 755 WCHAR command[1024]; /* command from registry */ 756 WCHAR wBuffer[256]; /* Used to GetProfileString */ 757 UINT retval = SE_ERR_NOASSOC; 758 WCHAR *tok; /* token pointer */ 759 WCHAR xlpFile[256]; /* result of SearchPath */ 760 DWORD attribs; /* file attributes */ 761 762 TRACE("%s\n", debugstr_w(lpFile)); 763 764 if (!lpResult) 765 return ERROR_INVALID_PARAMETER; 766 767 xlpFile[0] = '\0'; 768 lpResult[0] = '\0'; /* Start off with an empty return string */ 769 if (key) *key = '\0'; 770 771 /* trap NULL parameters on entry */ 772 if (!lpFile) 773 { 774 WARN("(lpFile=%s,lpResult=%s): NULL parameter\n", 775 debugstr_w(lpFile), debugstr_w(lpResult)); 776 return ERROR_FILE_NOT_FOUND; /* File not found. Close enough, I guess. */ 777 } 778 779 if (SHELL_TryAppPathW( lpFile, lpResult, env )) 780 { 781 TRACE("found %s via App Paths\n", debugstr_w(lpResult)); 782 return 33; 783 } 784 785 if (SearchPathW(lpPath, lpFile, L".exe", ARRAY_SIZE(xlpFile), xlpFile, NULL)) 786 { 787 TRACE("SearchPathW returned non-zero\n"); 788 lpFile = xlpFile; 789 /* The file was found in the application-supplied default directory (or the system search path) */ 790 } 791 else if (lpPath && SearchPathW(NULL, lpFile, L".exe", ARRAY_SIZE(xlpFile), xlpFile, NULL)) 792 { 793 TRACE("SearchPathW returned non-zero\n"); 794 lpFile = xlpFile; 795 /* The file was found in one of the directories in the system-wide search path */ 796 } 797 798 attribs = GetFileAttributesW(lpFile); 799 if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY)) 800 { 801 wcscpy(classname, L"Folder"); 802 } 803 else 804 { 805 /* Did we get something? Anything? */ 806 if (xlpFile[0] == 0) 807 { 808 TRACE("Returning SE_ERR_FNF\n"); 809 return SE_ERR_FNF; 810 } 811 /* First thing we need is the file's extension */ 812 extension = wcsrchr(xlpFile, '.'); /* Assume last "." is the one; */ 813 /* File->Run in progman uses */ 814 /* .\FILE.EXE :( */ 815 TRACE("xlpFile=%s,extension=%s\n", debugstr_w(xlpFile), debugstr_w(extension)); 816 817 if (extension == NULL || extension[1] == 0) 818 { 819 WARN("Returning SE_ERR_NOASSOC\n"); 820 return SE_ERR_NOASSOC; 821 } 822 823 /* Three places to check: */ 824 /* 1. win.ini, [windows], programs (NB no leading '.') */ 825 /* 2. Registry, HKEY_CLASS_ROOT\<classname>\shell\open\command */ 826 /* 3. win.ini, [extensions], extension (NB no leading '.' */ 827 /* All I know of the order is that registry is checked before */ 828 /* extensions; however, it'd make sense to check the programs */ 829 /* section first, so that's what happens here. */ 830 831 /* See if it's a program - if GetProfileString fails, we skip this 832 * section. Actually, if GetProfileString fails, we've probably 833 * got a lot more to worry about than running a program... */ 834 if (GetProfileStringW(L"windows", L"programs", L"exe pif bat cmd com", wBuffer, ARRAY_SIZE(wBuffer)) > 0) 835 { 836 CharLowerW(wBuffer); 837 tok = wBuffer; 838 while (*tok) 839 { 840 WCHAR *p = tok; 841 while (*p && *p != ' ' && *p != '\t') p++; 842 if (*p) 843 { 844 *p++ = 0; 845 while (*p == ' ' || *p == '\t') p++; 846 } 847 848 if (_wcsicmp(tok, &extension[1]) == 0) /* have to skip the leading "." */ 849 { 850 wcscpy(lpResult, xlpFile); 851 /* Need to perhaps check that the file has a path 852 * attached */ 853 TRACE("found %s\n", debugstr_w(lpResult)); 854 return 33; 855 /* Greater than 32 to indicate success */ 856 } 857 tok = p; 858 } 859 } 860 861 /* Check registry */ 862 if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, classname, 863 &classnamelen) == ERROR_SUCCESS) 864 { 865 classnamelen /= sizeof(WCHAR); 866 if (classnamelen == ARRAY_SIZE(classname)) 867 classnamelen--; 868 869 classname[classnamelen] = '\0'; 870 TRACE("File type: %s\n", debugstr_w(classname)); 871 } 872 else 873 { 874 *classname = '\0'; 875 } 876 } 877 878 if (*classname) 879 { 880 /* pass the verb string to SHELL_FindExecutableByVerb() */ 881 retval = SHELL_FindExecutableByVerb(lpVerb, key, classname, command, sizeof(command)); 882 883 if (retval > 32) 884 { 885 DWORD finishedLen; 886 SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen, lpPath); 887 if (finishedLen > resultLen) 888 ERR("Argify buffer not large enough.. truncated\n"); 889 /* Remove double quotation marks and command line arguments */ 890 if (*lpResult == '"') 891 { 892 WCHAR *p = lpResult; 893 while (*(p + 1) != '"') 894 { 895 *p = *(p + 1); 896 p++; 897 } 898 *p = '\0'; 899 } 900 else 901 { 902 /* Truncate on first space */ 903 WCHAR *p = lpResult; 904 while (*p != ' ' && *p != '\0') 905 p++; 906 *p = '\0'; 907 } 908 } 909 } 910 else /* Check win.ini */ 911 { 912 /* Toss the leading dot */ 913 extension++; 914 if (GetProfileStringW(L"extensions", extension, L"", command, ARRAY_SIZE(command)) > 0) 915 { 916 if (wcslen(command) != 0) 917 { 918 wcscpy(lpResult, command); 919 tok = wcschr(lpResult, '^'); /* should be ^.extension? */ 920 if (tok != NULL) 921 { 922 tok[0] = '\0'; 923 wcscat(lpResult, xlpFile); /* what if no dir in xlpFile? */ 924 tok = wcschr(command, '^'); /* see above */ 925 if ((tok != NULL) && (wcslen(tok) > 5)) 926 { 927 wcscat(lpResult, &tok[5]); 928 } 929 } 930 retval = 33; /* FIXME - see above */ 931 } 932 } 933 } 934 935 TRACE("returning %s\n", debugstr_w(lpResult)); 936 return retval; 937 } 938 939 /****************************************************************** 940 * dde_cb 941 * 942 * callback for the DDE connection. not really useful 943 */ 944 static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv, 945 HSZ hsz1, HSZ hsz2, HDDEDATA hData, 946 ULONG_PTR dwData1, ULONG_PTR dwData2) 947 { 948 TRACE("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n", 949 uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2); 950 return NULL; 951 } 952 953 /****************************************************************** 954 * dde_connect 955 * 956 * ShellExecute helper. Used to do an operation with a DDE connection 957 * 958 * Handles both the direct connection (try #1), and if it fails, 959 * launching an application and trying (#2) to connect to it 960 * 961 */ 962 static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec, 963 const WCHAR* lpFile, WCHAR *env, 964 LPCWSTR szCommandline, LPITEMIDLIST pidl, SHELL_ExecuteW32 execfunc, 965 const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out) 966 { 967 WCHAR regkey[256]; 968 WCHAR * endkey = regkey + wcslen(key); 969 WCHAR app[256], topic[256], ifexec[256], static_res[256]; 970 CHeapPtr<WCHAR, CLocalAllocator> dynamic_res; 971 WCHAR * res; 972 LONG applen, topiclen, ifexeclen; 973 WCHAR * exec; 974 DWORD ddeInst = 0; 975 DWORD tid; 976 DWORD resultLen, endkeyLen; 977 HSZ hszApp, hszTopic; 978 HCONV hConv; 979 HDDEDATA hDdeData; 980 unsigned ret = SE_ERR_NOASSOC; 981 BOOL unicode = !(GetVersion() & 0x80000000); 982 983 if (strlenW(key) + 1 > ARRAY_SIZE(regkey)) 984 { 985 FIXME("input parameter %s larger than buffer\n", debugstr_w(key)); 986 return 2; 987 } 988 wcscpy(regkey, key); 989 endkeyLen = ARRAY_SIZE(regkey) - (endkey - regkey); 990 if (strlenW(L"\\application") + 1 > endkeyLen) 991 { 992 FIXME("endkey %s overruns buffer\n", debugstr_w(L"\\application")); 993 return 2; 994 } 995 wcscpy(endkey, L"\\application"); 996 applen = sizeof(app); 997 if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, app, &applen) != ERROR_SUCCESS) 998 { 999 WCHAR command[1024], fullpath[MAX_PATH]; 1000 LPWSTR ptr = NULL; 1001 DWORD ret = 0; 1002 1003 /* Get application command from start string and find filename of application */ 1004 if (*start == '"') 1005 { 1006 if (strlenW(start + 1) + 1 > ARRAY_SIZE(command)) 1007 { 1008 FIXME("size of input parameter %s larger than buffer\n", 1009 debugstr_w(start + 1)); 1010 return 2; 1011 } 1012 wcscpy(command, start + 1); 1013 if ((ptr = wcschr(command, '"'))) 1014 * ptr = 0; 1015 ret = SearchPathW(NULL, command, L".exe", ARRAY_SIZE(fullpath), fullpath, &ptr); 1016 } 1017 else 1018 { 1019 LPCWSTR p; 1020 LPWSTR space; 1021 for (p = start; (space = const_cast<LPWSTR>(strchrW(p, ' '))); p = space + 1) 1022 { 1023 int idx = space - start; 1024 memcpy(command, start, idx * sizeof(WCHAR)); 1025 command[idx] = '\0'; 1026 if ((ret = SearchPathW(NULL, command, L".exe", ARRAY_SIZE(fullpath), fullpath, &ptr))) 1027 break; 1028 } 1029 if (!ret) 1030 ret = SearchPathW(NULL, start, L".exe", ARRAY_SIZE(fullpath), fullpath, &ptr); 1031 } 1032 1033 if (!ret) 1034 { 1035 ERR("Unable to find application path for command %s\n", debugstr_w(start)); 1036 return ERROR_ACCESS_DENIED; 1037 } 1038 if (strlenW(ptr) + 1 > ARRAY_SIZE(app)) 1039 { 1040 FIXME("size of found path %s larger than buffer\n", debugstr_w(ptr)); 1041 return 2; 1042 } 1043 wcscpy(app, ptr); 1044 1045 /* Remove extensions (including .so) */ 1046 ptr = app + wcslen(app) - 3; 1047 if (ptr > app && !wcscmp(ptr, L".so")) 1048 *ptr = 0; 1049 1050 ptr = const_cast<LPWSTR>(strrchrW(app, '.')); 1051 assert(ptr); 1052 *ptr = 0; 1053 } 1054 1055 if (strlenW(L"\\topic") + 1 > endkeyLen) 1056 { 1057 FIXME("endkey %s overruns buffer\n", debugstr_w(L"\\topic")); 1058 return 2; 1059 } 1060 wcscpy(endkey, L"\\topic"); 1061 topiclen = sizeof(topic); 1062 if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, topic, &topiclen) != ERROR_SUCCESS) 1063 { 1064 wcscpy(topic, L"System"); 1065 } 1066 1067 if (unicode) 1068 { 1069 if (DdeInitializeW(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR) 1070 return 2; 1071 } 1072 else 1073 { 1074 if (DdeInitializeA(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR) 1075 return 2; 1076 } 1077 1078 hszApp = DdeCreateStringHandleW(ddeInst, app, CP_WINUNICODE); 1079 hszTopic = DdeCreateStringHandleW(ddeInst, topic, CP_WINUNICODE); 1080 1081 hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL); 1082 exec = ddeexec; 1083 if (!hConv) 1084 { 1085 TRACE("Launching %s\n", debugstr_w(start)); 1086 ret = execfunc(start, env, TRUE, psei, psei_out); 1087 if (ret <= 32) 1088 { 1089 TRACE("Couldn't launch\n"); 1090 goto error; 1091 } 1092 /* if ddeexec is NULL, then we just need to exit here */ 1093 if (ddeexec == NULL) 1094 { 1095 TRACE("Exiting because ddeexec is NULL. ret=42.\n"); 1096 /* See https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew */ 1097 /* for reason why we use 42 here and also "Shell32_apitest ShellExecuteW" regression test */ 1098 return 42; 1099 } 1100 /* if ddeexec is 'empty string', then we just need to exit here */ 1101 if (wcscmp(ddeexec, L"") == 0) 1102 { 1103 TRACE("Exiting because ddeexec is 'empty string'. ret=42.\n"); 1104 /* See https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew */ 1105 /* for reason why we use 42 here and also "Shell32_apitest ShellExecuteW" regression test */ 1106 return 42; 1107 } 1108 hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL); 1109 if (!hConv) 1110 { 1111 TRACE("Couldn't connect. ret=%d\n", ret); 1112 DdeUninitialize(ddeInst); 1113 SetLastError(ERROR_DDE_FAIL); 1114 return 30; /* whatever */ 1115 } 1116 if (strlenW(L"\\ifexec") + 1 > endkeyLen) 1117 { 1118 FIXME("endkey %s overruns buffer\n", debugstr_w(L"\\ifexec")); 1119 return 2; 1120 } 1121 strcpyW(endkey, L"\\ifexec"); 1122 ifexeclen = sizeof(ifexec); 1123 if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, ifexec, &ifexeclen) == ERROR_SUCCESS) 1124 { 1125 exec = ifexec; 1126 } 1127 } 1128 1129 SHELL_ArgifyW(static_res, ARRAY_SIZE(static_res), exec, lpFile, pidl, szCommandline, &resultLen, NULL); 1130 if (resultLen > ARRAY_SIZE(static_res)) 1131 { 1132 dynamic_res.Allocate(resultLen); 1133 res = dynamic_res; 1134 SHELL_ArgifyW(dynamic_res, resultLen, exec, lpFile, pidl, szCommandline, NULL, NULL); 1135 } 1136 else 1137 res = static_res; 1138 TRACE("%s %s => %s\n", debugstr_w(exec), debugstr_w(lpFile), debugstr_w(res)); 1139 1140 /* It's documented in the KB 330337 that IE has a bug and returns 1141 * error DMLERR_NOTPROCESSED on XTYP_EXECUTE request. 1142 */ 1143 if (unicode) 1144 hDdeData = DdeClientTransaction((LPBYTE)res, (strlenW(res) + 1) * sizeof(WCHAR), hConv, 0L, 0, XTYP_EXECUTE, 30000, &tid); 1145 else 1146 { 1147 DWORD lenA = WideCharToMultiByte(CP_ACP, 0, res, -1, NULL, 0, NULL, NULL); 1148 CHeapPtr<char, CLocalAllocator> resA; 1149 resA.Allocate(lenA); 1150 WideCharToMultiByte(CP_ACP, 0, res, -1, resA, lenA, NULL, NULL); 1151 hDdeData = DdeClientTransaction( (LPBYTE)(LPSTR)resA, lenA, hConv, 0L, 0, 1152 XTYP_EXECUTE, 10000, &tid ); 1153 } 1154 if (hDdeData) 1155 DdeFreeDataHandle(hDdeData); 1156 else 1157 WARN("DdeClientTransaction failed with error %04x\n", DdeGetLastError(ddeInst)); 1158 ret = 33; 1159 1160 DdeDisconnect(hConv); 1161 1162 error: 1163 DdeUninitialize(ddeInst); 1164 1165 return ret; 1166 } 1167 1168 /************************************************************************* 1169 * execute_from_key [Internal] 1170 */ 1171 static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, 1172 LPCWSTR szCommandline, LPCWSTR executable_name, 1173 SHELL_ExecuteW32 execfunc, 1174 LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out) 1175 { 1176 WCHAR cmd[256], param[1024], ddeexec[256]; 1177 DWORD cmdlen = sizeof(cmd), ddeexeclen = sizeof(ddeexec); 1178 UINT_PTR retval = SE_ERR_NOASSOC; 1179 DWORD resultLen; 1180 LPWSTR tmp; 1181 1182 TRACE("%s %s %s %s %s\n", debugstr_w(key), debugstr_w(lpFile), debugstr_w(env), 1183 debugstr_w(szCommandline), debugstr_w(executable_name)); 1184 1185 cmd[0] = '\0'; 1186 param[0] = '\0'; 1187 1188 /* Get the application from the registry */ 1189 if (RegQueryValueW(HKEY_CLASSES_ROOT, key, cmd, (LONG *)&cmdlen) == ERROR_SUCCESS) 1190 { 1191 TRACE("got cmd: %s\n", debugstr_w(cmd)); 1192 1193 /* Is there a replace() function anywhere? */ 1194 cmdlen /= sizeof(WCHAR); 1195 if (cmdlen >= ARRAY_SIZE(cmd)) 1196 cmdlen = ARRAY_SIZE(cmd) - 1; 1197 cmd[cmdlen] = '\0'; 1198 SHELL_ArgifyW(param, ARRAY_SIZE(param), cmd, lpFile, (LPITEMIDLIST)psei->lpIDList, szCommandline, &resultLen, 1199 (psei->lpDirectory && *psei->lpDirectory) ? psei->lpDirectory : NULL); 1200 if (resultLen > ARRAY_SIZE(param)) 1201 ERR("Argify buffer not large enough, truncating\n"); 1202 } 1203 1204 /* Get the parameters needed by the application 1205 from the associated ddeexec key */ 1206 tmp = const_cast<LPWSTR>(strstrW(key, L"command")); 1207 assert(tmp); 1208 wcscpy(tmp, L"ddeexec"); 1209 1210 if (RegQueryValueW(HKEY_CLASSES_ROOT, key, ddeexec, (LONG *)&ddeexeclen) == ERROR_SUCCESS) 1211 { 1212 TRACE("Got ddeexec %s => %s\n", debugstr_w(key), debugstr_w(ddeexec)); 1213 if (!param[0]) strcpyW(param, executable_name); 1214 retval = dde_connect(key, param, ddeexec, lpFile, env, szCommandline, (LPITEMIDLIST)psei->lpIDList, execfunc, psei, psei_out); 1215 } 1216 else if (param[0]) 1217 { 1218 TRACE("executing: %s\n", debugstr_w(param)); 1219 retval = execfunc(param, env, FALSE, psei, psei_out); 1220 } 1221 else 1222 WARN("Nothing appropriate found for %s\n", debugstr_w(key)); 1223 1224 return retval; 1225 } 1226 1227 /************************************************************************* 1228 * FindExecutableA [SHELL32.@] 1229 */ 1230 HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult) 1231 { 1232 HINSTANCE retval; 1233 WCHAR *wFile = NULL, *wDirectory = NULL; 1234 WCHAR wResult[MAX_PATH]; 1235 1236 if (lpFile) __SHCloneStrAtoW(&wFile, lpFile); 1237 if (lpDirectory) __SHCloneStrAtoW(&wDirectory, lpDirectory); 1238 1239 retval = FindExecutableW(wFile, wDirectory, wResult); 1240 WideCharToMultiByte(CP_ACP, 0, wResult, -1, lpResult, MAX_PATH, NULL, NULL); 1241 SHFree(wFile); 1242 SHFree(wDirectory); 1243 1244 TRACE("returning %s\n", lpResult); 1245 return retval; 1246 } 1247 1248 /************************************************************************* 1249 * FindExecutableW [SHELL32.@] 1250 * 1251 * This function returns the executable associated with the specified file 1252 * for the default verb. 1253 * 1254 * PARAMS 1255 * lpFile [I] The file to find the association for. This must refer to 1256 * an existing file otherwise FindExecutable fails and returns 1257 * SE_ERR_FNF. 1258 * lpResult [O] Points to a buffer into which the executable path is 1259 * copied. This parameter must not be NULL otherwise 1260 * FindExecutable() segfaults. The buffer must be of size at 1261 * least MAX_PATH characters. 1262 * 1263 * RETURNS 1264 * A value greater than 32 on success, less than or equal to 32 otherwise. 1265 * See the SE_ERR_* constants. 1266 * 1267 * NOTES 1268 * On Windows XP and 2003, FindExecutable() seems to first convert the 1269 * filename into 8.3 format, thus taking into account only the first three 1270 * characters of the extension, and expects to find an association for those. 1271 * However other Windows versions behave sanely. 1272 */ 1273 HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult) 1274 { 1275 UINT_PTR retval; 1276 WCHAR old_dir[MAX_PATH], res[MAX_PATH]; 1277 DWORD cch = _countof(res); 1278 LPCWSTR dirs[2]; 1279 1280 TRACE("File %s, Dir %s\n", debugstr_w(lpFile), debugstr_w(lpDirectory)); 1281 1282 *lpResult = UNICODE_NULL; 1283 1284 GetCurrentDirectoryW(_countof(old_dir), old_dir); 1285 1286 if (lpDirectory && *lpDirectory) 1287 { 1288 SetCurrentDirectoryW(lpDirectory); 1289 dirs[0] = lpDirectory; 1290 } 1291 else 1292 { 1293 dirs[0] = old_dir; 1294 } 1295 dirs[1] = NULL; 1296 1297 if (!GetShortPathNameW(lpFile, res, _countof(res))) 1298 StringCchCopyW(res, _countof(res), lpFile); 1299 1300 if (PathResolveW(res, dirs, PRF_TRYPROGRAMEXTENSIONS | PRF_FIRSTDIRDEF)) 1301 { 1302 // NOTE: The last parameter of this AssocQueryStringW call is "strange" in Windows. 1303 if (PathIsExeW(res) || 1304 SUCCEEDED(AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_EXECUTABLE, res, NULL, res, &cch))) 1305 { 1306 StringCchCopyW(lpResult, MAX_PATH, res); 1307 retval = 42; 1308 } 1309 else 1310 { 1311 retval = SE_ERR_NOASSOC; 1312 } 1313 } 1314 else 1315 { 1316 retval = SE_ERR_FNF; 1317 } 1318 1319 TRACE("returning %s\n", debugstr_w(lpResult)); 1320 SetCurrentDirectoryW(old_dir); 1321 return (HINSTANCE)retval; 1322 } 1323 1324 /* FIXME: is this already implemented somewhere else? */ 1325 static HKEY ShellExecute_GetClassKey(const SHELLEXECUTEINFOW *sei) 1326 { 1327 LPCWSTR ext = NULL, lpClass = NULL; 1328 CHeapPtr<WCHAR, CLocalAllocator> cls; 1329 DWORD type = 0, sz = 0; 1330 HKEY hkey = 0; 1331 LONG r; 1332 1333 if (sei->fMask & SEE_MASK_CLASSALL) 1334 return sei->hkeyClass; 1335 1336 if (sei->fMask & SEE_MASK_CLASSNAME) 1337 lpClass = sei->lpClass; 1338 else 1339 { 1340 ext = PathFindExtensionW(sei->lpFile); 1341 TRACE("ext = %s\n", debugstr_w(ext)); 1342 if (!ext) 1343 return hkey; 1344 1345 r = RegOpenKeyW(HKEY_CLASSES_ROOT, ext, &hkey); 1346 if (r != ERROR_SUCCESS) 1347 return hkey; 1348 1349 r = RegQueryValueExW(hkey, NULL, 0, &type, NULL, &sz); 1350 if (r == ERROR_SUCCESS && type == REG_SZ) 1351 { 1352 sz += sizeof (WCHAR); 1353 cls.Allocate(sz / sizeof(WCHAR)); 1354 cls[0] = 0; 1355 RegQueryValueExW(hkey, NULL, 0, &type, (LPBYTE)(LPWSTR)cls, &sz); 1356 } 1357 1358 RegCloseKey( hkey ); 1359 lpClass = cls; 1360 } 1361 1362 TRACE("class = %s\n", debugstr_w(lpClass)); 1363 1364 hkey = 0; 1365 if (lpClass) 1366 RegOpenKeyW( HKEY_CLASSES_ROOT, lpClass, &hkey); 1367 1368 return hkey; 1369 } 1370 1371 static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei, CComPtr<IDataObject>& dataObj) 1372 { 1373 CComHeapPtr<ITEMIDLIST> allocatedPidl; 1374 LPITEMIDLIST pidl = NULL; 1375 1376 if (sei->fMask & SEE_MASK_CLASSALL) 1377 { 1378 pidl = (LPITEMIDLIST)sei->lpIDList; 1379 } 1380 else 1381 { 1382 WCHAR fullpath[MAX_PATH]; 1383 BOOL ret; 1384 1385 fullpath[0] = 0; 1386 ret = GetFullPathNameW(sei->lpFile, MAX_PATH, fullpath, NULL); 1387 if (!ret) 1388 return HRESULT_FROM_WIN32(GetLastError()); 1389 1390 pidl = ILCreateFromPathW(fullpath); 1391 allocatedPidl.Attach(pidl); 1392 } 1393 1394 CComPtr<IShellFolder> shf; 1395 LPCITEMIDLIST pidllast = NULL; 1396 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); 1397 if (FAILED_UNEXPECTEDLY(hr)) 1398 return hr; 1399 1400 return shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IDataObject, &dataObj)); 1401 } 1402 1403 static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, 1404 LPSHELLEXECUTEINFOW sei) 1405 { 1406 CComPtr<IContextMenu> cm = NULL; 1407 CMINVOKECOMMANDINFOEX ici; 1408 MENUITEMINFOW info; 1409 WCHAR string[0x80]; 1410 INT i, n, def = -1; 1411 HMENU hmenu = 0; 1412 HRESULT r; 1413 1414 TRACE("%p %p\n", obj, sei); 1415 1416 r = obj->QueryInterface(IID_PPV_ARG(IContextMenu, &cm)); 1417 if (FAILED(r)) 1418 return r; 1419 1420 hmenu = CreateMenu(); 1421 if (!hmenu) 1422 goto end; 1423 1424 /* the number of the last menu added is returned in r */ 1425 r = cm->QueryContextMenu(hmenu, 0, 0x20, 0x7fff, CMF_DEFAULTONLY); 1426 if (FAILED(r)) 1427 goto end; 1428 1429 n = GetMenuItemCount(hmenu); 1430 for (i = 0; i < n; i++) 1431 { 1432 memset(&info, 0, sizeof(info)); 1433 info.cbSize = sizeof info; 1434 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID; 1435 info.dwTypeData = string; 1436 info.cch = sizeof string; 1437 string[0] = 0; 1438 GetMenuItemInfoW(hmenu, i, TRUE, &info); 1439 1440 TRACE("menu %d %s %08x %08lx %08x %08x\n", i, debugstr_w(string), 1441 info.fState, info.dwItemData, info.fType, info.wID); 1442 if ((!sei->lpVerb && (info.fState & MFS_DEFAULT)) || 1443 (sei->lpVerb && !lstrcmpiW(sei->lpVerb, string))) 1444 { 1445 def = i; 1446 break; 1447 } 1448 } 1449 1450 r = E_FAIL; 1451 if (def == -1) 1452 goto end; 1453 1454 memset(&ici, 0, sizeof ici); 1455 ici.cbSize = sizeof ici; 1456 ici.fMask = CMIC_MASK_UNICODE | (sei->fMask & (SEE_MASK_NO_CONSOLE | SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); 1457 ici.nShow = sei->nShow; 1458 ici.lpVerb = MAKEINTRESOURCEA(def); 1459 ici.hwnd = sei->hwnd; 1460 ici.lpParametersW = sei->lpParameters; 1461 1462 r = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); 1463 1464 TRACE("invoke command returned %08x\n", r); 1465 1466 end: 1467 if (hmenu) 1468 DestroyMenu( hmenu ); 1469 return r; 1470 } 1471 1472 static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei) 1473 { 1474 TRACE("%p %s %p\n", hkey, debugstr_guid(guid), sei); 1475 1476 CCoInit coInit; 1477 1478 if (FAILED_UNEXPECTEDLY(coInit.hr)) 1479 return coInit.hr; 1480 1481 CComPtr<IShellExtInit> obj; 1482 HRESULT hr = CoCreateInstance(*guid, NULL, CLSCTX_INPROC_SERVER, 1483 IID_PPV_ARG(IShellExtInit, &obj)); 1484 if (FAILED_UNEXPECTEDLY(hr)) 1485 return hr; 1486 1487 CComPtr<IDataObject> dataobj; 1488 hr = shellex_get_dataobj(sei, dataobj); 1489 if (FAILED_UNEXPECTEDLY(hr)) 1490 return hr; 1491 1492 hr = obj->Initialize(NULL, dataobj, hkey); 1493 if (FAILED_UNEXPECTEDLY(hr)) 1494 return hr; 1495 1496 CComPtr<IObjectWithSite> ows; 1497 hr = obj->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows)); 1498 if (FAILED_UNEXPECTEDLY(hr)) 1499 return hr; 1500 1501 ows->SetSite(NULL); 1502 1503 return shellex_run_context_menu_default(obj, sei); 1504 } 1505 1506 static HRESULT shellex_get_contextmenu(LPSHELLEXECUTEINFOW sei, CComPtr<IContextMenu>& cm) 1507 { 1508 CComHeapPtr<ITEMIDLIST> allocatedPidl; 1509 LPITEMIDLIST pidl = NULL; 1510 1511 if (sei->lpIDList) 1512 { 1513 pidl = (LPITEMIDLIST)sei->lpIDList; 1514 } 1515 else 1516 { 1517 SFGAOF sfga = 0; 1518 HRESULT hr = SHParseDisplayName(sei->lpFile, NULL, &allocatedPidl, SFGAO_STORAGECAPMASK, &sfga); 1519 if (FAILED(hr)) 1520 { 1521 WCHAR Buffer[MAX_PATH] = {}; 1522 // FIXME: MAX_PATH..... 1523 UINT retval = SHELL_FindExecutable(sei->lpDirectory, sei->lpFile, sei->lpVerb, Buffer, _countof(Buffer), NULL, NULL, NULL, sei->lpParameters); 1524 if (retval <= 32) 1525 return HRESULT_FROM_WIN32(retval); 1526 1527 hr = SHParseDisplayName(Buffer, NULL, &allocatedPidl, SFGAO_STORAGECAPMASK, &sfga); 1528 // This should not happen, we found it... 1529 if (FAILED_UNEXPECTEDLY(hr)) 1530 return hr; 1531 } 1532 1533 pidl = allocatedPidl; 1534 } 1535 1536 CComPtr<IShellFolder> shf; 1537 LPCITEMIDLIST pidllast = NULL; 1538 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); 1539 if (FAILED(hr)) 1540 return hr; 1541 1542 return shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IContextMenu, &cm)); 1543 } 1544 1545 static HRESULT ShellExecute_ContextMenuVerb(LPSHELLEXECUTEINFOW sei) 1546 { 1547 TRACE("%p\n", sei); 1548 1549 CCoInit coInit; 1550 1551 if (FAILED_UNEXPECTEDLY(coInit.hr)) 1552 return coInit.hr; 1553 1554 CComPtr<IContextMenu> cm; 1555 HRESULT hr = shellex_get_contextmenu(sei, cm); 1556 if (FAILED_UNEXPECTEDLY(hr)) 1557 return hr; 1558 1559 CComHeapPtr<char> verb, parameters, dir; 1560 __SHCloneStrWtoA(&verb, sei->lpVerb); 1561 __SHCloneStrWtoA(¶meters, sei->lpParameters); 1562 __SHCloneStrWtoA(&dir, sei->lpDirectory); 1563 1564 BOOL fDefault = StrIsNullOrEmpty(sei->lpVerb); 1565 CMINVOKECOMMANDINFOEX ici = { sizeof(ici) }; 1566 ici.fMask = SeeFlagsToCmicFlags(sei->fMask) | CMIC_MASK_UNICODE; 1567 ici.nShow = sei->nShow; 1568 if (!fDefault) 1569 { 1570 ici.lpVerb = verb; 1571 ici.lpVerbW = sei->lpVerb; 1572 } 1573 ici.hwnd = sei->hwnd; 1574 ici.lpParameters = parameters; 1575 ici.lpParametersW = sei->lpParameters; 1576 ici.lpDirectory = dir; 1577 ici.lpDirectoryW = sei->lpDirectory; 1578 ici.dwHotKey = sei->dwHotKey; 1579 ici.hIcon = sei->hIcon; 1580 if (ici.fMask & (CMIC_MASK_HASLINKNAME | CMIC_MASK_HASTITLE)) 1581 ici.lpTitleW = sei->lpClass; 1582 1583 enum { idFirst = 1, idLast = 0x7fff }; 1584 HMENU hMenu = CreatePopupMenu(); 1585 hr = cm->QueryContextMenu(hMenu, 0, idFirst, idLast, fDefault ? CMF_DEFAULTONLY : 0); 1586 if (!FAILED_UNEXPECTEDLY(hr)) 1587 { 1588 if (fDefault) 1589 { 1590 INT uDefault = GetMenuDefaultItem(hMenu, FALSE, 0); 1591 uDefault = (uDefault != -1) ? uDefault - idFirst : 0; 1592 ici.lpVerb = MAKEINTRESOURCEA(uDefault); 1593 ici.lpVerbW = MAKEINTRESOURCEW(uDefault); 1594 } 1595 1596 hr = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); 1597 if (!FAILED_UNEXPECTEDLY(hr)) 1598 hr = S_OK; 1599 } 1600 1601 DestroyMenu(hMenu); 1602 1603 return hr; 1604 } 1605 1606 1607 /************************************************************************* 1608 * ShellExecute_FromContextMenu [Internal] 1609 */ 1610 static LONG ShellExecute_FromContextMenuHandlers( LPSHELLEXECUTEINFOW sei ) 1611 { 1612 HKEY hkey, hkeycm = 0; 1613 WCHAR szguid[39]; 1614 HRESULT hr; 1615 GUID guid; 1616 DWORD i; 1617 LONG r; 1618 1619 TRACE("%s\n", debugstr_w(sei->lpFile)); 1620 1621 hkey = ShellExecute_GetClassKey(sei); 1622 if (!hkey) 1623 return ERROR_FUNCTION_FAILED; 1624 1625 r = RegOpenKeyW(hkey, L"shellex\\ContextMenuHandlers", &hkeycm); 1626 if (r == ERROR_SUCCESS) 1627 { 1628 i = 0; 1629 while (1) 1630 { 1631 r = RegEnumKeyW(hkeycm, i++, szguid, ARRAY_SIZE(szguid)); 1632 if (r != ERROR_SUCCESS) 1633 break; 1634 1635 hr = CLSIDFromString(szguid, &guid); 1636 if (SUCCEEDED(hr)) 1637 { 1638 /* stop at the first one that succeeds in running */ 1639 hr = shellex_load_object_and_run(hkey, &guid, sei); 1640 if (SUCCEEDED(hr)) 1641 break; 1642 } 1643 } 1644 RegCloseKey(hkeycm); 1645 } 1646 1647 if (hkey != sei->hkeyClass) 1648 RegCloseKey(hkey); 1649 return r; 1650 } 1651 1652 static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR lpstrProtocol, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc); 1653 1654 static UINT_PTR SHELL_execute_class(LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) 1655 { 1656 WCHAR execCmd[1024], classname[1024]; 1657 /* launch a document by fileclass like 'WordPad.Document.1' */ 1658 /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */ 1659 /* FIXME: wcmd should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */ 1660 ULONG cmask = (psei->fMask & SEE_MASK_CLASSALL); 1661 DWORD resultLen; 1662 BOOL done; 1663 UINT_PTR rslt; 1664 1665 /* FIXME: remove following block when SHELL_quote_and_execute supports hkeyClass parameter */ 1666 if (cmask != SEE_MASK_CLASSNAME) 1667 { 1668 WCHAR wcmd[1024]; 1669 HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? psei->hkeyClass : NULL, 1670 (cmask == SEE_MASK_CLASSNAME) ? psei->lpClass : NULL, 1671 psei->lpVerb, 1672 execCmd, sizeof(execCmd)); 1673 1674 /* FIXME: get the extension of lpFile, check if it fits to the lpClass */ 1675 TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(execCmd), debugstr_w(wszApplicationName)); 1676 1677 wcmd[0] = '\0'; 1678 done = SHELL_ArgifyW(wcmd, ARRAY_SIZE(wcmd), execCmd, wszApplicationName, (LPITEMIDLIST)psei->lpIDList, psei->lpParameters, 1679 &resultLen, (psei->lpDirectory && *psei->lpDirectory) ? psei->lpDirectory : NULL); 1680 if (!done && wszApplicationName[0]) 1681 { 1682 #if 0 // Given HKCR\.test=SZ:"test" and HKCR\test\shell\open\command=SZ:"cmd.exe /K echo.Hello", no filename is 1683 // appended on Windows when there is no %1 nor %L when executed with: shlextdbg.exe /shellexec=c:\file.test /INVOKE 1684 strcatW(wcmd, L" "); 1685 if (*wszApplicationName != '"') 1686 { 1687 strcatW(wcmd, L"\""); 1688 strcatW(wcmd, wszApplicationName); 1689 strcatW(wcmd, L"\""); 1690 } 1691 else 1692 strcatW(wcmd, wszApplicationName); 1693 #endif 1694 } 1695 if (resultLen > ARRAY_SIZE(wcmd)) 1696 ERR("Argify buffer not large enough... truncating\n"); 1697 return execfunc(wcmd, NULL, FALSE, psei, psei_out); 1698 } 1699 1700 strcpyW(classname, psei->lpClass); 1701 rslt = SHELL_FindExecutableByVerb(psei->lpVerb, NULL, classname, execCmd, sizeof(execCmd)); 1702 1703 TRACE("SHELL_FindExecutableByVerb returned %u (%s, %s)\n", (unsigned int)rslt, debugstr_w(classname), debugstr_w(execCmd)); 1704 if (33 > rslt) 1705 return rslt; 1706 rslt = SHELL_quote_and_execute( execCmd, L"", classname, 1707 wszApplicationName, NULL, psei, 1708 psei_out, execfunc ); 1709 return rslt; 1710 1711 } 1712 1713 static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen) 1714 { 1715 WCHAR buffer[MAX_PATH]; 1716 BOOL appKnownSingular = FALSE; 1717 1718 /* last chance to translate IDList: now also allow CLSID paths */ 1719 if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW((LPCITEMIDLIST)sei->lpIDList, buffer, ARRAY_SIZE(buffer)))) { 1720 if (buffer[0] == ':' && buffer[1] == ':') { 1721 /* open shell folder for the specified class GUID */ 1722 if (strlenW(buffer) + 1 > parametersLen) 1723 ERR("parameters len exceeds buffer size (%i > %i), truncating\n", 1724 lstrlenW(buffer) + 1, parametersLen); 1725 lstrcpynW(wszParameters, buffer, parametersLen); 1726 if (strlenW(L"explorer.exe") > dwApplicationNameLen) 1727 ERR("application len exceeds buffer size (%i), truncating\n", 1728 dwApplicationNameLen); 1729 lstrcpynW(wszApplicationName, L"explorer.exe", dwApplicationNameLen); 1730 appKnownSingular = TRUE; 1731 1732 sei->fMask &= ~SEE_MASK_INVOKEIDLIST; 1733 } else { 1734 WCHAR target[max(MAX_PATH, _countof(buffer))]; 1735 DWORD attribs; 1736 DWORD resultLen; 1737 /* Check if we're executing a directory and if so use the 1738 handler for the Folder class */ 1739 strcpyW(target, buffer); 1740 attribs = GetFileAttributesW(buffer); 1741 if (attribs != INVALID_FILE_ATTRIBUTES && 1742 (attribs & FILE_ATTRIBUTE_DIRECTORY) && 1743 HCR_GetExecuteCommandW(0, L"Folder", 1744 sei->lpVerb, 1745 buffer, sizeof(buffer))) { 1746 SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen, 1747 buffer, target, (LPITEMIDLIST)sei->lpIDList, NULL, &resultLen, 1748 !StrIsNullOrEmpty(sei->lpDirectory) ? sei->lpDirectory : NULL); 1749 if (resultLen > dwApplicationNameLen) 1750 ERR("Argify buffer not large enough... truncating\n"); // FIXME: Report this to the caller? 1751 appKnownSingular = FALSE; 1752 // HACKFIX: We really want the !appKnownSingular code in SHELL_execute to split the 1753 // parameters for us but we cannot guarantee that the exe in the registry is quoted. 1754 // We have now turned 'explorer.exe "%1" into 'explorer.exe "c:\path\from\pidl"' and 1755 // need to split to application and parameters. 1756 LPCWSTR params = PathGetArgsW(wszApplicationName); 1757 lstrcpynW(wszParameters, params, parametersLen); 1758 PathRemoveArgsW(wszApplicationName); 1759 PathUnquoteSpacesW(wszApplicationName); 1760 appKnownSingular = TRUE; 1761 } 1762 sei->fMask &= ~SEE_MASK_INVOKEIDLIST; 1763 } 1764 } 1765 return appKnownSingular; 1766 } 1767 1768 static BOOL 1769 SHELL_InvokePidl( 1770 _In_ LPSHELLEXECUTEINFOW sei, 1771 _In_ LPCITEMIDLIST pidl) 1772 { 1773 // Bind pidl 1774 CComPtr<IShellFolder> psfFolder; 1775 LPCITEMIDLIST pidlLast; 1776 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &pidlLast); 1777 if (FAILED_UNEXPECTEDLY(hr)) 1778 return FALSE; 1779 1780 // Get the context menu to invoke a command 1781 CComPtr<IContextMenu> pCM; 1782 hr = psfFolder->GetUIObjectOf(NULL, 1, &pidlLast, IID_NULL_PPV_ARG(IContextMenu, &pCM)); 1783 if (FAILED_UNEXPECTEDLY(hr)) 1784 return FALSE; 1785 1786 // Invoke a command 1787 CMINVOKECOMMANDINFO ici = { sizeof(ici) }; 1788 ici.fMask = (sei->fMask & (SEE_MASK_NO_CONSOLE | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); 1789 ici.nShow = sei->nShow; 1790 ici.hwnd = sei->hwnd; 1791 char szVerb[VERBKEY_CCHMAX]; 1792 if (sei->lpVerb && sei->lpVerb[0]) 1793 { 1794 WideCharToMultiByte(CP_ACP, 0, sei->lpVerb, -1, szVerb, _countof(szVerb), NULL, NULL); 1795 szVerb[_countof(szVerb) - 1] = ANSI_NULL; // Avoid buffer overrun 1796 ici.lpVerb = szVerb; 1797 } 1798 else // The default verb? 1799 { 1800 HMENU hMenu = CreatePopupMenu(); 1801 const INT idCmdFirst = 1, idCmdLast = 0x7FFF; 1802 hr = pCM->QueryContextMenu(hMenu, 0, idCmdFirst, idCmdLast, CMF_DEFAULTONLY); 1803 if (FAILED_UNEXPECTEDLY(hr)) 1804 { 1805 DestroyMenu(hMenu); 1806 return FALSE; 1807 } 1808 1809 INT nDefaultID = GetMenuDefaultItem(hMenu, FALSE, 0); 1810 DestroyMenu(hMenu); 1811 if (nDefaultID == -1) 1812 nDefaultID = idCmdFirst; 1813 1814 ici.lpVerb = MAKEINTRESOURCEA(nDefaultID - idCmdFirst); 1815 } 1816 hr = pCM->InvokeCommand(&ici); 1817 1818 return !FAILED_UNEXPECTEDLY(hr); 1819 } 1820 1821 static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR wszKeyname, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) 1822 { 1823 UINT_PTR retval; 1824 DWORD len; 1825 CHeapPtr<WCHAR, CLocalAllocator> wszQuotedCmd; 1826 1827 /* Length of quotes plus length of command plus NULL terminator */ 1828 len = 2 + lstrlenW(wcmd) + 1; 1829 if (wszParameters[0]) 1830 { 1831 /* Length of space plus length of parameters */ 1832 len += 1 + lstrlenW(wszParameters); 1833 } 1834 wszQuotedCmd.Allocate(len); 1835 /* Must quote to handle case where cmd contains spaces, 1836 * else security hole if malicious user creates executable file "C:\\Program" 1837 */ 1838 strcpyW(wszQuotedCmd, L"\""); 1839 strcatW(wszQuotedCmd, wcmd); 1840 strcatW(wszQuotedCmd, L"\""); 1841 if (wszParameters[0]) 1842 { 1843 strcatW(wszQuotedCmd, L" "); 1844 strcatW(wszQuotedCmd, wszParameters); 1845 } 1846 1847 TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(wszKeyname)); 1848 1849 if (*wszKeyname) 1850 retval = execute_from_key(wszKeyname, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out); 1851 else 1852 retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out); 1853 1854 return retval; 1855 } 1856 1857 static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) 1858 { 1859 UINT_PTR retval; 1860 CHeapPtr<WCHAR, CLocalAllocator> lpstrProtocol; 1861 LPCWSTR lpstrRes; 1862 INT iSize; 1863 DWORD len; 1864 1865 lpstrRes = strchrW(lpFile, ':'); 1866 if (lpstrRes) 1867 iSize = lpstrRes - lpFile; 1868 else 1869 iSize = strlenW(lpFile); 1870 1871 TRACE("Got URL: %s\n", debugstr_w(lpFile)); 1872 /* Looking for ...<protocol>\shell\<lpVerb>\command */ 1873 len = iSize + lstrlenW(L"\\shell\\") + lstrlenW(L"\\command") + 1; 1874 if (psei->lpVerb && *psei->lpVerb) 1875 len += lstrlenW(psei->lpVerb); 1876 else 1877 len += lstrlenW(L"open"); 1878 lpstrProtocol.Allocate(len); 1879 memcpy(lpstrProtocol, lpFile, iSize * sizeof(WCHAR)); 1880 lpstrProtocol[iSize] = '\0'; 1881 strcatW(lpstrProtocol, L"\\shell\\"); 1882 strcatW(lpstrProtocol, psei->lpVerb && *psei->lpVerb ? psei->lpVerb : L"open"); 1883 strcatW(lpstrProtocol, L"\\command"); 1884 1885 retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters, 1886 wcmd, execfunc, psei, psei_out); 1887 1888 return retval; 1889 } 1890 1891 static void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename) 1892 { 1893 WCHAR msg[2048]; 1894 DWORD_PTR msgArguments[3] = { (DWORD_PTR)filename, 0, 0 }; 1895 DWORD error_code; 1896 1897 error_code = GetLastError(); 1898 if (retval == SE_ERR_NOASSOC) 1899 LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, ARRAY_SIZE(msg)); 1900 else 1901 FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, 1902 NULL, 1903 error_code, 1904 LANG_USER_DEFAULT, 1905 msg, 1906 ARRAY_SIZE(msg), 1907 (va_list*)msgArguments); 1908 1909 MessageBoxW(hwnd, msg, NULL, MB_ICONERROR); 1910 } 1911 1912 static WCHAR *expand_environment( const WCHAR *str ) 1913 { 1914 CHeapPtr<WCHAR, CLocalAllocator> buf; 1915 DWORD len; 1916 1917 len = ExpandEnvironmentStringsW(str, NULL, 0); 1918 if (!len) return NULL; 1919 1920 if (!buf.Allocate(len)) 1921 return NULL; 1922 1923 len = ExpandEnvironmentStringsW(str, buf, len); 1924 if (!len) 1925 return NULL; 1926 1927 return buf.Detach(); 1928 } 1929 1930 /************************************************************************* 1931 * SHELL_execute [Internal] 1932 */ 1933 static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) 1934 { 1935 static const DWORD unsupportedFlags = 1936 SEE_MASK_ICON | SEE_MASK_HOTKEY | 1937 SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | 1938 SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR; 1939 1940 DWORD len; 1941 UINT_PTR retval = SE_ERR_NOASSOC; 1942 BOOL appKnownSingular = FALSE; 1943 1944 /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */ 1945 SHELLEXECUTEINFOW sei_tmp = *sei; 1946 1947 TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n", 1948 sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb), 1949 debugstr_w(sei_tmp.lpFile), debugstr_w(sei_tmp.lpParameters), 1950 debugstr_w(sei_tmp.lpDirectory), sei_tmp.nShow, 1951 ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ? 1952 debugstr_w(sei_tmp.lpClass) : "not used"); 1953 1954 sei->hProcess = NULL; 1955 1956 /* make copies of all path/command strings */ 1957 CHeapPtr<WCHAR, CLocalAllocator> wszApplicationName; 1958 DWORD dwApplicationNameLen = MAX_PATH + 2; 1959 if (!sei_tmp.lpFile) 1960 { 1961 wszApplicationName.Allocate(dwApplicationNameLen); 1962 *wszApplicationName = '\0'; 1963 } 1964 else if (*sei_tmp.lpFile == '\"' && sei_tmp.lpFile[(len = strlenW(sei_tmp.lpFile))-1] == '\"') 1965 { 1966 if(len-1 >= dwApplicationNameLen) 1967 dwApplicationNameLen = len; 1968 1969 wszApplicationName.Allocate(dwApplicationNameLen); 1970 memcpy(wszApplicationName, sei_tmp.lpFile + 1, len * sizeof(WCHAR)); 1971 1972 if(len > 2) 1973 wszApplicationName[len-2] = '\0'; 1974 appKnownSingular = TRUE; 1975 1976 TRACE("wszApplicationName=%s\n", debugstr_w(wszApplicationName)); 1977 } 1978 else 1979 { 1980 DWORD l = strlenW(sei_tmp.lpFile) + 1; 1981 if(l > dwApplicationNameLen) dwApplicationNameLen = l + 1; 1982 wszApplicationName.Allocate(dwApplicationNameLen); 1983 memcpy(wszApplicationName, sei_tmp.lpFile, l * sizeof(WCHAR)); 1984 1985 if (wszApplicationName[2] == 0 && wszApplicationName[1] == L':' && 1986 ((L'A' <= wszApplicationName[0] && wszApplicationName[0] <= L'Z') || 1987 (L'a' <= wszApplicationName[0] && wszApplicationName[0] <= L'z'))) 1988 { 1989 // 'C:' --> 'C:\' 1990 PathAddBackslashW(wszApplicationName); 1991 } 1992 } 1993 1994 WCHAR parametersBuffer[1024]; 1995 LPWSTR wszParameters = parametersBuffer; 1996 CHeapPtr<WCHAR, CLocalAllocator> wszParamAlloc; 1997 DWORD parametersLen = _countof(parametersBuffer); 1998 1999 if (sei_tmp.lpParameters) 2000 { 2001 len = lstrlenW(sei_tmp.lpParameters) + 1; 2002 if (len > parametersLen) 2003 { 2004 wszParamAlloc.Allocate(len); 2005 wszParameters = wszParamAlloc; 2006 parametersLen = len; 2007 } 2008 strcpyW(wszParameters, sei_tmp.lpParameters); 2009 } 2010 else 2011 *wszParameters = L'\0'; 2012 2013 // Get the working directory 2014 WCHAR dirBuffer[MAX_PATH]; 2015 LPWSTR wszDir = dirBuffer; 2016 wszDir[0] = UNICODE_NULL; 2017 CHeapPtr<WCHAR, CLocalAllocator> wszDirAlloc; 2018 if (sei_tmp.lpDirectory && *sei_tmp.lpDirectory) 2019 { 2020 if (sei_tmp.fMask & SEE_MASK_DOENVSUBST) 2021 { 2022 LPWSTR tmp = expand_environment(sei_tmp.lpDirectory); 2023 if (tmp) 2024 { 2025 wszDirAlloc.Attach(tmp); 2026 wszDir = wszDirAlloc; 2027 } 2028 } 2029 else 2030 { 2031 __SHCloneStrW(&wszDirAlloc, sei_tmp.lpDirectory); 2032 if (wszDirAlloc) 2033 wszDir = wszDirAlloc; 2034 } 2035 } 2036 if (!wszDir[0]) 2037 { 2038 ::GetCurrentDirectoryW(_countof(dirBuffer), dirBuffer); 2039 wszDir = dirBuffer; 2040 } 2041 // NOTE: ShellExecute should accept the invalid working directory for historical reason. 2042 if (!PathIsDirectoryW(wszDir)) 2043 { 2044 INT iDrive = PathGetDriveNumberW(wszDir); 2045 if (iDrive >= 0) 2046 { 2047 PathStripToRootW(wszDir); 2048 if (!PathIsDirectoryW(wszDir)) 2049 { 2050 ::GetWindowsDirectoryW(dirBuffer, _countof(dirBuffer)); 2051 wszDir = dirBuffer; 2052 } 2053 } 2054 } 2055 2056 /* adjust string pointers to point to the new buffers */ 2057 sei_tmp.lpFile = wszApplicationName; 2058 sei_tmp.lpParameters = wszParameters; 2059 sei_tmp.lpDirectory = wszDir; 2060 2061 if (sei_tmp.fMask & unsupportedFlags) 2062 { 2063 FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags); 2064 } 2065 2066 /* process the IDList */ 2067 if (sei_tmp.fMask & SEE_MASK_IDLIST && 2068 (sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) != SEE_MASK_INVOKEIDLIST) 2069 { 2070 CComPtr<IShellExecuteHookW> pSEH; 2071 2072 HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_PPV_ARG(IShellExecuteHookW, &pSEH), NULL); 2073 2074 if (SUCCEEDED(hr)) 2075 { 2076 hr = pSEH->Execute(&sei_tmp); 2077 if (hr == S_OK) 2078 return TRUE; 2079 } 2080 2081 SHGetPathFromIDListW((LPCITEMIDLIST)sei_tmp.lpIDList, wszApplicationName); 2082 appKnownSingular = TRUE; 2083 TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName)); 2084 } 2085 2086 if ((sei_tmp.fMask & SEE_MASK_DOENVSUBST) && !StrIsNullOrEmpty(sei_tmp.lpFile)) 2087 { 2088 WCHAR *tmp = expand_environment(sei_tmp.lpFile); 2089 if (tmp) 2090 { 2091 wszApplicationName.Attach(tmp); 2092 sei_tmp.lpFile = wszApplicationName; 2093 } 2094 } 2095 2096 if ((sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) == SEE_MASK_INVOKEIDLIST) 2097 { 2098 HRESULT hr = ShellExecute_ContextMenuVerb(&sei_tmp); 2099 if (SUCCEEDED(hr)) 2100 { 2101 sei->hInstApp = (HINSTANCE)42; 2102 return TRUE; 2103 } 2104 } 2105 2106 if (ERROR_SUCCESS == ShellExecute_FromContextMenuHandlers(&sei_tmp)) 2107 { 2108 sei->hInstApp = (HINSTANCE) 33; 2109 return TRUE; 2110 } 2111 2112 if (sei_tmp.fMask & SEE_MASK_CLASSALL) 2113 { 2114 retval = SHELL_execute_class(wszApplicationName, &sei_tmp, sei, execfunc); 2115 if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) 2116 { 2117 OPENASINFO Info; 2118 2119 //FIXME 2120 // need full path 2121 2122 Info.pcszFile = wszApplicationName; 2123 Info.pcszClass = NULL; 2124 Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; 2125 2126 //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK) 2127 DBG_UNREFERENCED_LOCAL_VARIABLE(Info); 2128 do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName); 2129 } 2130 return retval > 32; 2131 } 2132 2133 if (!(sei_tmp.fMask & SEE_MASK_IDLIST) && // Not an ID List 2134 (StrCmpNIW(sei_tmp.lpFile, L"shell:", 6) == 0 || 2135 StrCmpNW(sei_tmp.lpFile, L"::{", 3) == 0)) 2136 { 2137 CComHeapPtr<ITEMIDLIST> pidlParsed; 2138 HRESULT hr = SHParseDisplayName(sei_tmp.lpFile, NULL, &pidlParsed, 0, NULL); 2139 if (SUCCEEDED(hr) && SHELL_InvokePidl(&sei_tmp, pidlParsed)) 2140 { 2141 sei_tmp.hInstApp = (HINSTANCE)UlongToHandle(42); 2142 return TRUE; 2143 } 2144 } 2145 2146 /* Has the IDList not yet been translated? */ 2147 if (sei_tmp.fMask & SEE_MASK_IDLIST) 2148 { 2149 appKnownSingular = SHELL_translate_idlist( &sei_tmp, wszParameters, 2150 parametersLen, 2151 wszApplicationName, 2152 dwApplicationNameLen ); 2153 } 2154 2155 /* convert file URLs */ 2156 if (UrlIsFileUrlW(sei_tmp.lpFile)) 2157 { 2158 CHeapPtr<WCHAR, CLocalAllocator> buf; 2159 DWORD size = MAX_PATH; 2160 if (!buf.Allocate(size) || FAILED(PathCreateFromUrlW(sei_tmp.lpFile, buf, &size, 0))) 2161 return SE_ERR_OOM; 2162 2163 wszApplicationName.Attach(buf.Detach()); 2164 sei_tmp.lpFile = wszApplicationName; 2165 } 2166 2167 /* Else, try to execute the filename */ 2168 TRACE("execute: %s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir)); 2169 2170 /* separate out command line arguments from executable file name */ 2171 LPCWSTR lpFile = sei_tmp.lpFile; 2172 if (!*sei_tmp.lpParameters && !appKnownSingular) 2173 { 2174 /* If the executable path is quoted, handle the rest of the command line as parameters. */ 2175 if (sei_tmp.lpFile[0] == L'"') 2176 { 2177 LPWSTR pszArgs = PathGetArgsW(wszApplicationName); 2178 PathRemoveArgsW(wszApplicationName); 2179 PathUnquoteSpacesW(wszApplicationName); 2180 parametersLen = lstrlenW(pszArgs); 2181 if (parametersLen < _countof(parametersBuffer)) 2182 { 2183 StringCchCopyW(parametersBuffer, _countof(parametersBuffer), pszArgs); 2184 wszParameters = parametersBuffer; 2185 } 2186 else 2187 { 2188 wszParamAlloc.Attach(StrDupW(pszArgs)); 2189 wszParameters = wszParamAlloc; 2190 } 2191 } 2192 /* We have to test sei instead of sei_tmp because sei_tmp had its 2193 * input fMask modified above in SHELL_translate_idlist. 2194 * This code is needed to handle the case where we only have an 2195 * lpIDList with multiple CLSID/PIDL's (not 'My Computer' only) */ 2196 else if ((sei->fMask & SEE_MASK_IDLIST) == SEE_MASK_IDLIST) 2197 { 2198 WCHAR buffer[MAX_PATH], xlpFile[MAX_PATH]; 2199 LPWSTR space, s; 2200 2201 LPWSTR beg = wszApplicationName; 2202 for(s = beg; (space = const_cast<LPWSTR>(strchrW(s, L' '))); s = space + 1) 2203 { 2204 int idx = space - sei_tmp.lpFile; 2205 memcpy(buffer, sei_tmp.lpFile, idx * sizeof(WCHAR)); 2206 buffer[idx] = '\0'; 2207 2208 if (SearchPathW(*sei_tmp.lpDirectory ? sei_tmp.lpDirectory : NULL, 2209 buffer, L".exe", _countof(xlpFile), xlpFile, NULL)) 2210 { 2211 /* separate out command from parameter string */ 2212 LPCWSTR p = space + 1; 2213 2214 while(isspaceW(*p)) 2215 ++p; 2216 2217 strcpyW(wszParameters, p); 2218 *space = L'\0'; 2219 2220 break; 2221 } 2222 } 2223 } 2224 } 2225 2226 WCHAR wcmdBuffer[1024]; 2227 LPWSTR wcmd = wcmdBuffer; 2228 DWORD wcmdLen = _countof(wcmdBuffer); 2229 CHeapPtr<WCHAR, CLocalAllocator> wcmdAlloc; 2230 2231 /* Only execute if it has an executable extension */ 2232 if (PathIsExeW(lpFile)) 2233 { 2234 len = lstrlenW(wszApplicationName) + 3; 2235 if (sei_tmp.lpParameters[0]) 2236 len += 1 + lstrlenW(wszParameters); 2237 if (len > wcmdLen) 2238 { 2239 wcmdAlloc.Allocate(len); 2240 wcmd = wcmdAlloc; 2241 wcmdLen = len; 2242 } 2243 swprintf(wcmd, L"\"%s\"", (LPWSTR)wszApplicationName); 2244 if (sei_tmp.lpParameters[0]) 2245 { 2246 strcatW(wcmd, L" "); 2247 strcatW(wcmd, wszParameters); 2248 } 2249 2250 retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei); 2251 if (retval > 32) 2252 return TRUE; 2253 } 2254 2255 /* Else, try to find the executable */ 2256 WCHAR wszKeyname[256]; 2257 CHeapPtr<WCHAR, CLocalAllocator> env; 2258 wcmd[0] = UNICODE_NULL; 2259 retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, wszKeyname, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters); 2260 if (retval > 32) /* Found */ 2261 { 2262 retval = SHELL_quote_and_execute(wcmd, wszParameters, wszKeyname, 2263 wszApplicationName, env, &sei_tmp, 2264 sei, execfunc); 2265 } 2266 else if (PathIsDirectoryW(lpFile)) 2267 { 2268 WCHAR wExec[MAX_PATH]; 2269 CHeapPtr<WCHAR, CLocalAllocator> lpQuotedFile; 2270 if (lpQuotedFile.Allocate(strlenW(lpFile) + 3)) 2271 { 2272 retval = SHELL_FindExecutable(sei_tmp.lpDirectory, L"explorer", 2273 L"open", wExec, MAX_PATH, 2274 NULL, &env, NULL, NULL); 2275 if (retval > 32) 2276 { 2277 swprintf(lpQuotedFile, L"\"%s\"", lpFile); 2278 retval = SHELL_quote_and_execute(wExec, lpQuotedFile, 2279 wszKeyname, 2280 wszApplicationName, env, 2281 &sei_tmp, sei, execfunc); 2282 } 2283 } 2284 else 2285 retval = 0; /* Out of memory */ 2286 } 2287 else if (PathIsURLW(lpFile)) /* File not found, check for URL */ 2288 { 2289 retval = SHELL_execute_url(lpFile, wcmd, &sei_tmp, sei, execfunc ); 2290 } 2291 /* Check if file specified is in the form www.??????.*** */ 2292 else if (!strncmpiW(lpFile, L"www", 3)) 2293 { 2294 /* if so, prefix lpFile with http:// and call ShellExecute */ 2295 WCHAR lpstrTmpFile[256]; 2296 strcpyW(lpstrTmpFile, L"http://"); 2297 strcatW(lpstrTmpFile, lpFile); 2298 retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0); 2299 } 2300 2301 TRACE("retval %lu\n", retval); 2302 2303 if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) 2304 { 2305 OPENASINFO Info; 2306 2307 //FIXME 2308 // need full path 2309 2310 Info.pcszFile = wszApplicationName; 2311 Info.pcszClass = NULL; 2312 Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; 2313 2314 //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK) 2315 DBG_UNREFERENCED_LOCAL_VARIABLE(Info); 2316 do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName); 2317 } 2318 2319 sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval); 2320 2321 return retval > 32; 2322 } 2323 2324 /************************************************************************* 2325 * ShellExecuteA [SHELL32.290] 2326 */ 2327 HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, 2328 LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd) 2329 { 2330 SHELLEXECUTEINFOA sei; 2331 2332 TRACE("%p,%s,%s,%s,%s,%d\n", 2333 hWnd, debugstr_a(lpVerb), debugstr_a(lpFile), 2334 debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd); 2335 2336 sei.cbSize = sizeof(sei); 2337 sei.fMask = SEE_MASK_FLAG_NO_UI; 2338 sei.hwnd = hWnd; 2339 sei.lpVerb = lpVerb; 2340 sei.lpFile = lpFile; 2341 sei.lpParameters = lpParameters; 2342 sei.lpDirectory = lpDirectory; 2343 sei.nShow = iShowCmd; 2344 sei.lpIDList = 0; 2345 sei.lpClass = 0; 2346 sei.hkeyClass = 0; 2347 sei.dwHotKey = 0; 2348 sei.hProcess = 0; 2349 2350 ShellExecuteExA(&sei); 2351 return sei.hInstApp; 2352 } 2353 2354 static DWORD 2355 ShellExecute_Normal(_Inout_ LPSHELLEXECUTEINFOW sei) 2356 { 2357 // FIXME 2358 return SHELL_execute(sei, SHELL_ExecuteW) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; 2359 } 2360 2361 static VOID 2362 ShellExecute_ShowError( 2363 _In_ const SHELLEXECUTEINFOW *ExecInfo, 2364 _In_opt_ LPCWSTR pszCaption, 2365 _In_ DWORD dwError) 2366 { 2367 // FIXME: Show error message 2368 } 2369 2370 /************************************************************************* 2371 * ShellExecuteExA [SHELL32.292] 2372 */ 2373 BOOL 2374 WINAPI 2375 DECLSPEC_HOTPATCH 2376 ShellExecuteExA(LPSHELLEXECUTEINFOA sei) 2377 { 2378 SHELLEXECUTEINFOW seiW; 2379 BOOL ret; 2380 WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL, *wClass = NULL; 2381 2382 TRACE("%p\n", sei); 2383 2384 if (sei->cbSize != sizeof(SHELLEXECUTEINFOA)) 2385 { 2386 sei->hInstApp = (HINSTANCE)ERROR_ACCESS_DENIED; 2387 SetLastError(ERROR_ACCESS_DENIED); 2388 return FALSE; 2389 } 2390 2391 memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW)); 2392 2393 seiW.cbSize = sizeof(SHELLEXECUTEINFOW); 2394 2395 if (sei->lpVerb) 2396 seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb); 2397 2398 if (sei->lpFile) 2399 seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile); 2400 2401 if (sei->lpParameters) 2402 seiW.lpParameters = __SHCloneStrAtoW(&wParameters, sei->lpParameters); 2403 2404 if (sei->lpDirectory) 2405 seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory); 2406 2407 if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME && sei->lpClass) 2408 seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass); 2409 else 2410 seiW.lpClass = NULL; 2411 2412 ret = ShellExecuteExW(&seiW); 2413 2414 sei->hInstApp = seiW.hInstApp; 2415 2416 if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) 2417 sei->hProcess = seiW.hProcess; 2418 2419 SHFree(wVerb); 2420 SHFree(wFile); 2421 SHFree(wParameters); 2422 SHFree(wDirectory); 2423 SHFree(wClass); 2424 2425 return ret; 2426 } 2427 2428 /************************************************************************* 2429 * ShellExecuteExW [SHELL32.293] 2430 */ 2431 BOOL 2432 WINAPI 2433 DECLSPEC_HOTPATCH 2434 ShellExecuteExW(LPSHELLEXECUTEINFOW sei) 2435 { 2436 HRESULT hrCoInit; 2437 DWORD dwError; 2438 ULONG fOldMask; 2439 2440 if (sei->cbSize != sizeof(SHELLEXECUTEINFOW)) 2441 { 2442 sei->hInstApp = (HINSTANCE)UlongToHandle(SE_ERR_ACCESSDENIED); 2443 SetLastError(ERROR_ACCESS_DENIED); 2444 return FALSE; 2445 } 2446 2447 hrCoInit = SHCoInitializeAnyApartment(); 2448 2449 if (SHRegGetBoolUSValueW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", 2450 L"MaximizeApps", FALSE, FALSE)) 2451 { 2452 switch (sei->nShow) 2453 { 2454 case SW_SHOW: 2455 case SW_SHOWDEFAULT: 2456 case SW_SHOWNORMAL: 2457 case SW_RESTORE: 2458 sei->nShow = SW_SHOWMAXIMIZED; 2459 break; 2460 default: 2461 break; 2462 } 2463 } 2464 2465 fOldMask = sei->fMask; 2466 2467 if (!(fOldMask & SEE_MASK_NOASYNC) && SHELL_InRunDllProcess()) 2468 sei->fMask |= SEE_MASK_WAITFORINPUTIDLE | SEE_MASK_NOASYNC; 2469 2470 dwError = ShellExecute_Normal(sei); 2471 2472 if (dwError && dwError != ERROR_DLL_NOT_FOUND && dwError != ERROR_CANCELLED) 2473 ShellExecute_ShowError(sei, NULL, dwError); 2474 2475 sei->fMask = fOldMask; 2476 2477 if (SUCCEEDED(hrCoInit)) 2478 CoUninitialize(); 2479 2480 if (dwError) 2481 SetLastError(dwError); 2482 2483 return dwError == ERROR_SUCCESS; 2484 } 2485 2486 /************************************************************************* 2487 * ShellExecuteW [SHELL32.294] 2488 * from shellapi.h 2489 * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, 2490 * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd); 2491 */ 2492 HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile, 2493 LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd) 2494 { 2495 SHELLEXECUTEINFOW sei; 2496 2497 TRACE("\n"); 2498 sei.cbSize = sizeof(sei); 2499 sei.fMask = SEE_MASK_FLAG_NO_UI; 2500 sei.hwnd = hwnd; 2501 sei.lpVerb = lpVerb; 2502 sei.lpFile = lpFile; 2503 sei.lpParameters = lpParameters; 2504 sei.lpDirectory = lpDirectory; 2505 sei.nShow = nShowCmd; 2506 sei.lpIDList = 0; 2507 sei.lpClass = 0; 2508 sei.hkeyClass = 0; 2509 sei.dwHotKey = 0; 2510 sei.hProcess = 0; 2511 2512 SHELL_execute(&sei, SHELL_ExecuteW); 2513 return sei.hInstApp; 2514 } 2515 2516 /************************************************************************* 2517 * WOWShellExecute [SHELL32.@] 2518 * 2519 * FIXME: the callback function most likely doesn't work the same way on Windows. 2520 */ 2521 EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, 2522 LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd, void *callback) 2523 { 2524 SHELLEXECUTEINFOW seiW; 2525 WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL; 2526 HANDLE hProcess = 0; 2527 2528 seiW.lpVerb = lpVerb ? __SHCloneStrAtoW(&wVerb, lpVerb) : NULL; 2529 seiW.lpFile = lpFile ? __SHCloneStrAtoW(&wFile, lpFile) : NULL; 2530 seiW.lpParameters = lpParameters ? __SHCloneStrAtoW(&wParameters, lpParameters) : NULL; 2531 seiW.lpDirectory = lpDirectory ? __SHCloneStrAtoW(&wDirectory, lpDirectory) : NULL; 2532 2533 seiW.cbSize = sizeof(seiW); 2534 seiW.fMask = 0; 2535 seiW.hwnd = hWnd; 2536 seiW.nShow = iShowCmd; 2537 seiW.lpIDList = 0; 2538 seiW.lpClass = 0; 2539 seiW.hkeyClass = 0; 2540 seiW.dwHotKey = 0; 2541 seiW.hProcess = hProcess; 2542 2543 SHELL_execute(&seiW, (SHELL_ExecuteW32)callback); 2544 2545 SHFree(wVerb); 2546 SHFree(wFile); 2547 SHFree(wParameters); 2548 SHFree(wDirectory); 2549 return seiW.hInstApp; 2550 } 2551 2552 /************************************************************************* 2553 * OpenAs_RunDLLW [SHELL32.@] 2554 */ 2555 EXTERN_C void WINAPI 2556 OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow) 2557 { 2558 OPENASINFO info; 2559 TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow); 2560 2561 ZeroMemory(&info, sizeof(info)); 2562 info.pcszFile = cmdline; 2563 info.pcszClass = NULL; 2564 info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC; 2565 2566 SHOpenWithDialog(hwnd, &info); 2567 } 2568 2569 /************************************************************************* 2570 * OpenAs_RunDLLA [SHELL32.@] 2571 */ 2572 EXTERN_C void WINAPI 2573 OpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow) 2574 { 2575 LPWSTR pszCmdLineW = NULL; 2576 TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_a(cmdline), cmdshow); 2577 2578 if (cmdline) 2579 __SHCloneStrAtoW(&pszCmdLineW, cmdline); 2580 OpenAs_RunDLLW(hwnd, hinst, pszCmdLineW, cmdshow); 2581 SHFree(pszCmdLineW); 2582 } 2583 2584 /*************************************************************************/ 2585 2586 static LPCWSTR 2587 SplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0) 2588 { 2589 LPCWSTR pch; 2590 size_t ich = 0; 2591 if (*psz == L'"') 2592 { 2593 // 1st argument is quoted. the string in quotes is quoted 1st argument. 2594 // [pch] --> [pszArg0+ich] 2595 for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch) 2596 { 2597 if (*pch == L'"' && pch[1] == L'"') 2598 { 2599 // doubled double quotations found! 2600 pszArg0[ich] = L'"'; 2601 } 2602 else if (*pch == L'"') 2603 { 2604 // single double quotation found! 2605 ++pch; 2606 break; 2607 } 2608 else 2609 { 2610 // otherwise 2611 pszArg0[ich] = *pch; 2612 } 2613 } 2614 } 2615 else 2616 { 2617 // 1st argument is unquoted. non-space sequence is 1st argument. 2618 // [pch] --> [pszArg0+ich] 2619 for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch) 2620 { 2621 pszArg0[ich] = *pch; 2622 } 2623 } 2624 pszArg0[ich] = 0; 2625 2626 // skip space 2627 while (iswspace(*pch)) 2628 ++pch; 2629 2630 return pch; 2631 } 2632 2633 HRESULT WINAPI ShellExecCmdLine( 2634 HWND hwnd, 2635 LPCWSTR pwszCommand, 2636 LPCWSTR pwszStartDir, 2637 int nShow, 2638 LPVOID pUnused, 2639 DWORD dwSeclFlags) 2640 { 2641 SHELLEXECUTEINFOW info; 2642 DWORD dwSize, dwError, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC; 2643 LPCWSTR pszVerb = NULL; 2644 WCHAR szFile[MAX_PATH], szFile2[MAX_PATH]; 2645 HRESULT hr; 2646 LPCWSTR pchParams; 2647 LPWSTR lpCommand = NULL; 2648 2649 if (pwszCommand == NULL) 2650 RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 2651 1, (ULONG_PTR*)pwszCommand); 2652 2653 __SHCloneStrW(&lpCommand, pwszCommand); 2654 StrTrimW(lpCommand, L" \t"); 2655 2656 if (dwSeclFlags & SECL_NO_UI) 2657 dwFlags |= SEE_MASK_FLAG_NO_UI; 2658 if (dwSeclFlags & SECL_LOG_USAGE) 2659 dwFlags |= SEE_MASK_FLAG_LOG_USAGE; 2660 if (dwSeclFlags & SECL_USE_IDLIST) 2661 dwFlags |= SEE_MASK_INVOKEIDLIST; 2662 2663 if (dwSeclFlags & SECL_RUNAS) 2664 { 2665 dwSize = 0; 2666 hr = AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize); 2667 if (SUCCEEDED(hr) && dwSize != 0) 2668 { 2669 pszVerb = L"runas"; 2670 } 2671 } 2672 2673 if (PathIsURLW(lpCommand) || UrlIsW(lpCommand, URLIS_APPLIABLE)) 2674 { 2675 StringCchCopyW(szFile, _countof(szFile), lpCommand); 2676 pchParams = NULL; 2677 } 2678 else 2679 { 2680 PCWSTR apPathList[2]; 2681 2682 pchParams = SplitParams(lpCommand, szFile, _countof(szFile)); 2683 if (szFile[0] != UNICODE_NULL && szFile[1] == L':' && 2684 szFile[2] == UNICODE_NULL) 2685 { 2686 PathAddBackslashW(szFile); 2687 } 2688 2689 WCHAR szCurDir[MAX_PATH]; 2690 GetCurrentDirectoryW(_countof(szCurDir), szCurDir); 2691 if (pwszStartDir) 2692 { 2693 SetCurrentDirectoryW(pwszStartDir); 2694 } 2695 2696 if ((PathIsRelativeW(szFile) && 2697 GetFullPathNameW(szFile, _countof(szFile2), szFile2, NULL) && 2698 PathFileExistsW(szFile2)) || 2699 SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL)) 2700 { 2701 StringCchCopyW(szFile, _countof(szFile), szFile2); 2702 } 2703 2704 apPathList[0] = pwszStartDir; 2705 apPathList[1] = NULL; 2706 PathFindOnPathExW(szFile, apPathList, WHICH_DEFAULT); 2707 2708 if (!(dwSeclFlags & SECL_ALLOW_NONEXE)) 2709 { 2710 if (!GetBinaryTypeW(szFile, &dwType)) 2711 { 2712 SHFree(lpCommand); 2713 2714 if (!(dwSeclFlags & SECL_NO_UI)) 2715 { 2716 WCHAR szText[128 + MAX_PATH], szFormat[128]; 2717 LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); 2718 StringCchPrintfW(szText, _countof(szText), szFormat, szFile); 2719 MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); 2720 } 2721 return CO_E_APPNOTFOUND; 2722 } 2723 } 2724 else 2725 { 2726 if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES) 2727 { 2728 SHFree(lpCommand); 2729 2730 if (!(dwSeclFlags & SECL_NO_UI)) 2731 { 2732 WCHAR szText[128 + MAX_PATH], szFormat[128]; 2733 LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); 2734 StringCchPrintfW(szText, _countof(szText), szFormat, szFile); 2735 MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); 2736 } 2737 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 2738 } 2739 } 2740 } 2741 2742 ZeroMemory(&info, sizeof(info)); 2743 info.cbSize = sizeof(info); 2744 info.fMask = dwFlags; 2745 info.hwnd = hwnd; 2746 info.lpVerb = pszVerb; 2747 info.lpFile = szFile; 2748 info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL; 2749 info.lpDirectory = pwszStartDir; 2750 info.nShow = nShow; 2751 if (ShellExecuteExW(&info)) 2752 { 2753 if (info.lpIDList) 2754 CoTaskMemFree(info.lpIDList); 2755 2756 SHFree(lpCommand); 2757 2758 return S_OK; 2759 } 2760 2761 dwError = GetLastError(); 2762 2763 SHFree(lpCommand); 2764 2765 return HRESULT_FROM_WIN32(dwError); 2766 } 2767 2768 /************************************************************************* 2769 * RealShellExecuteExA (SHELL32.266) 2770 */ 2771 EXTERN_C 2772 HINSTANCE WINAPI 2773 RealShellExecuteExA( 2774 _In_opt_ HWND hwnd, 2775 _In_opt_ LPCSTR lpOperation, 2776 _In_opt_ LPCSTR lpFile, 2777 _In_opt_ LPCSTR lpParameters, 2778 _In_opt_ LPCSTR lpDirectory, 2779 _In_opt_ LPSTR lpReturn, 2780 _In_opt_ LPCSTR lpTitle, 2781 _In_opt_ LPVOID lpReserved, 2782 _In_ INT nCmdShow, 2783 _Out_opt_ PHANDLE lphProcess, 2784 _In_ DWORD dwFlags) 2785 { 2786 SHELLEXECUTEINFOA ExecInfo; 2787 2788 TRACE("(%p, %s, %s, %s, %s, %p, %s, %p, %u, %p, %lu)\n", 2789 hwnd, debugstr_a(lpOperation), debugstr_a(lpFile), debugstr_a(lpParameters), 2790 debugstr_a(lpDirectory), lpReserved, debugstr_a(lpTitle), 2791 lpReserved, nCmdShow, lphProcess, dwFlags); 2792 2793 ZeroMemory(&ExecInfo, sizeof(ExecInfo)); 2794 ExecInfo.cbSize = sizeof(ExecInfo); 2795 ExecInfo.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNKNOWN_0x1000; 2796 ExecInfo.hwnd = hwnd; 2797 ExecInfo.lpVerb = lpOperation; 2798 ExecInfo.lpFile = lpFile; 2799 ExecInfo.lpParameters = lpParameters; 2800 ExecInfo.lpDirectory = lpDirectory; 2801 ExecInfo.nShow = (WORD)nCmdShow; 2802 2803 if (lpReserved) 2804 { 2805 ExecInfo.fMask |= SEE_MASK_USE_RESERVED; 2806 ExecInfo.hInstApp = (HINSTANCE)lpReserved; 2807 } 2808 2809 if (lpTitle) 2810 { 2811 ExecInfo.fMask |= SEE_MASK_HASTITLE; 2812 ExecInfo.lpClass = lpTitle; 2813 } 2814 2815 if (dwFlags & 1) 2816 ExecInfo.fMask |= SEE_MASK_FLAG_SEPVDM; 2817 2818 if (dwFlags & 2) 2819 ExecInfo.fMask |= SEE_MASK_NO_CONSOLE; 2820 2821 if (lphProcess == NULL) 2822 { 2823 ShellExecuteExA(&ExecInfo); 2824 } 2825 else 2826 { 2827 ExecInfo.fMask |= SEE_MASK_NOCLOSEPROCESS; 2828 ShellExecuteExA(&ExecInfo); 2829 *lphProcess = ExecInfo.hProcess; 2830 } 2831 2832 return ExecInfo.hInstApp; 2833 } 2834 2835 /************************************************************************* 2836 * RealShellExecuteExW (SHELL32.267) 2837 */ 2838 EXTERN_C 2839 HINSTANCE WINAPI 2840 RealShellExecuteExW( 2841 _In_opt_ HWND hwnd, 2842 _In_opt_ LPCWSTR lpOperation, 2843 _In_opt_ LPCWSTR lpFile, 2844 _In_opt_ LPCWSTR lpParameters, 2845 _In_opt_ LPCWSTR lpDirectory, 2846 _In_opt_ LPWSTR lpReturn, 2847 _In_opt_ LPCWSTR lpTitle, 2848 _In_opt_ LPVOID lpReserved, 2849 _In_ INT nCmdShow, 2850 _Out_opt_ PHANDLE lphProcess, 2851 _In_ DWORD dwFlags) 2852 { 2853 SHELLEXECUTEINFOW ExecInfo; 2854 2855 TRACE("(%p, %s, %s, %s, %s, %p, %s, %p, %u, %p, %lu)\n", 2856 hwnd, debugstr_w(lpOperation), debugstr_w(lpFile), debugstr_w(lpParameters), 2857 debugstr_w(lpDirectory), lpReserved, debugstr_w(lpTitle), 2858 lpReserved, nCmdShow, lphProcess, dwFlags); 2859 2860 ZeroMemory(&ExecInfo, sizeof(ExecInfo)); 2861 ExecInfo.cbSize = sizeof(ExecInfo); 2862 ExecInfo.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNKNOWN_0x1000; 2863 ExecInfo.hwnd = hwnd; 2864 ExecInfo.lpVerb = lpOperation; 2865 ExecInfo.lpFile = lpFile; 2866 ExecInfo.lpParameters = lpParameters; 2867 ExecInfo.lpDirectory = lpDirectory; 2868 ExecInfo.nShow = (WORD)nCmdShow; 2869 2870 if (lpReserved) 2871 { 2872 ExecInfo.fMask |= SEE_MASK_USE_RESERVED; 2873 ExecInfo.hInstApp = (HINSTANCE)lpReserved; 2874 } 2875 2876 if (lpTitle) 2877 { 2878 ExecInfo.fMask |= SEE_MASK_HASTITLE; 2879 ExecInfo.lpClass = lpTitle; 2880 } 2881 2882 if (dwFlags & 1) 2883 ExecInfo.fMask |= SEE_MASK_FLAG_SEPVDM; 2884 2885 if (dwFlags & 2) 2886 ExecInfo.fMask |= SEE_MASK_NO_CONSOLE; 2887 2888 if (lphProcess == NULL) 2889 { 2890 ShellExecuteExW(&ExecInfo); 2891 } 2892 else 2893 { 2894 ExecInfo.fMask |= SEE_MASK_NOCLOSEPROCESS; 2895 ShellExecuteExW(&ExecInfo); 2896 *lphProcess = ExecInfo.hProcess; 2897 } 2898 2899 return ExecInfo.hInstApp; 2900 } 2901 2902 /************************************************************************* 2903 * RealShellExecuteA (SHELL32.265) 2904 */ 2905 EXTERN_C 2906 HINSTANCE WINAPI 2907 RealShellExecuteA( 2908 _In_opt_ HWND hwnd, 2909 _In_opt_ LPCSTR lpOperation, 2910 _In_opt_ LPCSTR lpFile, 2911 _In_opt_ LPCSTR lpParameters, 2912 _In_opt_ LPCSTR lpDirectory, 2913 _In_opt_ LPSTR lpReturn, 2914 _In_opt_ LPCSTR lpTitle, 2915 _In_opt_ LPVOID lpReserved, 2916 _In_ INT nCmdShow, 2917 _Out_opt_ PHANDLE lphProcess) 2918 { 2919 return RealShellExecuteExA(hwnd, 2920 lpOperation, 2921 lpFile, 2922 lpParameters, 2923 lpDirectory, 2924 lpReturn, 2925 lpTitle, 2926 lpReserved, 2927 nCmdShow, 2928 lphProcess, 2929 0); 2930 } 2931 2932 /************************************************************************* 2933 * RealShellExecuteW (SHELL32.268) 2934 */ 2935 EXTERN_C 2936 HINSTANCE WINAPI 2937 RealShellExecuteW( 2938 _In_opt_ HWND hwnd, 2939 _In_opt_ LPCWSTR lpOperation, 2940 _In_opt_ LPCWSTR lpFile, 2941 _In_opt_ LPCWSTR lpParameters, 2942 _In_opt_ LPCWSTR lpDirectory, 2943 _In_opt_ LPWSTR lpReturn, 2944 _In_opt_ LPCWSTR lpTitle, 2945 _In_opt_ LPVOID lpReserved, 2946 _In_ INT nCmdShow, 2947 _Out_opt_ PHANDLE lphProcess) 2948 { 2949 return RealShellExecuteExW(hwnd, 2950 lpOperation, 2951 lpFile, 2952 lpParameters, 2953 lpDirectory, 2954 lpReturn, 2955 lpTitle, 2956 lpReserved, 2957 nCmdShow, 2958 lphProcess, 2959 0); 2960 } 2961