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