Implement A->W call for GetNamedSecurityInfo.
[wine] / dlls / shell32 / shlexec.c
1 /*
2  *                              Shell Library Functions
3  *
4  * Copyright 1998 Marcus Meissner
5  * Copyright 2002 Eric Pouech
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <ctype.h>
33 #include <assert.h>
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winerror.h"
38 #include "winreg.h"
39 #include "wownt32.h"
40 #include "shellapi.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43 #include "shlobj.h"
44 #include "shlwapi.h"
45 #include "ddeml.h"
46
47 #include "wine/winbase16.h"
48 #include "shell32_main.h"
49 #include "undocshell.h"
50 #include "pidl.h"
51
52 #include "wine/debug.h"
53
54 WINE_DEFAULT_DEBUG_CHANNEL(exec);
55
56 static const WCHAR wszOpen[] = {'o','p','e','n',0};
57 static const WCHAR wszExe[] = {'.','e','x','e',0};
58 static const WCHAR wszILPtr[] = {':','%','p',0};
59 static const WCHAR wszShell[] = {'\\','s','h','e','l','l','\\',0};
60 static const WCHAR wszFolder[] = {'F','o','l','d','e','r',0};
61 static const WCHAR wszEmpty[] = {0};
62
63
64 /***********************************************************************
65  *      SHELL_ArgifyW [Internal]
66  *
67  * this function is supposed to expand the escape sequences found in the registry
68  * some diving reported that the following were used:
69  * + %1, %2...  seem to report to parameter of index N in ShellExecute pmts
70  *      %1 file
71  *      %2 printer
72  *      %3 driver
73  *      %4 port
74  * %I address of a global item ID (explorer switch /idlist)
75  * %L seems to be %1 as long filename followed by the 8+3 variation
76  * %S ???
77  * %* all following parameters (see batfile)
78  *
79  * FIXME: use 'len'
80  */
81 static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args)
82 {
83     WCHAR   xlpFile[1024];
84     BOOL    done = FALSE;
85     PWSTR   res = out;
86     PCWSTR  cmd;
87     LPVOID  pv;
88
89     TRACE("%p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt),
90           debugstr_w(lpFile), pidl, args);
91
92     while (*fmt)
93     {
94         if (*fmt == '%')
95         {
96             switch (*++fmt)
97             {
98             case '\0':
99             case '%':
100                 *res++ = '%';
101                 break;
102
103             case '2':
104             case '3':
105             case '4':
106             case '5':
107             case '6':
108             case '7':
109             case '8':
110             case '9':
111             case '0':
112             case '*':
113                 if (args)
114                 {
115                     if (*fmt == '*')
116                     {
117                         *res++ = '"';
118                         while(*args)
119                             *res++ = *args++;
120                         *res++ = '"';
121                     }
122                     else
123                     {
124                         while(*args && !isspace(*args))
125                             *res++ = *args++;
126
127                         while(isspace(*args))
128                             ++args;
129                     }
130                     break;
131                 }
132                 /* else fall through */
133             case '1':
134                 if (!done || (*fmt == '1'))
135                 {
136                     /*FIXME Is the call to SearchPathW() really needed? We already have separated out the parameter string in args. */
137                     if (SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
138                         cmd = xlpFile;
139                     else
140                         cmd = lpFile;
141
142                     /* Add double quotation marks unless we already have them (e.g.: "file://%1" %* for exefile) */
143                     if (res == out || *(fmt + 1) != '"')
144                     {
145                         *res++ = '"';
146                         strcpyW(res, cmd);
147                         res += strlenW(cmd);
148                         *res++ = '"';
149                     }
150                     else
151                     {
152                         strcpyW(res, cmd);
153                         res += strlenW(cmd);
154                     }
155                 }
156                 break;
157
158             /*
159              * IE uses this a lot for activating things such as windows media
160              * player. This is not verified to be fully correct but it appears
161              * to work just fine.
162              */
163             case 'l':
164             case 'L':
165                 if (lpFile) {
166                     strcpyW(res, lpFile);
167                     res += strlenW(lpFile);
168                 }
169                 break;
170
171             case 'i':
172             case 'I':
173                 if (pidl) {
174                     HGLOBAL hmem = SHAllocShared(pidl, ILGetSize(pidl), 0);
175                     pv = SHLockShared(hmem, 0);
176                     res += sprintfW(res, wszILPtr, pv);
177                     SHUnlockShared(pv);
178                 }
179                 break;
180
181             default: FIXME("Unknown escape sequence %%%c\n", *fmt);
182             }
183             fmt++;
184             done = TRUE;
185         }
186         else
187             *res++ = *fmt++;
188     }
189
190     *res = '\0';
191
192     return done;
193 }
194
195 HRESULT SHELL_GetPathFromIDListForExecuteA(LPCITEMIDLIST pidl, LPSTR pszPath, UINT uOutSize)
196 {
197     STRRET strret;
198     IShellFolder* desktop;
199
200     HRESULT hr = SHGetDesktopFolder(&desktop);
201
202     if (SUCCEEDED(hr)) {
203         hr = IShellFolder_GetDisplayNameOf(desktop, pidl, SHGDN_FORPARSING, &strret);
204
205         if (SUCCEEDED(hr))
206             StrRetToStrNA(pszPath, uOutSize, &strret, pidl);
207
208         IShellFolder_Release(desktop);
209     }
210
211     return hr;
212 }
213
214 HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize)
215 {
216     STRRET strret;
217     IShellFolder* desktop;
218
219     HRESULT hr = SHGetDesktopFolder(&desktop);
220
221     if (SUCCEEDED(hr)) {
222         hr = IShellFolder_GetDisplayNameOf(desktop, pidl, SHGDN_FORPARSING, &strret);
223
224         if (SUCCEEDED(hr))
225             StrRetToStrNW(pszPath, uOutSize, &strret, pidl);
226
227         IShellFolder_Release(desktop);
228     }
229
230     return hr;
231 }
232
233 /*************************************************************************
234  *      SHELL_ResolveShortCutW [Internal]
235  *      read shortcut file at 'wcmd'
236  */
237 static HRESULT SHELL_ResolveShortCutW(LPWSTR wcmd, LPWSTR wargs, LPWSTR wdir, HWND hwnd, LPCWSTR lpVerb, int* pshowcmd, LPITEMIDLIST* ppidl)
238 {
239     IShellFolder* psf;
240
241     HRESULT hr = SHGetDesktopFolder(&psf);
242
243     *ppidl = NULL;
244
245     if (SUCCEEDED(hr)) {
246         LPITEMIDLIST pidl;
247         ULONG l;
248
249         hr = IShellFolder_ParseDisplayName(psf, 0, 0, wcmd, &l, &pidl, 0);
250
251         if (SUCCEEDED(hr)) {
252             IShellLinkW* psl;
253
254             hr = IShellFolder_GetUIObjectOf(psf, NULL, 1, (LPCITEMIDLIST*)&pidl, &IID_IShellLinkW, NULL, (LPVOID*)&psl);
255
256             if (SUCCEEDED(hr)) {
257                 hr = IShellLinkW_Resolve(psl, hwnd, 0);
258
259                 if (SUCCEEDED(hr)) {
260                     hr = IShellLinkW_GetPath(psl, wcmd, MAX_PATH, NULL, SLGP_UNCPRIORITY);
261
262                     if (SUCCEEDED(hr)) {
263                         if (!*wcmd) {
264                             /* We could not translate the PIDL in the shell link into a valid file system path - so return the PIDL instead. */
265                             hr = IShellLinkW_GetIDList(psl, ppidl);
266
267                             if (SUCCEEDED(hr) && *ppidl) {
268                                 /* We got a PIDL instead of a file system path - try to translate it. */
269                                 if (SUCCEEDED(SHELL_GetPathFromIDListW(*ppidl, wcmd, MAX_PATH))) {
270                                     SHFree(*ppidl);
271                                     *ppidl = NULL;
272                                 }
273                             }
274                         }
275
276                         if (SUCCEEDED(hr)) {
277                             /* get command line arguments, working directory and display mode if available */
278                             IShellLinkW_GetWorkingDirectory(psl, wdir, MAX_PATH);
279                             IShellLinkW_GetArguments(psl, wargs, MAX_PATH);
280                             IShellLinkW_GetShowCmd(psl, pshowcmd);
281                         }
282                     }
283                 }
284
285                 IShellLinkW_Release(psl);
286             }
287
288             SHFree(pidl);
289         }
290
291         IShellFolder_Release(psf);
292     }
293
294     return hr;
295 }
296
297 /*************************************************************************
298  *      SHELL_ExecuteW [Internal]
299  *
300  */
301 static UINT SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
302                             LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out)
303 {
304     STARTUPINFOW  startup;
305     PROCESS_INFORMATION info;
306     UINT retval = 31;
307     UINT gcdret = 0;
308     WCHAR curdir[MAX_PATH];
309
310     TRACE("Execute %s from directory %s\n", debugstr_w(lpCmd), debugstr_w(psei->lpDirectory));
311     /* ShellExecute specifies the command from psei->lpDirectory
312      * if present. Not from the current dir as CreateProcess does */
313     if( psei->lpDirectory && psei->lpDirectory[0] )
314         if( ( gcdret = GetCurrentDirectoryW( MAX_PATH, curdir)))
315             if( !SetCurrentDirectoryW( psei->lpDirectory))
316                 ERR("cannot set directory %s\n", debugstr_w(psei->lpDirectory));
317     ZeroMemory(&startup,sizeof(STARTUPINFOW));
318     startup.cb = sizeof(STARTUPINFOW);
319     startup.dwFlags = STARTF_USESHOWWINDOW;
320     startup.wShowWindow = psei->nShow;
321     if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT,
322                        env, *psei->lpDirectory? psei->lpDirectory: NULL, &startup, &info))
323     {
324         /* Give 30 seconds to the app to come up, if desired. Probably only needed
325            when starting app immediately before making a DDE connection. */
326         if (shWait)
327             if (WaitForInputIdle( info.hProcess, 30000 ) == WAIT_FAILED)
328                 WARN("WaitForInputIdle failed: Error %ld\n", GetLastError() );
329         retval = 33;
330         if (psei->fMask & SEE_MASK_NOCLOSEPROCESS)
331             psei_out->hProcess = info.hProcess;
332         else
333             CloseHandle( info.hProcess );
334         CloseHandle( info.hThread );
335     }
336     else if ((retval = GetLastError()) >= 32)
337     {
338         FIXME("Strange error set by CreateProcess: %d\n", retval);
339         retval = ERROR_BAD_FORMAT;
340     }
341
342     TRACE("returning %u\n", retval);
343
344     psei_out->hInstApp = (HINSTANCE)retval;
345     if( gcdret ) 
346         if( !SetCurrentDirectoryW( curdir))
347             ERR("cannot return to directory %s\n", debugstr_w(curdir));
348
349     return retval;
350 }
351
352
353 /***********************************************************************
354  *           SHELL_BuildEnvW    [Internal]
355  *
356  * Build the environment for the new process, adding the specified
357  * path to the PATH variable. Returned pointer must be freed by caller.
358  */
359 static void *SHELL_BuildEnvW( const WCHAR *path )
360 {
361     static const WCHAR wPath[] = {'P','A','T','H','=',0};
362     WCHAR *strings, *new_env;
363     WCHAR *p, *p2;
364     int total = strlenW(path) + 1;
365     BOOL got_path = FALSE;
366
367     if (!(strings = GetEnvironmentStringsW())) return NULL;
368     p = strings;
369     while (*p)
370     {
371         int len = strlenW(p) + 1;
372         if (!strncmpiW( p, wPath, 5 )) got_path = TRUE;
373         total += len;
374         p += len;
375     }
376     if (!got_path) total += 5;  /* we need to create PATH */
377     total++;  /* terminating null */
378
379     if (!(new_env = HeapAlloc( GetProcessHeap(), 0, total * sizeof(WCHAR) )))
380     {
381         FreeEnvironmentStringsW( strings );
382         return NULL;
383     }
384     p = strings;
385     p2 = new_env;
386     while (*p)
387     {
388         int len = strlenW(p) + 1;
389         memcpy( p2, p, len * sizeof(WCHAR) );
390         if (!strncmpiW( p, wPath, 5 ))
391         {
392             p2[len - 1] = ';';
393             strcpyW( p2 + len, path );
394             p2 += strlenW(path) + 1;
395         }
396         p += len;
397         p2 += len;
398     }
399     if (!got_path)
400     {
401         strcpyW( p2, wPath );
402         strcatW( p2, path );
403         p2 += strlenW(p2) + 1;
404     }
405     *p2 = 0;
406     FreeEnvironmentStringsW( strings );
407     return new_env;
408 }
409
410
411 /***********************************************************************
412  *           SHELL_TryAppPathW  [Internal]
413  *
414  * Helper function for SHELL_FindExecutable
415  * @param lpResult - pointer to a buffer of size MAX_PATH
416  * On entry: szName is a filename (probably without path separators).
417  * On exit: if szName found in "App Path", place full path in lpResult, and return true
418  */
419 static BOOL SHELL_TryAppPathW( LPCWSTR szName, LPWSTR lpResult, WCHAR **env)
420 {
421     static const WCHAR wszKeyAppPaths[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',
422         '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','A','p','p',' ','P','a','t','h','s','\\',0};
423     static const WCHAR wPath[] = {'P','a','t','h',0};
424     HKEY hkApp = 0;
425     WCHAR buffer[1024];
426     LONG len;
427     LONG res;
428     BOOL found = FALSE;
429
430     if (env) *env = NULL;
431     strcpyW(buffer, wszKeyAppPaths);
432     strcatW(buffer, szName);
433     res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp);
434     if (res) goto end;
435
436     len = MAX_PATH*sizeof(WCHAR);
437     res = RegQueryValueW(hkApp, NULL, lpResult, &len);
438     if (res) goto end;
439     found = TRUE;
440
441     if (env)
442     {
443         DWORD count = sizeof(buffer);
444         if (!RegQueryValueExW(hkApp, wPath, NULL, NULL, (LPBYTE)buffer, &count) && buffer[0])
445             *env = SHELL_BuildEnvW( buffer );
446     }
447
448 end:
449     if (hkApp) RegCloseKey(hkApp);
450     return found;
451 }
452
453 static UINT SHELL_FindExecutableByOperation(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOperation, LPWSTR key, LPWSTR filetype, LPWSTR command, LONG commandlen)
454 {
455     static const WCHAR wCommand[] = {'\\','c','o','m','m','a','n','d',0};
456
457     /* Looking for ...buffer\shell\<verb>\command */
458     strcatW(filetype, wszShell);
459     strcatW(filetype, lpOperation);
460     strcatW(filetype, wCommand);
461
462     if (RegQueryValueW(HKEY_CLASSES_ROOT, filetype, command,
463                        &commandlen) == ERROR_SUCCESS)
464     {
465         commandlen /= sizeof(WCHAR);
466         if (key) strcpyW(key, filetype);
467 #if 0
468         LPWSTR tmp;
469         WCHAR param[256];
470         LONG paramlen = sizeof(param);
471         static const WCHAR wSpace[] = {' ',0};
472
473         /* FIXME: it seems all Windows version don't behave the same here.
474          * the doc states that this ddeexec information can be found after
475          * the exec names.
476          * on Win98, it doesn't appear, but I think it does on Win2k
477          */
478         /* Get the parameters needed by the application
479            from the associated ddeexec key */
480         tmp = strstrW(filetype, wCommand);
481         tmp[0] = '\0';
482         strcatW(filetype, wDdeexec);
483         if (RegQueryValueW(HKEY_CLASSES_ROOT, filetype, param,
484                                      &paramlen) == ERROR_SUCCESS)
485         {
486             paramlen /= sizeof(WCHAR);
487             strcatW(command, wSpace);
488             strcatW(command, param);
489             commandlen += paramlen;
490         }
491 #endif
492
493         command[commandlen] = '\0';
494
495         return 33; /* FIXME see SHELL_FindExecutable() */
496     }
497
498     return 31;  /* default - 'No association was found' */
499 }
500
501 /*************************************************************************
502  *      SHELL_FindExecutable [Internal]
503  *
504  * Utility for code sharing between FindExecutable and ShellExecute
505  * in:
506  *      lpFile the name of a file
507  *      lpOperation the operation on it (open)
508  * out:
509  *      lpResult a buffer, big enough :-(, to store the command to do the
510  *              operation on the file
511  *      key a buffer, big enough, to get the key name to do actually the
512  *              command (it'll be used afterwards for more information
513  *              on the operation)
514  */
515 UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOperation,
516                                  LPWSTR lpResult, int resultLen, LPWSTR key, WCHAR **env, LPITEMIDLIST pidl, LPCWSTR args)
517 {
518     static const WCHAR wWindows[] = {'w','i','n','d','o','w','s',0};
519     static const WCHAR wPrograms[] = {'p','r','o','g','r','a','m','s',0};
520     static const WCHAR wExtensions[] = {'e','x','e',' ','p','i','f',' ','b','a','t',' ','c','m','d',' ','c','o','m',0};
521     WCHAR *extension = NULL; /* pointer to file extension */
522     WCHAR filetype[256];     /* registry name for this filetype */
523     LONG  filetypelen = sizeof(filetype); /* length of above */
524     WCHAR command[1024];     /* command from registry */
525     WCHAR wBuffer[256];      /* Used to GetProfileString */
526     UINT  retval = 31;       /* default - 'No association was found' */
527     WCHAR *tok;              /* token pointer */
528     WCHAR xlpFile[256];      /* result of SearchPath */
529     DWORD attribs;           /* file attributes */
530
531     TRACE("%s\n", (lpFile != NULL) ? debugstr_w(lpFile) : "-");
532
533     xlpFile[0] = '\0';
534     lpResult[0] = '\0'; /* Start off with an empty return string */
535     if (key) *key = '\0';
536
537     /* trap NULL parameters on entry */
538     if ((lpFile == NULL) || (lpResult == NULL) || (lpOperation == NULL))
539     {
540         WARN("(lpFile=%s,lpResult=%s,lpOperation=%s): NULL parameter\n",
541              debugstr_w(lpFile), debugstr_w(lpOperation), debugstr_w(lpResult));
542         return 2; /* File not found. Close enough, I guess. */
543     }
544
545     if (SHELL_TryAppPathW( lpFile, lpResult, env ))
546     {
547         TRACE("found %s via App Paths\n", debugstr_w(lpResult));
548         return 33;
549     }
550
551     if (SearchPathW(lpPath, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
552     {
553         TRACE("SearchPathW returned non-zero\n");
554         lpFile = xlpFile;
555         /* Hey, isn't this value ignored?  Why make this call?  Shouldn't we return here?  --dank*/
556     }
557
558     attribs = GetFileAttributesW(lpFile);
559     if (attribs!=INVALID_FILE_ATTRIBUTES && (attribs&FILE_ATTRIBUTE_DIRECTORY))
560     {
561        strcpyW(filetype, wszFolder);
562        filetypelen = 6;    /* strlen("Folder") */
563     }
564     else
565     {
566         /* First thing we need is the file's extension */
567         extension = strrchrW(xlpFile, '.'); /* Assume last "." is the one; */
568         /* File->Run in progman uses */
569         /* .\FILE.EXE :( */
570         TRACE("xlpFile=%s,extension=%s\n", debugstr_w(xlpFile), debugstr_w(extension));
571
572         if (extension == NULL || extension[1]==0)
573         {
574             WARN("Returning 31 - No association\n");
575             return 31; /* no association */
576         }
577
578         /* Three places to check: */
579         /* 1. win.ini, [windows], programs (NB no leading '.') */
580         /* 2. Registry, HKEY_CLASS_ROOT\<filetype>\shell\open\command */
581         /* 3. win.ini, [extensions], extension (NB no leading '.' */
582         /* All I know of the order is that registry is checked before */
583         /* extensions; however, it'd make sense to check the programs */
584         /* section first, so that's what happens here. */
585
586         /* See if it's a program - if GetProfileString fails, we skip this
587          * section. Actually, if GetProfileString fails, we've probably
588          * got a lot more to worry about than running a program... */
589         if (GetProfileStringW(wWindows, wPrograms, wExtensions, wBuffer, sizeof(wBuffer)/sizeof(WCHAR)) > 0)
590         {
591             CharLowerW(wBuffer);
592             tok = wBuffer;
593             while (*tok)
594             {
595                 WCHAR *p = tok;
596                 while (*p && *p != ' ' && *p != '\t') p++;
597                 if (*p)
598                 {
599                     *p++ = 0;
600                     while (*p == ' ' || *p == '\t') p++;
601                 }
602
603                 if (strcmpiW(tok, &extension[1]) == 0) /* have to skip the leading "." */
604                 {
605                     strcpyW(lpResult, xlpFile);
606                     /* Need to perhaps check that the file has a path
607                      * attached */
608                     TRACE("found %s\n", debugstr_w(lpResult));
609                     return 33;
610
611                     /* Greater than 32 to indicate success FIXME According to the
612                      * docs, I should be returning a handle for the
613                      * executable. Does this mean I'm supposed to open the
614                      * executable file or something? More RTFM, I guess... */
615                 }
616                 tok = p;
617             }
618         }
619
620         /* Check registry */
621         if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, filetype,
622                            &filetypelen) == ERROR_SUCCESS)
623         {
624             filetypelen /= sizeof(WCHAR);
625             filetype[filetypelen] = '\0';
626             TRACE("File type: %s\n", debugstr_w(filetype));
627         }
628     }
629
630     if (*filetype)
631     {
632         if (lpOperation)
633         {
634             /* pass the operation string to SHELL_FindExecutableByOperation() */
635             filetype[filetypelen] = '\0';
636             retval = SHELL_FindExecutableByOperation(lpPath, lpFile, lpOperation, key, filetype, command, sizeof(command));
637         }
638         else
639         {
640             WCHAR operation[MAX_PATH];
641             HKEY hkey;
642
643             /* Looking for ...buffer\shell\<operation>\command */
644             strcatW(filetype, wszShell);
645
646             /* enumerate the operation subkeys in the registry and search for one with an associated command */
647             if (RegOpenKeyW(HKEY_CLASSES_ROOT, filetype, &hkey) == ERROR_SUCCESS)
648             {
649                 int idx = 0;
650                 for(;; ++idx)
651                 {
652                     if (RegEnumKeyW(hkey, idx, operation, MAX_PATH) != ERROR_SUCCESS)
653                         break;
654
655                     filetype[filetypelen] = '\0';
656                     retval = SHELL_FindExecutableByOperation(lpPath, lpFile, operation, key, filetype, command, sizeof(command));
657
658                     if (retval > 32)
659                         break;
660             }
661                 RegCloseKey(hkey);
662             }
663         }
664
665         if (retval > 32)
666         {
667             SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args);
668
669             /* Remove double quotation marks and command line arguments */
670             if (*lpResult == '"')
671             {
672                 WCHAR *p = lpResult;
673                 while (*(p + 1) != '"')
674                 {
675                     *p = *(p + 1);
676                     p++;
677                 }
678                 *p = '\0';
679             }
680         }
681     }
682     else /* Check win.ini */
683     {
684         static const WCHAR wExtensions[] = {'e','x','t','e','n','s','i','o','n','s',0};
685
686         /* Toss the leading dot */
687         extension++;
688         if (GetProfileStringW(wExtensions, extension, wszEmpty, command, sizeof(command)/sizeof(WCHAR)) > 0)
689         {
690             if (strlenW(command) != 0)
691             {
692                 strcpyW(lpResult, command);
693                 tok = strchrW(lpResult, '^'); /* should be ^.extension? */
694                 if (tok != NULL)
695                 {
696                     tok[0] = '\0';
697                     strcatW(lpResult, xlpFile); /* what if no dir in xlpFile? */
698                     tok = strchrW(command, '^'); /* see above */
699                     if ((tok != NULL) && (strlenW(tok)>5))
700                     {
701                         strcatW(lpResult, &tok[5]);
702                     }
703                 }
704                 retval = 33; /* FIXME - see above */
705             }
706         }
707     }
708
709     TRACE("returning %s\n", debugstr_w(lpResult));
710     return retval;
711 }
712
713 /******************************************************************
714  *              dde_cb
715  *
716  * callback for the DDE connection. not really usefull
717  */
718 static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv,
719                                 HSZ hsz1, HSZ hsz2, HDDEDATA hData,
720                                 ULONG_PTR dwData1, ULONG_PTR dwData2)
721 {
722     TRACE("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
723            uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
724     return NULL;
725 }
726
727 /******************************************************************
728  *              dde_connect
729  *
730  * ShellExecute helper. Used to do an operation with a DDE connection
731  *
732  * Handles both the direct connection (try #1), and if it fails,
733  * launching an application and trying (#2) to connect to it
734  *
735  */
736 static unsigned dde_connect(WCHAR* key, WCHAR* start, WCHAR* ddeexec,
737                             const WCHAR* lpFile, WCHAR *env,
738                             LPCWSTR szCommandline, LPITEMIDLIST pidl, SHELL_ExecuteW32 execfunc,
739                             LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out)
740 {
741     static const WCHAR wApplication[] = {'\\','a','p','p','l','i','c','a','t','i','o','n',0};
742     static const WCHAR wTopic[] = {'\\','t','o','p','i','c',0};
743     WCHAR *     endkey = key + strlenW(key);
744     WCHAR       app[256], topic[256], ifexec[256], res[256];
745     LONG        applen, topiclen, ifexeclen;
746     WCHAR *     exec;
747     DWORD       ddeInst = 0;
748     DWORD       tid;
749     HSZ         hszApp, hszTopic;
750     HCONV       hConv;
751     HDDEDATA    hDdeData;
752     unsigned    ret = 31;
753
754     strcpyW(endkey, wApplication);
755     applen = sizeof(app);
756     if (RegQueryValueW(HKEY_CLASSES_ROOT, key, app, &applen) != ERROR_SUCCESS)
757     {
758         FIXME("default app name NIY %s\n", debugstr_w(key));
759         return 2;
760     }
761
762     strcpyW(endkey, wTopic);
763     topiclen = sizeof(topic);
764     if (RegQueryValueW(HKEY_CLASSES_ROOT, key, topic, &topiclen) != ERROR_SUCCESS)
765     {
766         static const WCHAR wSystem[] = {'S','y','s','t','e','m',0};
767         strcpyW(topic, wSystem);
768     }
769
770     if (DdeInitializeW(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR)
771     {
772         return 2;
773     }
774
775     hszApp = DdeCreateStringHandleW(ddeInst, app, CP_WINUNICODE);
776     hszTopic = DdeCreateStringHandleW(ddeInst, topic, CP_WINUNICODE);
777
778     hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
779     exec = ddeexec;
780     if (!hConv)
781     {
782         static const WCHAR wIfexec[] = {'\\','i','f','e','x','e','c',0};
783         TRACE("Launching '%s'\n", debugstr_w(start));
784         ret = execfunc(start, env, TRUE, psei, psei_out);
785         if (ret < 32)
786         {
787             TRACE("Couldn't launch\n");
788             goto error;
789         }
790         hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
791         if (!hConv)
792         {
793             TRACE("Couldn't connect. ret=%d\n", ret);
794             DdeUninitialize(ddeInst);
795             SetLastError(ERROR_DDE_FAIL);
796             return 30; /* whatever */
797         }
798         strcpyW(endkey, wIfexec);
799         ifexeclen = sizeof(ifexec);
800         if (RegQueryValueW(HKEY_CLASSES_ROOT, key, ifexec, &ifexeclen) == ERROR_SUCCESS)
801         {
802             exec = ifexec;
803         }
804     }
805
806     SHELL_ArgifyW(res, sizeof(res)/sizeof(WCHAR), exec, lpFile, pidl, szCommandline);
807     TRACE("%s %s => %s\n", debugstr_w(exec), debugstr_w(lpFile), debugstr_w(res));
808
809     /* It's documented in the KB 330337 that IE has a bug and returns
810      * error DMLERR_NOTPROCESSED on XTYP_EXECUTE request.
811      */
812     hDdeData = DdeClientTransaction((LPBYTE)res, (strlenW(res) + 1) * sizeof(WCHAR), hConv, 0L, 0,
813                                      XTYP_EXECUTE, 10000, &tid);
814     if (hDdeData)
815         DdeFreeDataHandle(hDdeData);
816     else
817         WARN("DdeClientTransaction failed with error %04x\n", DdeGetLastError(ddeInst));
818     ret = 33;
819
820     DdeDisconnect(hConv);
821
822  error:
823     DdeUninitialize(ddeInst);
824
825     return ret;
826 }
827
828 /*************************************************************************
829  *      execute_from_key [Internal]
830  */
831 static UINT execute_from_key(LPWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWSTR szCommandline,
832                              SHELL_ExecuteW32 execfunc,
833                              LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out)
834 {
835     WCHAR cmd[1024];
836     LONG cmdlen = sizeof(cmd);
837     UINT retval = 31;
838
839     cmd[0] = '\0';
840
841     /* Get the application for the registry */
842     if (RegQueryValueW(HKEY_CLASSES_ROOT, key, cmd, &cmdlen) == ERROR_SUCCESS)
843     {
844         static const WCHAR wCommand[] = {'c','o','m','m','a','n','d',0};
845         static const WCHAR wDdeexec[] = {'d','d','e','e','x','e','c',0};
846         LPWSTR tmp;
847         WCHAR param[256];
848         LONG paramlen = sizeof(param);
849
850         param[0] = '\0';
851
852         /* Get the parameters needed by the application
853            from the associated ddeexec key */
854         tmp = strstrW(key, wCommand);
855         assert(tmp);
856         strcpyW(tmp, wDdeexec);
857
858         if (RegQueryValueW(HKEY_CLASSES_ROOT, key, param, &paramlen) == ERROR_SUCCESS)
859         {
860             TRACE("Got ddeexec %s => %s\n", debugstr_w(key), debugstr_w(param));
861             retval = dde_connect(key, cmd, param, lpFile, env, szCommandline, psei->lpIDList, execfunc, psei, psei_out);
862         }
863         else
864         {
865             /* Is there a replace() function anywhere? */
866             cmdlen /= sizeof(WCHAR);
867             cmd[cmdlen] = '\0';
868             SHELL_ArgifyW(param, sizeof(param)/sizeof(WCHAR), cmd, lpFile, psei->lpIDList, szCommandline);
869             retval = execfunc(param, env, FALSE, psei, psei_out);
870         }
871     }
872     else TRACE("ooch\n");
873
874     return retval;
875 }
876
877 /*************************************************************************
878  * FindExecutableA                      [SHELL32.@]
879  */
880 HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
881 {
882     HINSTANCE retval;
883     WCHAR *wFile = NULL, *wDirectory = NULL;
884     WCHAR wResult[MAX_PATH];
885
886     if (lpFile) __SHCloneStrAtoW(&wFile, lpFile);
887     if (lpDirectory) __SHCloneStrAtoW(&wDirectory, lpDirectory);
888
889     retval = FindExecutableW(wFile, wDirectory, wResult);
890     WideCharToMultiByte(CP_ACP, 0, wResult, -1, lpResult, MAX_PATH, NULL, NULL);
891     if (wFile) SHFree( wFile );
892     if (wDirectory) SHFree( wDirectory );
893
894     TRACE("returning %s\n", lpResult);
895     return (HINSTANCE)retval;
896 }
897
898 /*************************************************************************
899  * FindExecutableW                      [SHELL32.@]
900  */
901 HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult)
902 {
903     UINT retval = 31;    /* default - 'No association was found' */
904     WCHAR old_dir[1024];
905
906     TRACE("File %s, Dir %s\n",
907           (lpFile != NULL ? debugstr_w(lpFile) : "-"), (lpDirectory != NULL ? debugstr_w(lpDirectory) : "-"));
908
909     lpResult[0] = '\0'; /* Start off with an empty return string */
910
911     /* trap NULL parameters on entry */
912     if ((lpFile == NULL) || (lpResult == NULL))
913     {
914         /* FIXME - should throw a warning, perhaps! */
915         return (HINSTANCE)2; /* File not found. Close enough, I guess. */
916     }
917
918     if (lpDirectory)
919     {
920         GetCurrentDirectoryW(sizeof(old_dir)/sizeof(WCHAR), old_dir);
921         SetCurrentDirectoryW(lpDirectory);
922     }
923
924     retval = SHELL_FindExecutable(lpDirectory, lpFile, wszOpen, lpResult, MAX_PATH, NULL, NULL, NULL, NULL);
925
926     TRACE("returning %s\n", debugstr_w(lpResult));
927     if (lpDirectory)
928         SetCurrentDirectoryW(old_dir);
929     return (HINSTANCE)retval;
930 }
931
932 /*************************************************************************
933  *      ShellExecuteExW32 [Internal]
934  */
935 BOOL WINAPI ShellExecuteExW32 (LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
936 {
937     static const WCHAR wQuote[] = {'"',0};
938     static const WCHAR wSpace[] = {' ',0};
939     static const WCHAR wWww[] = {'w','w','w',0};
940     static const WCHAR wFile[] = {'f','i','l','e',0};
941     static const WCHAR wHttp[] = {'h','t','t','p',':','/','/',0};
942     static const WCHAR wExtLnk[] = {'.','l','n','k',0};
943     static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0};
944
945     WCHAR wszApplicationName[MAX_PATH+2], wszParameters[1024], wszDir[MAX_PATH];
946     SHELLEXECUTEINFOW sei_tmp;  /* modifiable copy of SHELLEXECUTEINFO struct */
947     WCHAR wfileName[MAX_PATH];
948     WCHAR *env;
949     WCHAR lpstrProtocol[256];
950     LPCWSTR lpFile;
951     UINT retval = 31;
952     WCHAR wcmd[1024];
953     WCHAR buffer[MAX_PATH];
954     const WCHAR* ext;
955     BOOL done;
956
957     /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */
958     memcpy(&sei_tmp, sei, sizeof(sei_tmp));
959
960     TRACE("mask=0x%08lx hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n",
961             sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb),
962             debugstr_w(sei_tmp.lpFile), debugstr_w(sei_tmp.lpParameters),
963             debugstr_w(sei_tmp.lpDirectory), sei_tmp.nShow,
964             (sei_tmp.fMask & SEE_MASK_CLASSNAME) ? debugstr_w(sei_tmp.lpClass) : "not used");
965
966     sei->hProcess = NULL;
967
968     /* make copies of all path/command strings */
969     if (sei_tmp.lpFile)
970         strcpyW(wszApplicationName, sei_tmp.lpFile);
971     else
972         *wszApplicationName = '\0';
973
974     if (sei_tmp.lpParameters)
975         strcpyW(wszParameters, sei_tmp.lpParameters);
976     else
977         *wszParameters = '\0';
978
979     if (sei_tmp.lpDirectory)
980         strcpyW(wszDir, sei_tmp.lpDirectory);
981     else
982         *wszDir = '\0';
983
984     /* adjust string pointers to point to the new buffers */
985     sei_tmp.lpFile = wszApplicationName;
986     sei_tmp.lpParameters = wszParameters;
987     sei_tmp.lpDirectory = wszDir;
988
989     if (sei_tmp.fMask & (SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY |
990         SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT |
991         SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI | SEE_MASK_UNICODE |
992         SEE_MASK_NO_CONSOLE | SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR ))
993     {
994         FIXME("flags ignored: 0x%08lx\n", sei_tmp.fMask);
995     }
996
997     /* process the IDList */
998     if (sei_tmp.fMask & SEE_MASK_IDLIST)
999     {
1000         IShellExecuteHookW* pSEH;
1001
1002         HRESULT hr = SHBindToParent(sei_tmp.lpIDList, &IID_IShellExecuteHookW, (LPVOID*)&pSEH, NULL);
1003
1004         if (SUCCEEDED(hr))
1005         {
1006             hr = IShellExecuteHookW_Execute(pSEH, sei);
1007
1008             IShellExecuteHookW_Release(pSEH);
1009
1010             if (hr == S_OK)
1011                 return TRUE;
1012         }
1013
1014         wszApplicationName[0] = '"';
1015         SHGetPathFromIDListW(sei_tmp.lpIDList, wszApplicationName+1);
1016         strcatW(wszApplicationName, wQuote);
1017         TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName));
1018     }
1019
1020     if (sei_tmp.fMask & (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY))
1021     {
1022         /* launch a document by fileclass like 'WordPad.Document.1' */
1023         /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */
1024         /* FIXME: szCommandline should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */
1025         HCR_GetExecuteCommandW((sei_tmp.fMask & SEE_MASK_CLASSKEY) ? sei_tmp.hkeyClass : NULL,
1026                                (sei_tmp.fMask & SEE_MASK_CLASSNAME) ? sei_tmp.lpClass: NULL,
1027                                (sei_tmp.lpVerb) ? sei_tmp.lpVerb : wszOpen,
1028                                wszParameters, sizeof(wszParameters)/sizeof(WCHAR));
1029
1030         /* FIXME: get the extension of lpFile, check if it fits to the lpClass */
1031         TRACE("SEE_MASK_CLASSNAME->'%s', doc->'%s'\n", debugstr_w(wszParameters), debugstr_w(wszApplicationName));
1032
1033         wcmd[0] = '\0';
1034         done = SHELL_ArgifyW(wcmd, sizeof(wcmd)/sizeof(WCHAR), wszParameters, wszApplicationName, sei_tmp.lpIDList, NULL);
1035         if (!done && wszApplicationName[0])
1036         {
1037             strcatW(wcmd, wSpace);
1038             strcatW(wcmd, wszApplicationName);
1039         }
1040         retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei);
1041         if (retval > 32)
1042             return TRUE;
1043         else
1044             return FALSE;
1045     }
1046
1047
1048     /* resolve shell shortcuts */
1049     ext = PathFindExtensionW(sei_tmp.lpFile);
1050
1051     if (ext && !strcmpiW(ext, wExtLnk)) /* or check for: shell_attribs & SFGAO_LINK */
1052     {
1053         HRESULT hr;
1054
1055         /* expand paths before reading shell link */
1056         if (ExpandEnvironmentStringsW(sei_tmp.lpFile, buffer, MAX_PATH))
1057             lstrcpyW(wszApplicationName/*sei_tmp.lpFile*/, buffer);
1058
1059         if (*sei_tmp.lpParameters)
1060             if (ExpandEnvironmentStringsW(sei_tmp.lpParameters, buffer, MAX_PATH))
1061                 lstrcpyW(wszParameters/*sei_tmp.lpParameters*/, buffer);
1062
1063         hr = SHELL_ResolveShortCutW((LPWSTR)sei_tmp.lpFile, (LPWSTR)sei_tmp.lpParameters, (LPWSTR)sei_tmp.lpDirectory,
1064                                             sei_tmp.hwnd, sei_tmp.lpVerb?sei_tmp.lpVerb:wszEmpty, &sei_tmp.nShow, (LPITEMIDLIST*)&sei_tmp.lpIDList);
1065
1066         if (sei->lpIDList)
1067             sei->fMask |= SEE_MASK_IDLIST;
1068
1069         if (SUCCEEDED(hr))
1070         {
1071             /* repeat IDList processing if needed */
1072             if (sei_tmp.fMask & SEE_MASK_IDLIST)
1073             {
1074                 IShellExecuteHookW* pSEH;
1075
1076                 HRESULT hr = SHBindToParent(sei_tmp.lpIDList, &IID_IShellExecuteHookW, (LPVOID*)&pSEH, NULL);
1077
1078                 if (SUCCEEDED(hr))
1079                 {
1080                     hr = IShellExecuteHookW_Execute(pSEH, sei);
1081
1082                     IShellExecuteHookW_Release(pSEH);
1083
1084                     if (hr == S_OK)
1085                         return TRUE;
1086                 }
1087
1088                 TRACE("-- idlist=%p (%s)\n", debugstr_w(sei_tmp.lpIDList), debugstr_w(sei_tmp.lpFile));
1089             }
1090         }
1091     }
1092
1093
1094     /* Has the IDList not yet been translated? */
1095     if (sei_tmp.fMask & SEE_MASK_IDLIST)
1096     {
1097         /* last chance to translate IDList: now also allow CLSID paths */
1098         if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW(sei_tmp.lpIDList, buffer, sizeof(buffer)))) {
1099             if (buffer[0]==':' && buffer[1]==':') {
1100                 /* open shell folder for the specified class GUID */
1101                 strcpyW(wszParameters, buffer);
1102                 strcpyW(wszApplicationName, wExplorer);
1103
1104                 sei_tmp.fMask &= ~SEE_MASK_INVOKEIDLIST;
1105             } else if (HCR_GetExecuteCommandW(0, wszFolder, sei_tmp.lpVerb?sei_tmp.lpVerb:wszOpen, buffer, sizeof(buffer))) {
1106                 SHELL_ArgifyW(wszApplicationName, sizeof(wszApplicationName)/sizeof(WCHAR), buffer, NULL, sei_tmp.lpIDList, NULL);
1107
1108                 sei_tmp.fMask &= ~SEE_MASK_INVOKEIDLIST;
1109             }
1110         }
1111     }
1112
1113     /* expand environment strings */
1114     if (ExpandEnvironmentStringsW(sei_tmp.lpFile, buffer, MAX_PATH))
1115         lstrcpyW(wszApplicationName, buffer);
1116
1117     if (*sei_tmp.lpParameters)
1118         if (ExpandEnvironmentStringsW(sei_tmp.lpParameters, buffer, MAX_PATH))
1119             lstrcpyW(wszParameters, buffer);
1120
1121     if (*sei_tmp.lpDirectory)
1122         if (ExpandEnvironmentStringsW(sei_tmp.lpDirectory, buffer, MAX_PATH))
1123             lstrcpyW(wszDir, buffer);
1124
1125     /* Else, try to execute the filename */
1126     TRACE("execute:%s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir));
1127
1128     /* separate out command line arguments from executable file name */
1129     if (!*sei_tmp.lpParameters) {
1130         /* If the executable path is quoted, handle the rest of the command line as parameters. */
1131         if (sei_tmp.lpFile[0] == '"') {
1132             LPWSTR src = wszApplicationName/*sei_tmp.lpFile*/ + 1;
1133             LPWSTR dst = wfileName;
1134             LPWSTR end;
1135
1136             /* copy the unquoted executable path to 'wfileName' */
1137             while(*src && *src!='"')
1138                 *dst++ = *src++;
1139
1140             *dst = '\0';
1141
1142             if (*src == '"') {
1143                 end = ++src;
1144
1145                 while(isspace(*src))
1146                     ++src;
1147             } else
1148                 end = src;
1149
1150             /* copy the parameter string to 'wszParameters' */
1151             strcpyW(wszParameters, src);
1152
1153             /* terminate previous command string after the quote character */
1154             *end = '\0';
1155         }
1156         else
1157         {
1158             /* If the executable name is not quoted, we have to use this search loop here,
1159                that in CreateProcess() is not sufficient because it does not handle shell links. */
1160             WCHAR buffer[MAX_PATH], xlpFile[MAX_PATH];
1161             LPWSTR space, s;
1162
1163             LPWSTR beg = wszApplicationName/*sei_tmp.lpFile*/;
1164             for(s=beg; (space=strchrW(s, ' ')); s=space+1) {
1165                 int idx = space-sei_tmp.lpFile;
1166                 strncpyW(buffer, sei_tmp.lpFile, idx);
1167                 buffer[idx] = '\0';
1168
1169                 /*FIXME This finds directory paths if the targeted file name contains spaces. */
1170                 if (SearchPathW(*sei_tmp.lpDirectory? sei_tmp.lpDirectory: NULL, buffer, wszExe, sizeof(xlpFile), xlpFile, NULL))
1171                 {
1172                     /* separate out command from parameter string */
1173                     LPCWSTR p = space + 1;
1174
1175                     while(isspaceW(*p))
1176                         ++p;
1177
1178                     strcpyW(wszParameters, p);
1179                     *space = '\0';
1180
1181                     break;
1182                 }
1183             }
1184
1185             strcpyW(wfileName, sei_tmp.lpFile);
1186         }
1187     } else
1188         strcpyW(wfileName, sei_tmp.lpFile);
1189
1190     lpFile = wfileName;
1191
1192     if (sei_tmp.lpParameters[0]) {
1193         strcatW(wszApplicationName, wSpace);
1194         strcatW(wszApplicationName, wszParameters);
1195     }
1196
1197     /* We set the default to open, and that should generally work.
1198        But that is not really the way the MS docs say to do it. */
1199     if (!sei_tmp.lpVerb)
1200         sei_tmp.lpVerb = wszOpen;
1201
1202     retval = execfunc(wszApplicationName, NULL, FALSE, &sei_tmp, sei);
1203     if (retval > 32)
1204         return TRUE;
1205
1206     /* Else, try to find the executable */
1207     wcmd[0] = '\0';
1208     retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, 1024, lpstrProtocol, &env, sei_tmp.lpIDList, sei_tmp.lpParameters);
1209     if (retval > 32)  /* Found */
1210     {
1211         WCHAR wszQuotedCmd[MAX_PATH+2];
1212         /* Must quote to handle case where cmd contains spaces, 
1213          * else security hole if malicious user creates executable file "C:\\Program"
1214          */
1215         strcpyW(wszQuotedCmd, wQuote);
1216         strcatW(wszQuotedCmd, wcmd);
1217         strcatW(wszQuotedCmd, wQuote);
1218         if (wszParameters[0]) {
1219             strcatW(wszQuotedCmd, wSpace);
1220             strcatW(wszQuotedCmd, wszParameters);
1221         }
1222         TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(sei_tmp.lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(lpstrProtocol));
1223         if (*lpstrProtocol)
1224             retval = execute_from_key(lpstrProtocol, wszApplicationName, env, sei_tmp.lpParameters, execfunc, &sei_tmp, sei);
1225         else
1226             retval = execfunc(wszQuotedCmd, env, FALSE, &sei_tmp, sei);
1227         if (env) HeapFree( GetProcessHeap(), 0, env );
1228     }
1229     else if (PathIsURLW((LPWSTR)lpFile))    /* File not found, check for URL */
1230     {
1231         static const WCHAR wShell[] = {'\\','s','h','e','l','l','\\',0};
1232         static const WCHAR wCommand[] = {'\\','c','o','m','m','a','n','d',0};
1233         LPWSTR lpstrRes;
1234         INT iSize;
1235
1236         lpstrRes = strchrW(lpFile, ':');
1237         if (lpstrRes)
1238             iSize = lpstrRes - lpFile;
1239         else
1240             iSize = strlenW(lpFile);
1241
1242         TRACE("Got URL: %s\n", debugstr_w(lpFile));
1243         /* Looking for ...protocol\shell\lpOperation\command */
1244         strncpyW(lpstrProtocol, lpFile, iSize);
1245         lpstrProtocol[iSize] = '\0';
1246         strcatW(lpstrProtocol, wShell);
1247         strcatW(lpstrProtocol, sei_tmp.lpVerb? sei_tmp.lpVerb: wszOpen);
1248         strcatW(lpstrProtocol, wCommand);
1249
1250         /* Remove File Protocol from lpFile */
1251         /* In the case file://path/file     */
1252         if (!strncmpiW(lpFile, wFile, iSize))
1253         {
1254             lpFile += iSize;
1255             while (*lpFile == ':') lpFile++;
1256         }
1257         retval = execute_from_key(lpstrProtocol, lpFile, NULL, sei_tmp.lpParameters, execfunc, &sei_tmp, sei);
1258     }
1259     /* Check if file specified is in the form www.??????.*** */
1260     else if (!strncmpiW(lpFile, wWww, 3))
1261     {
1262         /* if so, append lpFile http:// and call ShellExecute */
1263         WCHAR lpstrTmpFile[256];
1264         strcpyW(lpstrTmpFile, wHttp);
1265         strcatW(lpstrTmpFile, lpFile);
1266         retval = (UINT)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0);
1267     }
1268
1269     TRACE("retval %u\n", retval);
1270
1271     if (retval <= 32)
1272     {
1273         sei->hInstApp = (HINSTANCE)retval;
1274         return FALSE;
1275     }
1276
1277     sei->hInstApp = (HINSTANCE)33;
1278     return TRUE;
1279 }
1280
1281 /*************************************************************************
1282  * ShellExecuteA                        [SHELL32.290]
1283  */
1284 HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation,LPCSTR lpFile,
1285                                LPCSTR lpParameters,LPCSTR lpDirectory, INT iShowCmd)
1286 {
1287     SHELLEXECUTEINFOA sei;
1288     HANDLE hProcess = 0;
1289
1290     TRACE("%p,%s,%s,%s,%s,%d\n",
1291            hWnd, lpOperation, lpFile, lpParameters, lpDirectory, iShowCmd);
1292
1293     sei.cbSize = sizeof(sei);
1294     sei.fMask = 0;
1295     sei.hwnd = hWnd;
1296     sei.lpVerb = lpOperation;
1297     sei.lpFile = lpFile;
1298     sei.lpParameters = lpParameters;
1299     sei.lpDirectory = lpDirectory;
1300     sei.nShow = iShowCmd;
1301     sei.lpIDList = 0;
1302     sei.lpClass = 0;
1303     sei.hkeyClass = 0;
1304     sei.dwHotKey = 0;
1305     sei.hProcess = hProcess;
1306
1307     ShellExecuteExA (&sei);
1308     return sei.hInstApp;
1309 }
1310
1311 /*************************************************************************
1312  * ShellExecuteEx                               [SHELL32.291]
1313  *
1314  */
1315 BOOL WINAPI ShellExecuteExAW (LPVOID sei)
1316 {
1317     if (SHELL_OsIsUnicode())
1318         return ShellExecuteExW32 (sei, SHELL_ExecuteW);
1319     return ShellExecuteExA (sei);
1320 }
1321
1322 /*************************************************************************
1323  * ShellExecuteExA                              [SHELL32.292]
1324  *
1325  */
1326 BOOL WINAPI ShellExecuteExA (LPSHELLEXECUTEINFOA sei)
1327 {
1328     SHELLEXECUTEINFOW seiW;
1329     BOOL ret;
1330     WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL, *wClass = NULL;
1331
1332     TRACE("%p\n", sei);
1333
1334     memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW));
1335
1336     if (sei->lpVerb)
1337         seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb);
1338
1339     if (sei->lpFile)
1340         seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile);
1341
1342     if (sei->lpParameters)
1343         seiW.lpParameters = __SHCloneStrAtoW(&wParameters, sei->lpParameters);
1344
1345     if (sei->lpDirectory)
1346         seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory);
1347
1348     if ((sei->fMask & SEE_MASK_CLASSNAME) && sei->lpClass)
1349         seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass);
1350     else
1351         seiW.lpClass = NULL;
1352
1353     ret = ShellExecuteExW32 (&seiW, SHELL_ExecuteW);
1354
1355     sei->hInstApp = seiW.hInstApp;
1356
1357     if (wVerb) SHFree(wVerb);
1358     if (wFile) SHFree(wFile);
1359     if (wParameters) SHFree(wParameters);
1360     if (wDirectory) SHFree(wDirectory);
1361     if (wClass) SHFree(wClass);
1362
1363     return ret;
1364 }
1365
1366 /*************************************************************************
1367  * ShellExecuteExW                              [SHELL32.293]
1368  *
1369  */
1370 BOOL WINAPI ShellExecuteExW (LPSHELLEXECUTEINFOW sei)
1371 {
1372     return  ShellExecuteExW32 (sei, SHELL_ExecuteW);
1373 }
1374
1375 /*************************************************************************
1376  * ShellExecuteW                        [SHELL32.294]
1377  * from shellapi.h
1378  * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpOperation,
1379  * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
1380  */
1381 HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile,
1382                                LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd)
1383 {
1384     SHELLEXECUTEINFOW sei;
1385     HANDLE hProcess = 0;
1386
1387     TRACE("\n");
1388     sei.cbSize = sizeof(sei);
1389     sei.fMask = 0;
1390     sei.hwnd = hwnd;
1391     sei.lpVerb = lpOperation;
1392     sei.lpFile = lpFile;
1393     sei.lpParameters = lpParameters;
1394     sei.lpDirectory = lpDirectory;
1395     sei.nShow = nShowCmd;
1396     sei.lpIDList = 0;
1397     sei.lpClass = 0;
1398     sei.hkeyClass = 0;
1399     sei.dwHotKey = 0;
1400     sei.hProcess = hProcess;
1401
1402     ShellExecuteExW32 (&sei, SHELL_ExecuteW);
1403     return sei.hInstApp;
1404 }