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