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