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