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