ShellExecute(NULL, "wordpad.exe",) now correctly finds wordpad.exe in
[wine] / dlls / shell32 / shlexec.c
1 /*
2  *                              Shell Library Functions
3  *
4  * Copyright 1998 Marcus Meissner
5  * Copyright 2002 Eric Pouech
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <ctype.h>
32 #include <assert.h>
33
34 #include "windef.h"
35 #include "winerror.h"
36 #include "winreg.h"
37 #include "wownt32.h"
38 #include "heap.h"
39 #include "shellapi.h"
40 #include "shlobj.h"
41 #include "shlwapi.h"
42 #include "ddeml.h"
43
44 #include "wine/winbase16.h"
45 #include "shell32_main.h"
46 #include "undocshell.h"
47
48 #include "wine/debug.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(exec);
51
52 /***********************************************************************
53  * this function is supposed to expand the escape sequences found in the registry
54  * some diving reported that the following were used:
55  * + %1, %2...  seem to report to parameter of index N in ShellExecute pmts
56  *      %1 file
57  *      %2 printer
58  *      %3 driver
59  *      %4 port
60  * %I adress of a global item ID (explorer switch /idlist)
61  * %L seems to be %1 as long filename followed by the 8+3 variation
62  * %S ???
63  * %* all following parameters (see batfile)
64  */
65 static BOOL argify(char* res, int len, const char* fmt, const char* lpFile)
66 {
67     char        xlpFile[1024];
68     BOOL        done = FALSE;
69
70     while (*fmt)
71     {
72         if (*fmt == '%')
73         {
74             switch (*++fmt)
75             {
76             case '\0':
77             case '%':
78                 *res++ = '%';
79                 break;
80             case '1':
81             case '*':
82                 if (!done || (*fmt == '1'))
83                 {
84                     if (SearchPathA(NULL, lpFile, ".exe", sizeof(xlpFile), xlpFile, NULL))
85                     {
86                         strcpy(res, xlpFile);
87                         res += strlen(xlpFile);
88                     }
89                     else
90                     {
91                         strcpy(res, lpFile);
92                         res += strlen(lpFile);
93                     }
94                 }
95                 break;
96             default: FIXME("Unknown escape sequence %%%c\n", *fmt);
97             }
98             fmt++;
99             done = TRUE;
100         }
101         else
102             *res++ = *fmt++;
103     }
104     *res = '\0';
105     return done;
106 }
107
108 /*************************************************************************
109  *      SHELL_ExecuteA [Internal]
110  *
111  */
112 static UINT SHELL_ExecuteA(char *lpCmd, LPSHELLEXECUTEINFOA sei, BOOL shWait)
113 {
114     STARTUPINFOA  startup;
115     PROCESS_INFORMATION info;
116     UINT retval = 31;
117
118     TRACE("Execute %s from directory %s\n", lpCmd, sei->lpDirectory);
119     ZeroMemory(&startup,sizeof(STARTUPINFOA));
120     startup.cb = sizeof(STARTUPINFOA);
121     startup.dwFlags = STARTF_USESHOWWINDOW;
122     startup.wShowWindow = sei->nShow;
123     if (CreateProcessA(NULL, lpCmd, NULL, NULL, FALSE, 0,
124                        NULL, sei->lpDirectory, &startup, &info))
125     {
126         /* Give 30 seconds to the app to come up, if desired. Probably only needed
127            when starting app immediately before making a DDE connection. */
128         if (shWait)
129             if (WaitForInputIdle( info.hProcess, 30000 ) == -1)
130                 WARN("WaitForInputIdle failed: Error %ld\n", GetLastError() );
131         retval = 33;
132         if(sei->fMask & SEE_MASK_NOCLOSEPROCESS)
133             sei->hProcess = info.hProcess;
134         else
135             CloseHandle( info.hProcess );
136         CloseHandle( info.hThread );
137     }
138     else if ((retval = GetLastError()) >= 32)
139     {
140         FIXME("Strange error set by CreateProcess: %d\n", retval);
141         retval = ERROR_BAD_FORMAT;
142     }
143
144     sei->hInstApp = (HINSTANCE)retval;
145     return retval;
146 }
147
148 /***********************************************************************
149  *           SHELL_TryAppPath
150  *
151  * Helper function for SHELL_FindExecutable
152  * @param lpResult - pointer to a buffer of size MAX_PATH
153  * On entry: szName is a filename (probably without path separators).
154  * On exit: if szName found in "App Path", place full path in lpResult, and return true
155  */
156 static BOOL SHELL_TryAppPath( LPCSTR szName, LPSTR lpResult)
157 {
158     HKEY hkApp = 0;
159     char szAppKey[256];
160     LONG len;
161     LONG res; 
162     BOOL found = FALSE;
163
164     sprintf(szAppKey, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s", szName);
165     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, szAppKey, 0, KEY_READ, &hkApp);
166     if (res) {
167         /*TRACE("RegOpenKeyExA(HKEY_LOCAL_MACHINE, %s,) returns %ld\n", szAppKey, res);*/
168         goto end;
169     }
170
171     len = MAX_PATH;
172     res = RegQueryValueA(hkApp, NULL, lpResult, &len);
173     if (res) {
174         /*TRACE("RegQueryValueA(hkApp, NULL,) returns %ld\n", res);*/
175         goto end;
176     }
177     /*TRACE("%s -> %s\n", szName, lpResult);*/
178     found = TRUE;
179
180 end:
181     if (hkApp) RegCloseKey(hkApp);
182     return found;
183 }
184
185 /*************************************************************************
186  *      SHELL_FindExecutable [Internal]
187  *
188  * Utility for code sharing between FindExecutable and ShellExecute
189  * in:
190  *      lpFile the name of a file
191  *      lpOperation the operation on it (open)
192  * out:
193  *      lpResult a buffer, big enough :-(, to store the command to do the
194  *              operation on the file
195  *      key a buffer, big enough, to get the key name to do actually the
196  *              command (it'll be used afterwards for more information
197  *              on the operation)
198  */
199 static UINT SHELL_FindExecutable(LPCSTR lpPath, LPCSTR lpFile, LPCSTR lpOperation,
200                                  LPSTR lpResult, LPSTR key)
201 {
202     char *extension = NULL; /* pointer to file extension */
203     char tmpext[5];         /* local copy to mung as we please */
204     char filetype[256];     /* registry name for this filetype */
205     LONG filetypelen = 256; /* length of above */
206     char command[256];      /* command from registry */
207     LONG commandlen = 256;  /* This is the most DOS can handle :) */
208     char buffer[256];       /* Used to GetProfileString */
209     UINT retval = 31;  /* default - 'No association was found' */
210     char *tok;              /* token pointer */
211     char xlpFile[256] = ""; /* result of SearchPath */
212
213     TRACE("%s\n", (lpFile != NULL) ? lpFile : "-");
214
215     lpResult[0] = '\0'; /* Start off with an empty return string */
216     if (key) *key = '\0';
217
218     /* trap NULL parameters on entry */
219     if ((lpFile == NULL) || (lpResult == NULL) || (lpOperation == NULL))
220     {
221         WARN("(lpFile=%s,lpResult=%s,lpOperation=%s): NULL parameter\n",
222              lpFile, lpOperation, lpResult);
223         return 2; /* File not found. Close enough, I guess. */
224     }
225
226     if (SHELL_TryAppPath( lpFile, lpResult ))
227     {
228         TRACE("found %s via App Paths\n", lpResult);
229         return 33;
230     }
231
232     if (SearchPathA(lpPath, lpFile, ".exe", sizeof(xlpFile), xlpFile, NULL))
233     {
234         TRACE("SearchPathA returned non-zero\n");
235         lpFile = xlpFile;
236         /* Hey, isn't this value ignored?  Why make this call?  Shouldn't we return here?  --dank*/
237     }
238
239     /* First thing we need is the file's extension */
240     extension = strrchr(xlpFile, '.'); /* Assume last "." is the one; */
241                                        /* File->Run in progman uses */
242                                        /* .\FILE.EXE :( */
243     TRACE("xlpFile=%s,extension=%s\n", xlpFile, extension);
244
245     if ((extension == NULL) || (extension == &xlpFile[strlen(xlpFile)]))
246     {
247         WARN("Returning 31 - No association\n");
248         return 31; /* no association */
249     }
250
251     /* Make local copy & lowercase it for reg & 'programs=' lookup */
252     lstrcpynA(tmpext, extension, 5);
253     CharLowerA(tmpext);
254     TRACE("%s file\n", tmpext);
255
256     /* Three places to check: */
257     /* 1. win.ini, [windows], programs (NB no leading '.') */
258     /* 2. Registry, HKEY_CLASS_ROOT\<filetype>\shell\open\command */
259     /* 3. win.ini, [extensions], extension (NB no leading '.' */
260     /* All I know of the order is that registry is checked before */
261     /* extensions; however, it'd make sense to check the programs */
262     /* section first, so that's what happens here. */
263
264     /* See if it's a program - if GetProfileString fails, we skip this
265      * section. Actually, if GetProfileString fails, we've probably
266      * got a lot more to worry about than running a program... */
267     if (GetProfileStringA("windows", "programs", "exe pif bat com",
268                           buffer, sizeof(buffer)) > 0)
269     {
270         UINT i;
271
272         for (i = 0;i<strlen(buffer); i++) buffer[i] = tolower(buffer[i]);
273
274         tok = strtok(buffer, " \t"); /* ? */
275         while (tok!= NULL)
276         {
277             if (strcmp(tok, &tmpext[1]) == 0) /* have to skip the leading "." */
278             {
279                 strcpy(lpResult, xlpFile);
280                 /* Need to perhaps check that the file has a path
281                  * attached */
282                 TRACE("found %s\n", lpResult);
283                 return 33;
284
285                 /* Greater than 32 to indicate success FIXME According to the
286                  * docs, I should be returning a handle for the
287                  * executable. Does this mean I'm supposed to open the
288                  * executable file or something? More RTFM, I guess... */
289             }
290             tok = strtok(NULL, " \t");
291         }
292     }
293
294     /* Check registry */
295     if (RegQueryValueA(HKEY_CLASSES_ROOT, tmpext, filetype,
296                        &filetypelen) == ERROR_SUCCESS)
297     {
298         filetype[filetypelen] = '\0';
299         TRACE("File type: %s\n", filetype);
300
301         /* Looking for ...buffer\shell\lpOperation\command */
302         strcat(filetype, "\\shell\\");
303         strcat(filetype, lpOperation);
304         strcat(filetype, "\\command");
305
306         if (RegQueryValueA(HKEY_CLASSES_ROOT, filetype, command,
307                            &commandlen) == ERROR_SUCCESS)
308         {
309             if (key) strcpy(key, filetype);
310 #if 0
311             LPSTR tmp;
312             char param[256];
313             LONG paramlen = 256;
314
315             /* FIXME: it seems all Windows version don't behave the same here.
316              * the doc states that this ddeexec information can be found after
317              * the exec names.
318              * on Win98, it doesn't appear, but I think it does on Win2k
319              */
320             /* Get the parameters needed by the application
321                from the associated ddeexec key */
322             tmp = strstr(filetype, "command");
323             tmp[0] = '\0';
324             strcat(filetype, "ddeexec");
325
326             if (RegQueryValueA(HKEY_CLASSES_ROOT, filetype, param, &paramlen) == ERROR_SUCCESS)
327             {
328                 strcat(command, " ");
329                 strcat(command, param);
330                 commandlen += paramlen;
331             }
332 #endif
333             command[commandlen] = '\0';
334             argify(lpResult, sizeof(lpResult), command, xlpFile);
335             retval = 33; /* FIXME see above */
336         }
337     }
338     else /* Check win.ini */
339     {
340         /* Toss the leading dot */
341         extension++;
342         if (GetProfileStringA("extensions", extension, "", command,
343                               sizeof(command)) > 0)
344         {
345             if (strlen(command) != 0)
346             {
347                 strcpy(lpResult, command);
348                 tok = strstr(lpResult, "^"); /* should be ^.extension? */
349                 if (tok != NULL)
350                 {
351                     tok[0] = '\0';
352                     strcat(lpResult, xlpFile); /* what if no dir in xlpFile? */
353                     tok = strstr(command, "^"); /* see above */
354                     if ((tok != NULL) && (strlen(tok)>5))
355                     {
356                         strcat(lpResult, &tok[5]);
357                     }
358                 }
359                 retval = 33; /* FIXME - see above */
360             }
361         }
362     }
363
364     TRACE("returning %s\n", lpResult);
365     return retval;
366 }
367
368 /******************************************************************
369  *              dde_cb
370  *
371  * callback for the DDE connection. not really usefull
372  */
373 static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv,
374                                 HSZ hsz1, HSZ hsz2,
375                                 HDDEDATA hData, DWORD dwData1, DWORD dwData2)
376 {
377     return NULL;
378 }
379
380 /******************************************************************
381  *              dde_connect
382  *
383  * ShellExecute helper. Used to do an operation with a DDE connection
384  *
385  * Handles both the direct connection (try #1), and if it fails,
386  * launching an application and trying (#2) to connect to it
387  *
388  */
389 static unsigned dde_connect(char* key, char* start, char* ddeexec,
390                             const char* lpFile,
391                             LPSHELLEXECUTEINFOA sei, SHELL_ExecuteA1632 execfunc)
392 {
393     char*       endkey = key + strlen(key);
394     char        app[256], topic[256], ifexec[256], res[256];
395     LONG        applen, topiclen, ifexeclen;
396     char*       exec;
397     DWORD       ddeInst = 0;
398     DWORD       tid;
399     HSZ         hszApp, hszTopic;
400     HCONV       hConv;
401     unsigned    ret = 31;
402
403     strcpy(endkey, "\\application");
404     applen = sizeof(app);
405     if (RegQueryValueA(HKEY_CLASSES_ROOT, key, app, &applen) != ERROR_SUCCESS)
406     {
407         FIXME("default app name NIY %s\n", key);
408         return 2;
409     }
410
411     strcpy(endkey, "\\topic");
412     topiclen = sizeof(topic);
413     if (RegQueryValueA(HKEY_CLASSES_ROOT, key, topic, &topiclen) != ERROR_SUCCESS)
414     {
415         strcpy(topic, "System");
416     }
417
418     if (DdeInitializeA(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR)
419     {
420         return 2;
421     }
422
423     hszApp = DdeCreateStringHandleA(ddeInst, app, CP_WINANSI);
424     hszTopic = DdeCreateStringHandleA(ddeInst, topic, CP_WINANSI);
425
426     hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
427     exec = ddeexec;
428     if (!hConv)
429     {
430         TRACE("Launching '%s'\n", start);
431         ret = execfunc(start, sei, TRUE);
432         if (ret < 32)
433         {
434             TRACE("Couldn't launch\n");
435             goto error;
436         }
437         hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
438         if (!hConv)
439         {
440             TRACE("Couldn't connect. ret=%d\n", ret);
441             ret = 30; /* whatever */
442             goto error;
443         }
444         strcpy(endkey, "\\ifexec");
445         ifexeclen = sizeof(ifexec);
446         if (RegQueryValueA(HKEY_CLASSES_ROOT, key, ifexec, &ifexeclen) == ERROR_SUCCESS)
447         {
448             exec = ifexec;
449         }
450     }
451
452     argify(res, sizeof(res), exec, lpFile);
453     TRACE("%s %s => %s\n", exec, lpFile, res);
454
455     ret = (DdeClientTransaction(res, strlen(res) + 1, hConv, 0L, 0,
456                                 XTYP_EXECUTE, 10000, &tid) != DMLERR_NO_ERROR) ? 31 : 33;
457     DdeDisconnect(hConv);
458  error:
459     DdeUninitialize(ddeInst);
460     return ret;
461 }
462
463 /*************************************************************************
464  *      execute_from_key [Internal]
465  */
466 static UINT execute_from_key(LPSTR key, LPCSTR lpFile, LPSHELLEXECUTEINFOA sei, SHELL_ExecuteA1632 execfunc)
467 {
468     char cmd[1024] = "";
469     LONG cmdlen = sizeof(cmd);
470     UINT retval = 31;
471
472     /* Get the application for the registry */
473     if (RegQueryValueA(HKEY_CLASSES_ROOT, key, cmd, &cmdlen) == ERROR_SUCCESS)
474     {
475         LPSTR tmp;
476         char param[256] = "";
477         LONG paramlen = 256;
478
479         /* Get the parameters needed by the application
480            from the associated ddeexec key */
481         tmp = strstr(key, "command");
482         assert(tmp);
483         strcpy(tmp, "ddeexec");
484
485         if (RegQueryValueA(HKEY_CLASSES_ROOT, key, param, &paramlen) == ERROR_SUCCESS)
486         {
487             TRACE("Got ddeexec %s => %s\n", key, param);
488             retval = dde_connect(key, cmd, param, lpFile, sei, execfunc);
489         }
490         else
491         {
492             /* Is there a replace() function anywhere? */
493             cmd[cmdlen] = '\0';
494             argify(param, sizeof(param), cmd, lpFile);
495             retval = execfunc(param, sei, FALSE);
496         }
497     }
498     else TRACE("ooch\n");
499
500     return retval;
501 }
502
503 /*************************************************************************
504  * FindExecutableA                      [SHELL32.@]
505  */
506 HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
507 {
508     UINT retval = 31;    /* default - 'No association was found' */
509     char old_dir[1024];
510
511     TRACE("File %s, Dir %s\n",
512           (lpFile != NULL ? lpFile : "-"), (lpDirectory != NULL ? lpDirectory : "-"));
513
514     lpResult[0] = '\0'; /* Start off with an empty return string */
515
516     /* trap NULL parameters on entry */
517     if ((lpFile == NULL) || (lpResult == NULL))
518     {
519         /* FIXME - should throw a warning, perhaps! */
520         return (HINSTANCE)2; /* File not found. Close enough, I guess. */
521     }
522
523     if (lpDirectory)
524     {
525         GetCurrentDirectoryA(sizeof(old_dir), old_dir);
526         SetCurrentDirectoryA(lpDirectory);
527     }
528
529     retval = SHELL_FindExecutable(lpDirectory, lpFile, "open", lpResult, NULL);
530
531     TRACE("returning %s\n", lpResult);
532     if (lpDirectory)
533         SetCurrentDirectoryA(old_dir);
534     return (HINSTANCE)retval;
535 }
536
537 /*************************************************************************
538  * FindExecutableW                      [SHELL32.@]
539  */
540 HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult)
541 {
542     FIXME("(%p,%p,%p): stub\n", lpFile, lpDirectory, lpResult);
543     return (HINSTANCE)31;    /* default - 'No association was found' */
544 }
545
546 /*************************************************************************
547  *      ShellExecuteExA32 [Internal]
548  */
549 BOOL WINAPI ShellExecuteExA32 (LPSHELLEXECUTEINFOA sei, SHELL_ExecuteA1632 execfunc)
550 {
551     CHAR szApplicationName[MAX_PATH],szCommandline[MAX_PATH],szPidl[20],fileName[MAX_PATH];
552     LPSTR pos;
553     int gap, len;
554     char lpstrProtocol[256];
555     LPCSTR lpFile,lpOperation;
556     UINT retval = 31;
557     char cmd[1024];
558     BOOL done;
559
560     TRACE("mask=0x%08lx hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n",
561             sei->fMask, sei->hwnd, debugstr_a(sei->lpVerb),
562             debugstr_a(sei->lpFile), debugstr_a(sei->lpParameters),
563             debugstr_a(sei->lpDirectory), sei->nShow,
564             (sei->fMask & SEE_MASK_CLASSNAME) ? debugstr_a(sei->lpClass) : "not used");
565
566     sei->hProcess = (HANDLE)NULL;
567     ZeroMemory(szApplicationName,MAX_PATH);
568     if (sei->lpFile)
569         strcpy(szApplicationName, sei->lpFile);
570
571     ZeroMemory(szCommandline,MAX_PATH);
572     if (sei->lpParameters)
573         strcpy(szCommandline, sei->lpParameters);
574
575     if (sei->fMask & ((SEE_MASK_CLASSKEY & ~SEE_MASK_CLASSNAME) |
576         SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY |
577         SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT |
578         SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI | SEE_MASK_UNICODE |
579         SEE_MASK_NO_CONSOLE | SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR ))
580     {
581         FIXME("flags ignored: 0x%08lx\n", sei->fMask);
582     }
583
584     /* process the IDList */
585     if ( (sei->fMask & SEE_MASK_INVOKEIDLIST) == SEE_MASK_INVOKEIDLIST) /*0x0c*/
586     {
587         SHGetPathFromIDListA (sei->lpIDList,szApplicationName);
588         TRACE("-- idlist=%p (%s)\n", sei->lpIDList, szApplicationName);
589     }
590     else
591     {
592         if (sei->fMask & SEE_MASK_IDLIST )
593         {
594             pos = strstr(szCommandline, "%I");
595             if (pos)
596             {
597                 LPVOID pv;
598                 HGLOBAL hmem = SHAllocShared ( sei->lpIDList, ILGetSize(sei->lpIDList), 0);
599                 pv = SHLockShared(hmem,0);
600                 sprintf(szPidl,":%p",pv );
601                 SHUnlockShared(pv);
602
603                 gap = strlen(szPidl);
604                 len = strlen(pos)-2;
605                 memmove(pos+gap,pos+2,len);
606                 memcpy(pos,szPidl,gap);
607             }
608         }
609     }
610
611     if (sei->fMask & SEE_MASK_CLASSNAME)
612     {
613         /* launch a document by fileclass like 'WordPad.Document.1' */
614         /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */
615         /* FIXME: szCommandline should not be of a fixed size. Plus MAX_PATH is way too short! */
616         HCR_GetExecuteCommandA(sei->lpClass, (sei->lpVerb) ? sei->lpVerb : "open", szCommandline, sizeof(szCommandline));
617         /* FIXME: get the extension of lpFile, check if it fits to the lpClass */
618         TRACE("SEE_MASK_CLASSNAME->'%s', doc->'%s'\n", szCommandline, szApplicationName);
619
620         cmd[0] = '\0';
621         done = argify(cmd, sizeof(cmd), szCommandline, szApplicationName);
622         if (!done && szApplicationName[0])
623         {
624             strcat(cmd, " ");
625             strcat(cmd, szApplicationName);
626         }
627         retval = execfunc(cmd, sei, FALSE);
628         if (retval > 32)
629             return TRUE;
630         else
631             return FALSE;
632     }
633
634     /* We set the default to open, and that should generally work.
635        But that is not really the way the MS docs say to do it. */
636     if (sei->lpVerb == NULL)
637         lpOperation = "open";
638     else
639         lpOperation = sei->lpVerb;
640
641     /* Else, try to execute the filename */
642     TRACE("execute:'%s','%s'\n",szApplicationName, szCommandline);
643
644     strcpy(fileName, szApplicationName);
645     lpFile = fileName;
646     if (szCommandline[0]) {
647         strcat(szApplicationName, " ");
648         strcat(szApplicationName, szCommandline);
649     }
650
651     retval = execfunc(szApplicationName, sei, FALSE);
652     if (retval > 32)
653         return TRUE;
654
655     /* Else, try to find the executable */
656     cmd[0] = '\0';
657     retval = SHELL_FindExecutable(sei->lpDirectory, lpFile, lpOperation, cmd, lpstrProtocol);
658     if (retval > 32)  /* Found */
659     {
660         CHAR szQuotedCmd[MAX_PATH+2];
661         /* Must quote to handle case where cmd contains spaces, 
662          * else security hole if malicious user creates executable file "C:\\Program"
663          */
664         if (szCommandline[0])
665             sprintf(szQuotedCmd, "\"%s\" %s", cmd, szCommandline);
666         else
667             sprintf(szQuotedCmd, "\"%s\"", cmd);
668         TRACE("%s/%s => %s/%s\n", szApplicationName, lpOperation, szQuotedCmd, lpstrProtocol);
669         if (*lpstrProtocol)
670             retval = execute_from_key(lpstrProtocol, szApplicationName, sei, execfunc);
671         else
672             retval = execfunc(szQuotedCmd, sei, FALSE);
673     }
674     else if (PathIsURLA((LPSTR)lpFile))    /* File not found, check for URL */
675     {
676         LPSTR lpstrRes;
677         INT iSize;
678
679         lpstrRes = strchr(lpFile, ':');
680         if (lpstrRes)
681             iSize = lpstrRes - lpFile;
682         else
683             iSize = strlen(lpFile);
684
685         TRACE("Got URL: %s\n", lpFile);
686         /* Looking for ...protocol\shell\lpOperation\command */
687         strncpy(lpstrProtocol, lpFile, iSize);
688         lpstrProtocol[iSize] = '\0';
689         strcat(lpstrProtocol, "\\shell\\");
690         strcat(lpstrProtocol, lpOperation);
691         strcat(lpstrProtocol, "\\command");
692
693         /* Remove File Protocol from lpFile */
694         /* In the case file://path/file     */
695         if (!strncasecmp(lpFile, "file", iSize))
696         {
697             lpFile += iSize;
698             while (*lpFile == ':') lpFile++;
699         }
700         retval = execute_from_key(lpstrProtocol, lpFile, sei, execfunc);
701     }
702     /* Check if file specified is in the form www.??????.*** */
703     else if (!strncasecmp(lpFile, "www", 3))
704     {
705         /* if so, append lpFile http:// and call ShellExecute */
706         char lpstrTmpFile[256] = "http://" ;
707         strcat(lpstrTmpFile, lpFile);
708         retval = (UINT)ShellExecuteA(sei->hwnd, lpOperation, lpstrTmpFile, NULL, NULL, 0);
709     }
710
711     if (retval <= 32)
712     {
713         sei->hInstApp = (HINSTANCE)retval;
714         return FALSE;
715     }
716
717     sei->hInstApp = (HINSTANCE)33;
718     return TRUE;
719 }
720
721 /*************************************************************************
722  * ShellExecuteA                        [SHELL32.290]
723  */
724 HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation,LPCSTR lpFile,
725                                LPCSTR lpParameters,LPCSTR lpDirectory, INT iShowCmd)
726 {
727     SHELLEXECUTEINFOA sei;
728     HANDLE hProcess = 0;
729
730     TRACE("\n");
731     sei.cbSize = sizeof(sei);
732     sei.fMask = 0;
733     sei.hwnd = hWnd;
734     sei.lpVerb = lpOperation;
735     sei.lpFile = lpFile;
736     sei.lpParameters = lpParameters;
737     sei.lpDirectory = lpDirectory;
738     sei.nShow = iShowCmd;
739     sei.lpIDList = 0;
740     sei.lpClass = 0;
741     sei.hkeyClass = 0;
742     sei.dwHotKey = 0;
743     sei.hProcess = hProcess;
744
745     ShellExecuteExA32 (&sei, SHELL_ExecuteA);
746     return sei.hInstApp;
747 }
748
749 /*************************************************************************
750  * ShellExecuteEx                               [SHELL32.291]
751  *
752  */
753 BOOL WINAPI ShellExecuteExAW (LPVOID sei)
754 {
755     if (SHELL_OsIsUnicode())
756         return ShellExecuteExW (sei);
757     return ShellExecuteExA32 (sei, SHELL_ExecuteA);
758 }
759
760 /*************************************************************************
761  * ShellExecuteExA                              [SHELL32.292]
762  *
763  */
764 BOOL WINAPI ShellExecuteExA (LPSHELLEXECUTEINFOA sei)
765 {
766     return  ShellExecuteExA32 (sei, SHELL_ExecuteA);
767 }
768
769 /*************************************************************************
770  * ShellExecuteExW                              [SHELL32.293]
771  *
772  */
773 BOOL WINAPI ShellExecuteExW (LPSHELLEXECUTEINFOW sei)
774 {
775     SHELLEXECUTEINFOA seiA;
776     DWORD ret;
777
778     TRACE("%p\n", sei);
779
780     memcpy(&seiA, sei, sizeof(SHELLEXECUTEINFOA));
781
782     if (sei->lpVerb)
783         seiA.lpVerb = HEAP_strdupWtoA( GetProcessHeap(), 0, sei->lpVerb);
784
785     if (sei->lpFile)
786         seiA.lpFile = HEAP_strdupWtoA( GetProcessHeap(), 0, sei->lpFile);
787
788     if (sei->lpParameters)
789         seiA.lpParameters = HEAP_strdupWtoA( GetProcessHeap(), 0, sei->lpParameters);
790
791     if (sei->lpDirectory)
792         seiA.lpDirectory = HEAP_strdupWtoA( GetProcessHeap(), 0, sei->lpDirectory);
793
794     if ((sei->fMask & SEE_MASK_CLASSNAME) && sei->lpClass)
795         seiA.lpClass = HEAP_strdupWtoA( GetProcessHeap(), 0, sei->lpClass);
796     else
797         seiA.lpClass = NULL;
798
799     ret = ShellExecuteExA(&seiA);
800
801     if (seiA.lpVerb)    HeapFree( GetProcessHeap(), 0, (LPSTR) seiA.lpVerb );
802     if (seiA.lpFile)    HeapFree( GetProcessHeap(), 0, (LPSTR) seiA.lpFile );
803     if (seiA.lpParameters)      HeapFree( GetProcessHeap(), 0, (LPSTR) seiA.lpParameters );
804     if (seiA.lpDirectory)       HeapFree( GetProcessHeap(), 0, (LPSTR) seiA.lpDirectory );
805     if (seiA.lpClass)   HeapFree( GetProcessHeap(), 0, (LPSTR) seiA.lpClass );
806
807     return ret;
808 }
809
810 /*************************************************************************
811  * ShellExecuteW                        [SHELL32.294]
812  * from shellapi.h
813  * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpOperation,
814  * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
815  */
816 HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile,
817                                LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd)
818 {
819     SHELLEXECUTEINFOW sei;
820     HANDLE hProcess = 0;
821
822     TRACE("\n");
823     sei.cbSize = sizeof(sei);
824     sei.fMask = 0;
825     sei.hwnd = hwnd;
826     sei.lpVerb = lpOperation;
827     sei.lpFile = lpFile;
828     sei.lpParameters = lpParameters;
829     sei.lpDirectory = lpDirectory;
830     sei.nShow = nShowCmd;
831     sei.lpIDList = 0;
832     sei.lpClass = 0;
833     sei.hkeyClass = 0;
834     sei.dwHotKey = 0;
835     sei.hProcess = hProcess;
836
837     ShellExecuteExW (&sei);
838     return sei.hInstApp;
839 }