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 hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL); 1084 if (!hConv) 1085 { 1086 TRACE("Couldn't connect. ret=%d\n", ret); 1087 DdeUninitialize(ddeInst); 1088 SetLastError(ERROR_DDE_FAIL); 1089 return 30; /* whatever */ 1090 } 1091 static const WCHAR wIfexec[] = L"\\ifexec"; 1092 if (strlenW(wIfexec) + 1 > endkeyLen) 1093 { 1094 FIXME("endkey %s overruns buffer\n", debugstr_w(wIfexec)); 1095 return 2; 1096 } 1097 strcpyW(endkey, wIfexec); 1098 ifexeclen = sizeof(ifexec); 1099 if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, ifexec, &ifexeclen) == ERROR_SUCCESS) 1100 { 1101 exec = ifexec; 1102 } 1103 } 1104 1105 SHELL_ArgifyW(static_res, sizeof(static_res)/sizeof(WCHAR), exec, lpFile, pidl, szCommandline, &resultLen, NULL); 1106 if (resultLen > sizeof(static_res)/sizeof(WCHAR)) 1107 { 1108 res = dynamic_res = static_cast<WCHAR *>(HeapAlloc(GetProcessHeap(), 0, resultLen * sizeof(WCHAR))); 1109 SHELL_ArgifyW(dynamic_res, resultLen, exec, lpFile, pidl, szCommandline, NULL, NULL); 1110 } 1111 else 1112 res = static_res; 1113 TRACE("%s %s => %s\n", debugstr_w(exec), debugstr_w(lpFile), debugstr_w(res)); 1114 1115 /* It's documented in the KB 330337 that IE has a bug and returns 1116 * error DMLERR_NOTPROCESSED on XTYP_EXECUTE request. 1117 */ 1118 if (unicode) 1119 hDdeData = DdeClientTransaction((LPBYTE)res, (strlenW(res) + 1) * sizeof(WCHAR), hConv, 0L, 0, XTYP_EXECUTE, 30000, &tid); 1120 else 1121 { 1122 DWORD lenA = WideCharToMultiByte(CP_ACP, 0, res, -1, NULL, 0, NULL, NULL); 1123 char *resA = (LPSTR)HeapAlloc(GetProcessHeap(), 0, lenA); 1124 WideCharToMultiByte(CP_ACP, 0, res, -1, resA, lenA, NULL, NULL); 1125 hDdeData = DdeClientTransaction( (LPBYTE)resA, lenA, hConv, 0L, 0, 1126 XTYP_EXECUTE, 10000, &tid ); 1127 HeapFree(GetProcessHeap(), 0, resA); 1128 } 1129 if (hDdeData) 1130 DdeFreeDataHandle(hDdeData); 1131 else 1132 WARN("DdeClientTransaction failed with error %04x\n", DdeGetLastError(ddeInst)); 1133 ret = 33; 1134 1135 HeapFree(GetProcessHeap(), 0, dynamic_res); 1136 1137 DdeDisconnect(hConv); 1138 1139 error: 1140 DdeUninitialize(ddeInst); 1141 1142 return ret; 1143 } 1144 1145 /************************************************************************* 1146 * execute_from_key [Internal] 1147 */ 1148 static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, 1149 LPCWSTR szCommandline, LPCWSTR executable_name, 1150 SHELL_ExecuteW32 execfunc, 1151 LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out) 1152 { 1153 WCHAR cmd[256], param[1024], ddeexec[256]; 1154 DWORD cmdlen = sizeof(cmd), ddeexeclen = sizeof(ddeexec); 1155 UINT_PTR retval = SE_ERR_NOASSOC; 1156 DWORD resultLen; 1157 LPWSTR tmp; 1158 1159 TRACE("%s %s %s %s %s\n", debugstr_w(key), debugstr_w(lpFile), debugstr_w(env), 1160 debugstr_w(szCommandline), debugstr_w(executable_name)); 1161 1162 cmd[0] = '\0'; 1163 param[0] = '\0'; 1164 1165 /* Get the application from the registry */ 1166 if (RegQueryValueW(HKEY_CLASSES_ROOT, key, cmd, (LONG *)&cmdlen) == ERROR_SUCCESS) 1167 { 1168 TRACE("got cmd: %s\n", debugstr_w(cmd)); 1169 1170 /* Is there a replace() function anywhere? */ 1171 cmdlen /= sizeof(WCHAR); 1172 if (cmdlen >= sizeof(cmd) / sizeof(WCHAR)) 1173 cmdlen = sizeof(cmd) / sizeof(WCHAR) - 1; 1174 cmd[cmdlen] = '\0'; 1175 SHELL_ArgifyW(param, sizeof(param) / sizeof(WCHAR), cmd, lpFile, (LPITEMIDLIST)psei->lpIDList, szCommandline, &resultLen, 1176 (psei->lpDirectory && *psei->lpDirectory) ? psei->lpDirectory : NULL); 1177 if (resultLen > sizeof(param) / sizeof(WCHAR)) 1178 ERR("Argify buffer not large enough, truncating\n"); 1179 } 1180 1181 /* Get the parameters needed by the application 1182 from the associated ddeexec key */ 1183 tmp = const_cast<LPWSTR>(strstrW(key, L"command")); 1184 assert(tmp); 1185 wcscpy(tmp, L"ddeexec"); 1186 1187 if (RegQueryValueW(HKEY_CLASSES_ROOT, key, ddeexec, (LONG *)&ddeexeclen) == ERROR_SUCCESS) 1188 { 1189 TRACE("Got ddeexec %s => %s\n", debugstr_w(key), debugstr_w(ddeexec)); 1190 if (!param[0]) strcpyW(param, executable_name); 1191 retval = dde_connect(key, param, ddeexec, lpFile, env, szCommandline, (LPITEMIDLIST)psei->lpIDList, execfunc, psei, psei_out); 1192 } 1193 else if (param[0]) 1194 { 1195 TRACE("executing: %s\n", debugstr_w(param)); 1196 retval = execfunc(param, env, FALSE, psei, psei_out); 1197 } 1198 else 1199 WARN("Nothing appropriate found for %s\n", debugstr_w(key)); 1200 1201 return retval; 1202 } 1203 1204 /************************************************************************* 1205 * FindExecutableA [SHELL32.@] 1206 */ 1207 HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult) 1208 { 1209 HINSTANCE retval; 1210 WCHAR *wFile = NULL, *wDirectory = NULL; 1211 WCHAR wResult[MAX_PATH]; 1212 1213 if (lpFile) __SHCloneStrAtoW(&wFile, lpFile); 1214 if (lpDirectory) __SHCloneStrAtoW(&wDirectory, lpDirectory); 1215 1216 retval = FindExecutableW(wFile, wDirectory, wResult); 1217 WideCharToMultiByte(CP_ACP, 0, wResult, -1, lpResult, MAX_PATH, NULL, NULL); 1218 SHFree(wFile); 1219 SHFree(wDirectory); 1220 1221 TRACE("returning %s\n", lpResult); 1222 return retval; 1223 } 1224 1225 /************************************************************************* 1226 * FindExecutableW [SHELL32.@] 1227 * 1228 * This function returns the executable associated with the specified file 1229 * for the default verb. 1230 * 1231 * PARAMS 1232 * lpFile [I] The file to find the association for. This must refer to 1233 * an existing file otherwise FindExecutable fails and returns 1234 * SE_ERR_FNF. 1235 * lpResult [O] Points to a buffer into which the executable path is 1236 * copied. This parameter must not be NULL otherwise 1237 * FindExecutable() segfaults. The buffer must be of size at 1238 * least MAX_PATH characters. 1239 * 1240 * RETURNS 1241 * A value greater than 32 on success, less than or equal to 32 otherwise. 1242 * See the SE_ERR_* constants. 1243 * 1244 * NOTES 1245 * On Windows XP and 2003, FindExecutable() seems to first convert the 1246 * filename into 8.3 format, thus taking into account only the first three 1247 * characters of the extension, and expects to find an association for those. 1248 * However other Windows versions behave sanely. 1249 */ 1250 HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult) 1251 { 1252 UINT_PTR retval = SE_ERR_NOASSOC; 1253 WCHAR old_dir[1024]; 1254 WCHAR res[MAX_PATH]; 1255 1256 TRACE("File %s, Dir %s\n", debugstr_w(lpFile), debugstr_w(lpDirectory)); 1257 1258 lpResult[0] = '\0'; /* Start off with an empty return string */ 1259 if (lpFile == NULL) 1260 return (HINSTANCE)SE_ERR_FNF; 1261 1262 if (lpDirectory) 1263 { 1264 GetCurrentDirectoryW(sizeof(old_dir) / sizeof(WCHAR), old_dir); 1265 SetCurrentDirectoryW(lpDirectory); 1266 } 1267 1268 retval = SHELL_FindExecutable(lpDirectory, lpFile, wszOpen, res, MAX_PATH, NULL, NULL, NULL, NULL); 1269 if (retval > 32) 1270 strcpyW(lpResult, res); 1271 1272 TRACE("returning %s\n", debugstr_w(lpResult)); 1273 if (lpDirectory) 1274 SetCurrentDirectoryW(old_dir); 1275 return (HINSTANCE)retval; 1276 } 1277 1278 /* FIXME: is this already implemented somewhere else? */ 1279 static HKEY ShellExecute_GetClassKey(const SHELLEXECUTEINFOW *sei) 1280 { 1281 LPCWSTR ext = NULL, lpClass = NULL; 1282 LPWSTR cls = NULL; 1283 DWORD type = 0, sz = 0; 1284 HKEY hkey = 0; 1285 LONG r; 1286 1287 if (sei->fMask & SEE_MASK_CLASSALL) 1288 return sei->hkeyClass; 1289 1290 if (sei->fMask & SEE_MASK_CLASSNAME) 1291 lpClass = sei->lpClass; 1292 else 1293 { 1294 ext = PathFindExtensionW(sei->lpFile); 1295 TRACE("ext = %s\n", debugstr_w(ext)); 1296 if (!ext) 1297 return hkey; 1298 1299 r = RegOpenKeyW(HKEY_CLASSES_ROOT, ext, &hkey); 1300 if (r != ERROR_SUCCESS) 1301 return hkey; 1302 1303 r = RegQueryValueExW(hkey, NULL, 0, &type, NULL, &sz); 1304 if (r == ERROR_SUCCESS && type == REG_SZ) 1305 { 1306 sz += sizeof (WCHAR); 1307 cls = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sz); 1308 cls[0] = 0; 1309 RegQueryValueExW(hkey, NULL, 0, &type, (LPBYTE) cls, &sz); 1310 } 1311 1312 RegCloseKey( hkey ); 1313 lpClass = cls; 1314 } 1315 1316 TRACE("class = %s\n", debugstr_w(lpClass)); 1317 1318 hkey = 0; 1319 if (lpClass) 1320 RegOpenKeyW( HKEY_CLASSES_ROOT, lpClass, &hkey); 1321 1322 HeapFree(GetProcessHeap(), 0, cls); 1323 1324 return hkey; 1325 } 1326 1327 static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei ) 1328 { 1329 LPCITEMIDLIST pidllast = NULL; 1330 CComPtr<IDataObject> dataobj; 1331 CComPtr<IShellFolder> shf; 1332 LPITEMIDLIST pidl = NULL; 1333 HRESULT r; 1334 1335 if (sei->fMask & SEE_MASK_CLASSALL) 1336 pidl = (LPITEMIDLIST)sei->lpIDList; 1337 else 1338 { 1339 WCHAR fullpath[MAX_PATH]; 1340 BOOL ret; 1341 1342 fullpath[0] = 0; 1343 ret = GetFullPathNameW(sei->lpFile, MAX_PATH, fullpath, NULL); 1344 if (!ret) 1345 goto end; 1346 1347 pidl = ILCreateFromPathW(fullpath); 1348 } 1349 1350 r = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); 1351 if (FAILED(r)) 1352 goto end; 1353 1354 shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IDataObject, &dataobj)); 1355 1356 end: 1357 if (pidl != sei->lpIDList) 1358 ILFree(pidl); 1359 return dataobj.Detach(); 1360 } 1361 1362 static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, 1363 LPSHELLEXECUTEINFOW sei) 1364 { 1365 CComPtr<IContextMenu> cm = NULL; 1366 CMINVOKECOMMANDINFOEX ici; 1367 MENUITEMINFOW info; 1368 WCHAR string[0x80]; 1369 INT i, n, def = -1; 1370 HMENU hmenu = 0; 1371 HRESULT r; 1372 1373 TRACE("%p %p\n", obj, sei); 1374 1375 r = obj->QueryInterface(IID_PPV_ARG(IContextMenu, &cm)); 1376 if (FAILED(r)) 1377 return r; 1378 1379 hmenu = CreateMenu(); 1380 if (!hmenu) 1381 goto end; 1382 1383 /* the number of the last menu added is returned in r */ 1384 r = cm->QueryContextMenu(hmenu, 0, 0x20, 0x7fff, CMF_DEFAULTONLY); 1385 if (FAILED(r)) 1386 goto end; 1387 1388 n = GetMenuItemCount(hmenu); 1389 for (i = 0; i < n; i++) 1390 { 1391 memset(&info, 0, sizeof(info)); 1392 info.cbSize = sizeof info; 1393 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID; 1394 info.dwTypeData = string; 1395 info.cch = sizeof string; 1396 string[0] = 0; 1397 GetMenuItemInfoW(hmenu, i, TRUE, &info); 1398 1399 TRACE("menu %d %s %08x %08lx %08x %08x\n", i, debugstr_w(string), 1400 info.fState, info.dwItemData, info.fType, info.wID); 1401 if ((!sei->lpVerb && (info.fState & MFS_DEFAULT)) || 1402 (sei->lpVerb && !lstrcmpiW(sei->lpVerb, string))) 1403 { 1404 def = i; 1405 break; 1406 } 1407 } 1408 1409 r = E_FAIL; 1410 if (def == -1) 1411 goto end; 1412 1413 memset(&ici, 0, sizeof ici); 1414 ici.cbSize = sizeof ici; 1415 ici.fMask = CMIC_MASK_UNICODE | (sei->fMask & (SEE_MASK_NO_CONSOLE | SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); 1416 ici.nShow = sei->nShow; 1417 ici.lpVerb = MAKEINTRESOURCEA(def); 1418 ici.hwnd = sei->hwnd; 1419 ici.lpParametersW = sei->lpParameters; 1420 1421 r = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); 1422 1423 TRACE("invoke command returned %08x\n", r); 1424 1425 end: 1426 if (hmenu) 1427 DestroyMenu( hmenu ); 1428 return r; 1429 } 1430 1431 static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei) 1432 { 1433 // Can not use CComPtr here because of CoUninitialize at the end, before the destructors would run. 1434 IDataObject *dataobj = NULL; 1435 IObjectWithSite *ows = NULL; 1436 IShellExtInit *obj = NULL; 1437 HRESULT r; 1438 1439 TRACE("%p %s %p\n", hkey, debugstr_guid(guid), sei); 1440 1441 r = CoInitialize(NULL); 1442 if (FAILED(r)) 1443 goto end; 1444 1445 r = CoCreateInstance(*guid, NULL, CLSCTX_INPROC_SERVER, 1446 IID_PPV_ARG(IShellExtInit, &obj)); 1447 if (FAILED(r)) 1448 { 1449 ERR("failed %08x\n", r); 1450 goto end; 1451 } 1452 1453 dataobj = shellex_get_dataobj(sei); 1454 if (!dataobj) 1455 { 1456 ERR("failed to get data object\n"); 1457 r = E_FAIL; 1458 goto end; 1459 } 1460 1461 r = obj->Initialize(NULL, dataobj, hkey); 1462 if (FAILED(r)) 1463 goto end; 1464 1465 r = obj->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows)); 1466 if (FAILED(r)) 1467 goto end; 1468 1469 ows->SetSite(NULL); 1470 1471 r = shellex_run_context_menu_default(obj, sei); 1472 1473 end: 1474 if (ows) 1475 ows->Release(); 1476 if (dataobj) 1477 dataobj->Release(); 1478 if (obj) 1479 obj->Release(); 1480 CoUninitialize(); 1481 return r; 1482 } 1483 1484 1485 /************************************************************************* 1486 * ShellExecute_FromContextMenu [Internal] 1487 */ 1488 static LONG ShellExecute_FromContextMenu( LPSHELLEXECUTEINFOW sei ) 1489 { 1490 HKEY hkey, hkeycm = 0; 1491 WCHAR szguid[39]; 1492 HRESULT hr; 1493 GUID guid; 1494 DWORD i; 1495 LONG r; 1496 1497 TRACE("%s\n", debugstr_w(sei->lpFile)); 1498 1499 hkey = ShellExecute_GetClassKey(sei); 1500 if (!hkey) 1501 return ERROR_FUNCTION_FAILED; 1502 1503 r = RegOpenKeyW(hkey, L"shellex\\ContextMenuHandlers", &hkeycm); 1504 if (r == ERROR_SUCCESS) 1505 { 1506 i = 0; 1507 while (1) 1508 { 1509 r = RegEnumKeyW(hkeycm, i++, szguid, sizeof(szguid) / sizeof(szguid[0])); 1510 if (r != ERROR_SUCCESS) 1511 break; 1512 1513 hr = CLSIDFromString(szguid, &guid); 1514 if (SUCCEEDED(hr)) 1515 { 1516 /* stop at the first one that succeeds in running */ 1517 hr = shellex_load_object_and_run(hkey, &guid, sei); 1518 if (SUCCEEDED(hr)) 1519 break; 1520 } 1521 } 1522 RegCloseKey(hkeycm); 1523 } 1524 1525 if (hkey != sei->hkeyClass) 1526 RegCloseKey(hkey); 1527 return r; 1528 } 1529 1530 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); 1531 1532 static UINT_PTR SHELL_execute_class(LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) 1533 { 1534 WCHAR execCmd[1024], classname[1024]; 1535 /* launch a document by fileclass like 'WordPad.Document.1' */ 1536 /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */ 1537 /* FIXME: wcmd should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */ 1538 ULONG cmask = (psei->fMask & SEE_MASK_CLASSALL); 1539 DWORD resultLen; 1540 BOOL done; 1541 UINT_PTR rslt; 1542 1543 /* FIXME: remove following block when SHELL_quote_and_execute supports hkeyClass parameter */ 1544 if (cmask != SEE_MASK_CLASSNAME) 1545 { 1546 WCHAR wcmd[1024]; 1547 HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? psei->hkeyClass : NULL, 1548 (cmask == SEE_MASK_CLASSNAME) ? psei->lpClass : NULL, 1549 psei->lpVerb, 1550 execCmd, sizeof(execCmd)); 1551 1552 /* FIXME: get the extension of lpFile, check if it fits to the lpClass */ 1553 TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(execCmd), debugstr_w(wszApplicationName)); 1554 1555 wcmd[0] = '\0'; 1556 done = SHELL_ArgifyW(wcmd, sizeof(wcmd) / sizeof(WCHAR), execCmd, wszApplicationName, (LPITEMIDLIST)psei->lpIDList, NULL, &resultLen, 1557 (psei->lpDirectory && *psei->lpDirectory) ? psei->lpDirectory : NULL); 1558 if (!done && wszApplicationName[0]) 1559 { 1560 strcatW(wcmd, L" "); 1561 if (*wszApplicationName != '"') 1562 { 1563 strcatW(wcmd, L"\""); 1564 strcatW(wcmd, wszApplicationName); 1565 strcatW(wcmd, L"\""); 1566 } 1567 else 1568 strcatW(wcmd, wszApplicationName); 1569 } 1570 if (resultLen > sizeof(wcmd) / sizeof(WCHAR)) 1571 ERR("Argify buffer not large enough... truncating\n"); 1572 return execfunc(wcmd, NULL, FALSE, psei, psei_out); 1573 } 1574 1575 strcpyW(classname, psei->lpClass); 1576 rslt = SHELL_FindExecutableByVerb(psei->lpVerb, NULL, classname, execCmd, sizeof(execCmd)); 1577 1578 TRACE("SHELL_FindExecutableByVerb returned %u (%s, %s)\n", (unsigned int)rslt, debugstr_w(classname), debugstr_w(execCmd)); 1579 if (33 > rslt) 1580 return rslt; 1581 rslt = SHELL_quote_and_execute( execCmd, L"", classname, 1582 wszApplicationName, NULL, psei, 1583 psei_out, execfunc ); 1584 return rslt; 1585 1586 } 1587 1588 static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen) 1589 { 1590 static const WCHAR wExplorer[] = L"explorer.exe"; 1591 WCHAR buffer[MAX_PATH]; 1592 BOOL appKnownSingular = FALSE; 1593 1594 /* last chance to translate IDList: now also allow CLSID paths */ 1595 if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW((LPCITEMIDLIST)sei->lpIDList, buffer, sizeof(buffer)/sizeof(WCHAR)))) { 1596 if (buffer[0] == ':' && buffer[1] == ':') { 1597 /* open shell folder for the specified class GUID */ 1598 if (strlenW(buffer) + 1 > parametersLen) 1599 ERR("parameters len exceeds buffer size (%i > %i), truncating\n", 1600 lstrlenW(buffer) + 1, parametersLen); 1601 lstrcpynW(wszParameters, buffer, parametersLen); 1602 if (strlenW(wExplorer) > dwApplicationNameLen) 1603 ERR("application len exceeds buffer size (%i > %i), truncating\n", 1604 lstrlenW(wExplorer) + 1, dwApplicationNameLen); 1605 lstrcpynW(wszApplicationName, wExplorer, dwApplicationNameLen); 1606 appKnownSingular = TRUE; 1607 1608 sei->fMask &= ~SEE_MASK_INVOKEIDLIST; 1609 } else { 1610 WCHAR target[MAX_PATH]; 1611 DWORD attribs; 1612 DWORD resultLen; 1613 /* Check if we're executing a directory and if so use the 1614 handler for the Folder class */ 1615 strcpyW(target, buffer); 1616 attribs = GetFileAttributesW(buffer); 1617 if (attribs != INVALID_FILE_ATTRIBUTES && 1618 (attribs & FILE_ATTRIBUTE_DIRECTORY) && 1619 HCR_GetExecuteCommandW(0, L"Folder", 1620 sei->lpVerb, 1621 buffer, sizeof(buffer))) { 1622 SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen, 1623 buffer, target, (LPITEMIDLIST)sei->lpIDList, NULL, &resultLen, 1624 (sei->lpDirectory && *sei->lpDirectory) ? sei->lpDirectory : NULL); 1625 if (resultLen > dwApplicationNameLen) 1626 ERR("Argify buffer not large enough... truncating\n"); 1627 appKnownSingular = FALSE; 1628 } 1629 sei->fMask &= ~SEE_MASK_INVOKEIDLIST; 1630 } 1631 } 1632 return appKnownSingular; 1633 } 1634 1635 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) 1636 { 1637 UINT_PTR retval; 1638 DWORD len; 1639 WCHAR *wszQuotedCmd; 1640 1641 /* Length of quotes plus length of command plus NULL terminator */ 1642 len = 2 + lstrlenW(wcmd) + 1; 1643 if (wszParameters[0]) 1644 { 1645 /* Length of space plus length of parameters */ 1646 len += 1 + lstrlenW(wszParameters); 1647 } 1648 wszQuotedCmd = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1649 /* Must quote to handle case where cmd contains spaces, 1650 * else security hole if malicious user creates executable file "C:\\Program" 1651 */ 1652 strcpyW(wszQuotedCmd, L"\""); 1653 strcatW(wszQuotedCmd, wcmd); 1654 strcatW(wszQuotedCmd, L"\""); 1655 if (wszParameters[0]) 1656 { 1657 strcatW(wszQuotedCmd, L" "); 1658 strcatW(wszQuotedCmd, wszParameters); 1659 } 1660 1661 TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(wszKeyname)); 1662 1663 if (*wszKeyname) 1664 retval = execute_from_key(wszKeyname, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out); 1665 else 1666 retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out); 1667 HeapFree(GetProcessHeap(), 0, wszQuotedCmd); 1668 return retval; 1669 } 1670 1671 static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) 1672 { 1673 static const WCHAR wShell[] = L"\\shell\\"; 1674 static const WCHAR wCommand[] = L"\\command"; 1675 UINT_PTR retval; 1676 WCHAR *lpstrProtocol; 1677 LPCWSTR lpstrRes; 1678 INT iSize; 1679 DWORD len; 1680 1681 lpstrRes = strchrW(lpFile, ':'); 1682 if (lpstrRes) 1683 iSize = lpstrRes - lpFile; 1684 else 1685 iSize = strlenW(lpFile); 1686 1687 TRACE("Got URL: %s\n", debugstr_w(lpFile)); 1688 /* Looking for ...<protocol>\shell\<lpVerb>\command */ 1689 len = iSize + lstrlenW(wShell) + lstrlenW(wCommand) + 1; 1690 if (psei->lpVerb && *psei->lpVerb) 1691 len += lstrlenW(psei->lpVerb); 1692 else 1693 len += lstrlenW(wszOpen); 1694 lpstrProtocol = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1695 memcpy(lpstrProtocol, lpFile, iSize * sizeof(WCHAR)); 1696 lpstrProtocol[iSize] = '\0'; 1697 strcatW(lpstrProtocol, wShell); 1698 strcatW(lpstrProtocol, psei->lpVerb && *psei->lpVerb ? psei->lpVerb : wszOpen); 1699 strcatW(lpstrProtocol, wCommand); 1700 1701 retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters, 1702 wcmd, execfunc, psei, psei_out); 1703 HeapFree(GetProcessHeap(), 0, lpstrProtocol); 1704 return retval; 1705 } 1706 1707 static void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename) 1708 { 1709 WCHAR msg[2048]; 1710 DWORD_PTR msgArguments[3] = { (DWORD_PTR)filename, 0, 0 }; 1711 DWORD error_code; 1712 1713 error_code = GetLastError(); 1714 if (retval == SE_ERR_NOASSOC) 1715 LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, sizeof(msg) / sizeof(WCHAR)); 1716 else 1717 FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, 1718 NULL, 1719 error_code, 1720 LANG_USER_DEFAULT, 1721 msg, 1722 sizeof(msg) / sizeof(WCHAR), 1723 (va_list*)msgArguments); 1724 1725 MessageBoxW(hwnd, msg, NULL, MB_ICONERROR); 1726 } 1727 1728 static WCHAR *expand_environment( const WCHAR *str ) 1729 { 1730 WCHAR *buf; 1731 DWORD len; 1732 1733 len = ExpandEnvironmentStringsW(str, NULL, 0); 1734 if (!len) return NULL; 1735 1736 buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1737 if (!buf) return NULL; 1738 1739 len = ExpandEnvironmentStringsW(str, buf, len); 1740 if (!len) 1741 { 1742 HeapFree(GetProcessHeap(), 0, buf); 1743 return NULL; 1744 } 1745 return buf; 1746 } 1747 1748 /************************************************************************* 1749 * SHELL_execute [Internal] 1750 */ 1751 static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) 1752 { 1753 static const DWORD unsupportedFlags = 1754 SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY | 1755 SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | 1756 SEE_MASK_UNICODE | SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR; 1757 1758 WCHAR parametersBuffer[1024], dirBuffer[MAX_PATH], wcmdBuffer[1024]; 1759 WCHAR *wszApplicationName, *wszParameters, *wszDir, *wcmd; 1760 DWORD dwApplicationNameLen = MAX_PATH + 2; 1761 DWORD parametersLen = sizeof(parametersBuffer) / sizeof(WCHAR); 1762 DWORD dirLen = sizeof(dirBuffer) / sizeof(WCHAR); 1763 DWORD wcmdLen = sizeof(wcmdBuffer) / sizeof(WCHAR); 1764 DWORD len; 1765 SHELLEXECUTEINFOW sei_tmp; /* modifiable copy of SHELLEXECUTEINFO struct */ 1766 WCHAR wfileName[MAX_PATH]; 1767 WCHAR *env; 1768 WCHAR wszKeyname[256]; 1769 LPCWSTR lpFile; 1770 UINT_PTR retval = SE_ERR_NOASSOC; 1771 BOOL appKnownSingular = FALSE; 1772 1773 /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */ 1774 sei_tmp = *sei; 1775 1776 TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n", 1777 sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb), 1778 debugstr_w(sei_tmp.lpFile), debugstr_w(sei_tmp.lpParameters), 1779 debugstr_w(sei_tmp.lpDirectory), sei_tmp.nShow, 1780 ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ? 1781 debugstr_w(sei_tmp.lpClass) : "not used"); 1782 1783 sei->hProcess = NULL; 1784 1785 /* make copies of all path/command strings */ 1786 if (!sei_tmp.lpFile) 1787 { 1788 wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen * sizeof(WCHAR)); 1789 *wszApplicationName = '\0'; 1790 } 1791 else if (*sei_tmp.lpFile == '\"' && sei_tmp.lpFile[(len = strlenW(sei_tmp.lpFile))-1] == '\"') 1792 { 1793 if(len-1 >= dwApplicationNameLen) 1794 dwApplicationNameLen = len; 1795 1796 wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen * sizeof(WCHAR)); 1797 memcpy(wszApplicationName, sei_tmp.lpFile + 1, len * sizeof(WCHAR)); 1798 1799 if(len > 2) 1800 wszApplicationName[len-2] = '\0'; 1801 appKnownSingular = TRUE; 1802 1803 TRACE("wszApplicationName=%s\n", debugstr_w(wszApplicationName)); 1804 } 1805 else 1806 { 1807 DWORD l = strlenW(sei_tmp.lpFile) + 1; 1808 if(l > dwApplicationNameLen) dwApplicationNameLen = l + 1; 1809 wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen * sizeof(WCHAR)); 1810 memcpy(wszApplicationName, sei_tmp.lpFile, l * sizeof(WCHAR)); 1811 } 1812 1813 wszParameters = parametersBuffer; 1814 if (sei_tmp.lpParameters) 1815 { 1816 len = lstrlenW(sei_tmp.lpParameters) + 1; 1817 if (len > parametersLen) 1818 { 1819 wszParameters = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1820 parametersLen = len; 1821 } 1822 strcpyW(wszParameters, sei_tmp.lpParameters); 1823 } 1824 else 1825 *wszParameters = L'\0'; 1826 1827 wszDir = dirBuffer; 1828 if (sei_tmp.lpDirectory) 1829 { 1830 len = lstrlenW(sei_tmp.lpDirectory) + 1; 1831 if (len > dirLen) 1832 { 1833 wszDir = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1834 dirLen = len; 1835 } 1836 strcpyW(wszDir, sei_tmp.lpDirectory); 1837 } 1838 else 1839 *wszDir = L'\0'; 1840 1841 /* adjust string pointers to point to the new buffers */ 1842 sei_tmp.lpFile = wszApplicationName; 1843 sei_tmp.lpParameters = wszParameters; 1844 sei_tmp.lpDirectory = wszDir; 1845 1846 if (sei_tmp.fMask & unsupportedFlags) 1847 { 1848 FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags); 1849 } 1850 1851 /* process the IDList */ 1852 if (sei_tmp.fMask & SEE_MASK_IDLIST) 1853 { 1854 CComPtr<IShellExecuteHookW> pSEH; 1855 1856 HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_PPV_ARG(IShellExecuteHookW, &pSEH), NULL); 1857 1858 if (SUCCEEDED(hr)) 1859 { 1860 hr = pSEH->Execute(&sei_tmp); 1861 1862 if (hr == S_OK) 1863 { 1864 HeapFree(GetProcessHeap(), 0, wszApplicationName); 1865 if (wszParameters != parametersBuffer) 1866 HeapFree(GetProcessHeap(), 0, wszParameters); 1867 if (wszDir != dirBuffer) 1868 HeapFree(GetProcessHeap(), 0, wszDir); 1869 return TRUE; 1870 } 1871 } 1872 1873 SHGetPathFromIDListW((LPCITEMIDLIST)sei_tmp.lpIDList, wszApplicationName); 1874 appKnownSingular = TRUE; 1875 TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName)); 1876 } 1877 1878 if (sei_tmp.fMask & SEE_MASK_DOENVSUBST) 1879 { 1880 WCHAR *tmp; 1881 1882 tmp = expand_environment(sei_tmp.lpFile); 1883 if (!tmp) 1884 { 1885 return FALSE; 1886 } 1887 HeapFree(GetProcessHeap(), 0, wszApplicationName); 1888 sei_tmp.lpFile = wszApplicationName = tmp; 1889 1890 tmp = expand_environment(sei_tmp.lpDirectory); 1891 if (!tmp) 1892 { 1893 return FALSE; 1894 } 1895 if (wszDir != dirBuffer) HeapFree(GetProcessHeap(), 0, wszDir); 1896 sei_tmp.lpDirectory = wszDir = tmp; 1897 } 1898 1899 if (ERROR_SUCCESS == ShellExecute_FromContextMenu(&sei_tmp)) 1900 { 1901 sei->hInstApp = (HINSTANCE) 33; 1902 HeapFree(GetProcessHeap(), 0, wszApplicationName); 1903 if (wszParameters != parametersBuffer) 1904 HeapFree(GetProcessHeap(), 0, wszParameters); 1905 if (wszDir != dirBuffer) 1906 HeapFree(GetProcessHeap(), 0, wszDir); 1907 return TRUE; 1908 } 1909 1910 if (sei_tmp.fMask & SEE_MASK_CLASSALL) 1911 { 1912 retval = SHELL_execute_class(wszApplicationName, &sei_tmp, sei, execfunc); 1913 if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) 1914 { 1915 OPENASINFO Info; 1916 1917 //FIXME 1918 // need full path 1919 1920 Info.pcszFile = wszApplicationName; 1921 Info.pcszClass = NULL; 1922 Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; 1923 1924 //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK) 1925 DBG_UNREFERENCED_LOCAL_VARIABLE(Info); 1926 do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName); 1927 } 1928 HeapFree(GetProcessHeap(), 0, wszApplicationName); 1929 if (wszParameters != parametersBuffer) 1930 HeapFree(GetProcessHeap(), 0, wszParameters); 1931 if (wszDir != dirBuffer) 1932 HeapFree(GetProcessHeap(), 0, wszDir); 1933 return retval > 32; 1934 } 1935 1936 /* Has the IDList not yet been translated? */ 1937 if (sei_tmp.fMask & SEE_MASK_IDLIST) 1938 { 1939 appKnownSingular = SHELL_translate_idlist( &sei_tmp, wszParameters, 1940 parametersLen, 1941 wszApplicationName, 1942 dwApplicationNameLen ); 1943 } 1944 1945 /* convert file URLs */ 1946 if (UrlIsFileUrlW(sei_tmp.lpFile)) 1947 { 1948 LPWSTR buf; 1949 DWORD size; 1950 1951 size = MAX_PATH; 1952 buf = static_cast<LPWSTR>(HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR))); 1953 if (!buf || FAILED(PathCreateFromUrlW(sei_tmp.lpFile, buf, &size, 0))) 1954 { 1955 HeapFree(GetProcessHeap(), 0, buf); 1956 return SE_ERR_OOM; 1957 } 1958 1959 HeapFree(GetProcessHeap(), 0, wszApplicationName); 1960 wszApplicationName = buf; 1961 sei_tmp.lpFile = wszApplicationName; 1962 } 1963 else /* or expand environment strings (not both!) */ 1964 { 1965 len = ExpandEnvironmentStringsW(sei_tmp.lpFile, NULL, 0); 1966 if (len > 0) 1967 { 1968 LPWSTR buf; 1969 buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); 1970 1971 ExpandEnvironmentStringsW(sei_tmp.lpFile, buf, len + 1); 1972 HeapFree(GetProcessHeap(), 0, wszApplicationName); 1973 wszApplicationName = buf; 1974 /* appKnownSingular unmodified */ 1975 1976 sei_tmp.lpFile = wszApplicationName; 1977 } 1978 } 1979 1980 if (*sei_tmp.lpDirectory) 1981 { 1982 len = ExpandEnvironmentStringsW(sei_tmp.lpDirectory, NULL, 0); 1983 if (len > 0) 1984 { 1985 LPWSTR buf; 1986 len++; 1987 buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1988 ExpandEnvironmentStringsW(sei_tmp.lpDirectory, buf, len); 1989 if (wszDir != dirBuffer) 1990 HeapFree(GetProcessHeap(), 0, wszDir); 1991 wszDir = buf; 1992 sei_tmp.lpDirectory = wszDir; 1993 } 1994 } 1995 1996 /* Else, try to execute the filename */ 1997 TRACE("execute: %s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir)); 1998 1999 /* separate out command line arguments from executable file name */ 2000 if (!*sei_tmp.lpParameters && !appKnownSingular) 2001 { 2002 /* If the executable path is quoted, handle the rest of the command line as parameters. */ 2003 if (sei_tmp.lpFile[0] == L'"') 2004 { 2005 LPWSTR src = wszApplicationName/*sei_tmp.lpFile*/ + 1; 2006 LPWSTR dst = wfileName; 2007 LPWSTR end; 2008 2009 /* copy the unquoted executable path to 'wfileName' */ 2010 while(*src && *src != L'"') 2011 *dst++ = *src++; 2012 2013 *dst = L'\0'; 2014 2015 if (*src == L'"') 2016 { 2017 end = ++src; 2018 2019 while(isspaceW(*src)) 2020 ++src; 2021 } 2022 else 2023 end = src; 2024 2025 /* copy the parameter string to 'wszParameters' */ 2026 strcpyW(wszParameters, src); 2027 2028 /* terminate previous command string after the quote character */ 2029 *end = L'\0'; 2030 lpFile = wfileName; 2031 } 2032 else 2033 { 2034 lpFile = sei_tmp.lpFile; 2035 } 2036 } 2037 else 2038 lpFile = sei_tmp.lpFile; 2039 2040 wcmd = wcmdBuffer; 2041 len = lstrlenW(wszApplicationName) + 3; 2042 if (sei_tmp.lpParameters[0]) 2043 len += 1 + lstrlenW(wszParameters); 2044 if (len > wcmdLen) 2045 { 2046 wcmd = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 2047 wcmdLen = len; 2048 } 2049 swprintf(wcmd, L"\"%s\"", wszApplicationName); 2050 if (sei_tmp.lpParameters[0]) 2051 { 2052 strcatW(wcmd, L" "); 2053 strcatW(wcmd, wszParameters); 2054 } 2055 2056 retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei); 2057 if (retval > 32) 2058 { 2059 HeapFree(GetProcessHeap(), 0, wszApplicationName); 2060 if (wszParameters != parametersBuffer) 2061 HeapFree(GetProcessHeap(), 0, wszParameters); 2062 if (wszDir != dirBuffer) 2063 HeapFree(GetProcessHeap(), 0, wszDir); 2064 if (wcmd != wcmdBuffer) 2065 HeapFree(GetProcessHeap(), 0, wcmd); 2066 return TRUE; 2067 } 2068 2069 /* Else, try to find the executable */ 2070 wcmd[0] = L'\0'; 2071 retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, wszKeyname, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters); 2072 if (retval > 32) /* Found */ 2073 { 2074 retval = SHELL_quote_and_execute(wcmd, wszParameters, wszKeyname, 2075 wszApplicationName, env, &sei_tmp, 2076 sei, execfunc); 2077 HeapFree(GetProcessHeap(), 0, env); 2078 } 2079 else if (PathIsDirectoryW(lpFile)) 2080 { 2081 WCHAR wExec[MAX_PATH]; 2082 WCHAR * lpQuotedFile = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (strlenW(lpFile) + 3)); 2083 2084 if (lpQuotedFile) 2085 { 2086 retval = SHELL_FindExecutable(sei_tmp.lpDirectory, L"explorer", 2087 wszOpen, wExec, MAX_PATH, 2088 NULL, &env, NULL, NULL); 2089 if (retval > 32) 2090 { 2091 swprintf(lpQuotedFile, L"\"%s\"", lpFile); 2092 retval = SHELL_quote_and_execute(wExec, lpQuotedFile, 2093 wszKeyname, 2094 wszApplicationName, env, 2095 &sei_tmp, sei, execfunc); 2096 HeapFree(GetProcessHeap(), 0, env); 2097 } 2098 HeapFree(GetProcessHeap(), 0, lpQuotedFile); 2099 } 2100 else 2101 retval = 0; /* Out of memory */ 2102 } 2103 else if (PathIsURLW(lpFile)) /* File not found, check for URL */ 2104 { 2105 retval = SHELL_execute_url(lpFile, wcmd, &sei_tmp, sei, execfunc ); 2106 } 2107 /* Check if file specified is in the form www.??????.*** */ 2108 else if (!strncmpiW(lpFile, L"www", 3)) 2109 { 2110 /* if so, prefix lpFile with http:// and call ShellExecute */ 2111 WCHAR lpstrTmpFile[256]; 2112 strcpyW(lpstrTmpFile, L"http://"); 2113 strcatW(lpstrTmpFile, lpFile); 2114 retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0); 2115 } 2116 2117 TRACE("retval %lu\n", retval); 2118 2119 if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) 2120 { 2121 OPENASINFO Info; 2122 2123 //FIXME 2124 // need full path 2125 2126 Info.pcszFile = wszApplicationName; 2127 Info.pcszClass = NULL; 2128 Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; 2129 2130 //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK) 2131 DBG_UNREFERENCED_LOCAL_VARIABLE(Info); 2132 do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName); 2133 } 2134 2135 HeapFree(GetProcessHeap(), 0, wszApplicationName); 2136 if (wszParameters != parametersBuffer) 2137 HeapFree(GetProcessHeap(), 0, wszParameters); 2138 if (wszDir != dirBuffer) 2139 HeapFree(GetProcessHeap(), 0, wszDir); 2140 if (wcmd != wcmdBuffer) 2141 HeapFree(GetProcessHeap(), 0, wcmd); 2142 2143 sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval); 2144 2145 return retval > 32; 2146 } 2147 2148 /************************************************************************* 2149 * ShellExecuteA [SHELL32.290] 2150 */ 2151 HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, 2152 LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd) 2153 { 2154 SHELLEXECUTEINFOA sei; 2155 2156 TRACE("%p,%s,%s,%s,%s,%d\n", 2157 hWnd, debugstr_a(lpVerb), debugstr_a(lpFile), 2158 debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd); 2159 2160 sei.cbSize = sizeof(sei); 2161 sei.fMask = SEE_MASK_FLAG_NO_UI; 2162 sei.hwnd = hWnd; 2163 sei.lpVerb = lpVerb; 2164 sei.lpFile = lpFile; 2165 sei.lpParameters = lpParameters; 2166 sei.lpDirectory = lpDirectory; 2167 sei.nShow = iShowCmd; 2168 sei.lpIDList = 0; 2169 sei.lpClass = 0; 2170 sei.hkeyClass = 0; 2171 sei.dwHotKey = 0; 2172 sei.hProcess = 0; 2173 2174 ShellExecuteExA(&sei); 2175 return sei.hInstApp; 2176 } 2177 2178 /************************************************************************* 2179 * ShellExecuteExA [SHELL32.292] 2180 * 2181 */ 2182 BOOL 2183 WINAPI 2184 DECLSPEC_HOTPATCH 2185 ShellExecuteExA(LPSHELLEXECUTEINFOA sei) 2186 { 2187 SHELLEXECUTEINFOW seiW; 2188 BOOL ret; 2189 WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL, *wClass = NULL; 2190 2191 TRACE("%p\n", sei); 2192 2193 memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW)); 2194 2195 if (sei->lpVerb) 2196 seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb); 2197 2198 if (sei->lpFile) 2199 seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile); 2200 2201 if (sei->lpParameters) 2202 seiW.lpParameters = __SHCloneStrAtoW(&wParameters, sei->lpParameters); 2203 2204 if (sei->lpDirectory) 2205 seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory); 2206 2207 if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME && sei->lpClass) 2208 seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass); 2209 else 2210 seiW.lpClass = NULL; 2211 2212 ret = SHELL_execute(&seiW, SHELL_ExecuteW); 2213 2214 sei->hInstApp = seiW.hInstApp; 2215 2216 if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) 2217 sei->hProcess = seiW.hProcess; 2218 2219 SHFree(wVerb); 2220 SHFree(wFile); 2221 SHFree(wParameters); 2222 SHFree(wDirectory); 2223 SHFree(wClass); 2224 2225 return ret; 2226 } 2227 2228 /************************************************************************* 2229 * ShellExecuteExW [SHELL32.293] 2230 * 2231 */ 2232 BOOL 2233 WINAPI 2234 DECLSPEC_HOTPATCH 2235 ShellExecuteExW(LPSHELLEXECUTEINFOW sei) 2236 { 2237 return SHELL_execute(sei, SHELL_ExecuteW); 2238 } 2239 2240 /************************************************************************* 2241 * ShellExecuteW [SHELL32.294] 2242 * from shellapi.h 2243 * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, 2244 * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd); 2245 */ 2246 HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile, 2247 LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd) 2248 { 2249 SHELLEXECUTEINFOW sei; 2250 2251 TRACE("\n"); 2252 sei.cbSize = sizeof(sei); 2253 sei.fMask = SEE_MASK_FLAG_NO_UI; 2254 sei.hwnd = hwnd; 2255 sei.lpVerb = lpVerb; 2256 sei.lpFile = lpFile; 2257 sei.lpParameters = lpParameters; 2258 sei.lpDirectory = lpDirectory; 2259 sei.nShow = nShowCmd; 2260 sei.lpIDList = 0; 2261 sei.lpClass = 0; 2262 sei.hkeyClass = 0; 2263 sei.dwHotKey = 0; 2264 sei.hProcess = 0; 2265 2266 SHELL_execute(&sei, SHELL_ExecuteW); 2267 return sei.hInstApp; 2268 } 2269 2270 /************************************************************************* 2271 * WOWShellExecute [SHELL32.@] 2272 * 2273 * FIXME: the callback function most likely doesn't work the same way on Windows. 2274 */ 2275 EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, 2276 LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd, void *callback) 2277 { 2278 SHELLEXECUTEINFOW seiW; 2279 WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL; 2280 HANDLE hProcess = 0; 2281 2282 seiW.lpVerb = lpVerb ? __SHCloneStrAtoW(&wVerb, lpVerb) : NULL; 2283 seiW.lpFile = lpFile ? __SHCloneStrAtoW(&wFile, lpFile) : NULL; 2284 seiW.lpParameters = lpParameters ? __SHCloneStrAtoW(&wParameters, lpParameters) : NULL; 2285 seiW.lpDirectory = lpDirectory ? __SHCloneStrAtoW(&wDirectory, lpDirectory) : NULL; 2286 2287 seiW.cbSize = sizeof(seiW); 2288 seiW.fMask = 0; 2289 seiW.hwnd = hWnd; 2290 seiW.nShow = iShowCmd; 2291 seiW.lpIDList = 0; 2292 seiW.lpClass = 0; 2293 seiW.hkeyClass = 0; 2294 seiW.dwHotKey = 0; 2295 seiW.hProcess = hProcess; 2296 2297 SHELL_execute(&seiW, (SHELL_ExecuteW32)callback); 2298 2299 SHFree(wVerb); 2300 SHFree(wFile); 2301 SHFree(wParameters); 2302 SHFree(wDirectory); 2303 return seiW.hInstApp; 2304 } 2305 2306 /************************************************************************* 2307 * OpenAs_RunDLLW [SHELL32.@] 2308 */ 2309 EXTERN_C void WINAPI 2310 OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow) 2311 { 2312 OPENASINFO info; 2313 TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow); 2314 2315 ZeroMemory(&info, sizeof(info)); 2316 info.pcszFile = cmdline; 2317 info.pcszClass = NULL; 2318 info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC; 2319 2320 SHOpenWithDialog(hwnd, &info); 2321 } 2322 2323 /************************************************************************* 2324 * OpenAs_RunDLLA [SHELL32.@] 2325 */ 2326 EXTERN_C void WINAPI 2327 OpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow) 2328 { 2329 LPWSTR pszCmdLineW = NULL; 2330 TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_a(cmdline), cmdshow); 2331 2332 if (cmdline) 2333 __SHCloneStrAtoW(&pszCmdLineW, cmdline); 2334 OpenAs_RunDLLW(hwnd, hinst, pszCmdLineW, cmdshow); 2335 SHFree(pszCmdLineW); 2336 } 2337 2338 /*************************************************************************/ 2339 2340 static LPCWSTR 2341 SplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0) 2342 { 2343 LPCWSTR pch; 2344 size_t ich = 0; 2345 if (*psz == L'"') 2346 { 2347 // 1st argument is quoted. the string in quotes is quoted 1st argument. 2348 // [pch] --> [pszArg0+ich] 2349 for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch) 2350 { 2351 if (*pch == L'"' && pch[1] == L'"') 2352 { 2353 // doubled double quotations found! 2354 pszArg0[ich] = L'"'; 2355 } 2356 else if (*pch == L'"') 2357 { 2358 // single double quotation found! 2359 ++pch; 2360 break; 2361 } 2362 else 2363 { 2364 // otherwise 2365 pszArg0[ich] = *pch; 2366 } 2367 } 2368 } 2369 else 2370 { 2371 // 1st argument is unquoted. non-space sequence is 1st argument. 2372 // [pch] --> [pszArg0+ich] 2373 for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch) 2374 { 2375 pszArg0[ich] = *pch; 2376 } 2377 } 2378 pszArg0[ich] = 0; 2379 2380 // skip space 2381 while (iswspace(*pch)) 2382 ++pch; 2383 2384 return pch; 2385 } 2386 2387 HRESULT WINAPI ShellExecCmdLine( 2388 HWND hwnd, 2389 LPCWSTR pwszCommand, 2390 LPCWSTR pwszStartDir, 2391 int nShow, 2392 LPVOID pUnused, 2393 DWORD dwSeclFlags) 2394 { 2395 SHELLEXECUTEINFOW info; 2396 DWORD dwSize, dwError, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC; 2397 LPCWSTR pszVerb = NULL; 2398 WCHAR szFile[MAX_PATH], szFile2[MAX_PATH]; 2399 HRESULT hr; 2400 LPCWSTR pchParams; 2401 LPWSTR lpCommand = NULL; 2402 2403 if (pwszCommand == NULL) 2404 RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 2405 1, (ULONG_PTR*)pwszCommand); 2406 2407 __SHCloneStrW(&lpCommand, pwszCommand); 2408 StrTrimW(lpCommand, L" \t"); 2409 2410 if (dwSeclFlags & SECL_NO_UI) 2411 dwFlags |= SEE_MASK_FLAG_NO_UI; 2412 if (dwSeclFlags & SECL_LOG_USAGE) 2413 dwFlags |= SEE_MASK_FLAG_LOG_USAGE; 2414 if (dwSeclFlags & SECL_USE_IDLIST) 2415 dwFlags |= SEE_MASK_INVOKEIDLIST; 2416 2417 if (dwSeclFlags & SECL_RUNAS) 2418 { 2419 dwSize = 0; 2420 hr = AssocQueryStringW(0, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize); 2421 if (SUCCEEDED(hr) && dwSize != 0) 2422 { 2423 pszVerb = L"runas"; 2424 } 2425 } 2426 2427 if (PathIsURLW(lpCommand) || UrlIsW(lpCommand, URLIS_APPLIABLE)) 2428 { 2429 StringCchCopyW(szFile, _countof(szFile), lpCommand); 2430 pchParams = NULL; 2431 } 2432 else 2433 { 2434 pchParams = SplitParams(lpCommand, szFile, _countof(szFile)); 2435 if (szFile[0] != UNICODE_NULL && szFile[1] == L':' && 2436 szFile[2] == UNICODE_NULL) 2437 { 2438 PathAddBackslashW(szFile); 2439 } 2440 if (SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL) || 2441 SearchPathW(NULL, szFile, wszExe, _countof(szFile2), szFile2, NULL) || 2442 SearchPathW(NULL, szFile, wszCom, _countof(szFile2), szFile2, NULL) || 2443 SearchPathW(pwszStartDir, szFile, NULL, _countof(szFile2), szFile2, NULL) || 2444 SearchPathW(pwszStartDir, szFile, wszExe, _countof(szFile2), szFile2, NULL) || 2445 SearchPathW(pwszStartDir, szFile, wszCom, _countof(szFile2), szFile2, NULL)) 2446 { 2447 StringCchCopyW(szFile, _countof(szFile), szFile2); 2448 } 2449 else if (SearchPathW(NULL, lpCommand, NULL, _countof(szFile2), szFile2, NULL) || 2450 SearchPathW(NULL, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) || 2451 SearchPathW(NULL, lpCommand, wszCom, _countof(szFile2), szFile2, NULL) || 2452 SearchPathW(pwszStartDir, lpCommand, NULL, _countof(szFile2), szFile2, NULL) || 2453 SearchPathW(pwszStartDir, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) || 2454 SearchPathW(pwszStartDir, lpCommand, wszCom, _countof(szFile2), szFile2, NULL)) 2455 { 2456 StringCchCopyW(szFile, _countof(szFile), szFile2); 2457 pchParams = NULL; 2458 } 2459 2460 if (!(dwSeclFlags & SECL_ALLOW_NONEXE)) 2461 { 2462 if (!GetBinaryTypeW(szFile, &dwType)) 2463 { 2464 SHFree(lpCommand); 2465 2466 if (!(dwSeclFlags & SECL_NO_UI)) 2467 { 2468 WCHAR szText[128 + MAX_PATH], szFormat[128]; 2469 LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); 2470 StringCchPrintfW(szText, _countof(szText), szFormat, szFile); 2471 MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); 2472 } 2473 return CO_E_APPNOTFOUND; 2474 } 2475 } 2476 else 2477 { 2478 if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES) 2479 { 2480 SHFree(lpCommand); 2481 2482 if (!(dwSeclFlags & SECL_NO_UI)) 2483 { 2484 WCHAR szText[128 + MAX_PATH], szFormat[128]; 2485 LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); 2486 StringCchPrintfW(szText, _countof(szText), szFormat, szFile); 2487 MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); 2488 } 2489 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 2490 } 2491 } 2492 } 2493 2494 ZeroMemory(&info, sizeof(info)); 2495 info.cbSize = sizeof(info); 2496 info.fMask = dwFlags; 2497 info.hwnd = hwnd; 2498 info.lpVerb = pszVerb; 2499 info.lpFile = szFile; 2500 info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL; 2501 info.lpDirectory = pwszStartDir; 2502 info.nShow = nShow; 2503 if (ShellExecuteExW(&info)) 2504 { 2505 if (info.lpIDList) 2506 CoTaskMemFree(info.lpIDList); 2507 2508 SHFree(lpCommand); 2509 2510 return S_OK; 2511 } 2512 2513 dwError = GetLastError(); 2514 2515 SHFree(lpCommand); 2516 2517 return HRESULT_FROM_WIN32(dwError); 2518 } 2519