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