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