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