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