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