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