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