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