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