shlexec.c 68.6 KB
Newer Older
1 2 3
/*
 * 				Shell Library Functions
 *
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * Copyright 1998 Marcus Meissner
 * Copyright 2002 Eric Pouech
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22
 */

#include "config.h"
23
#include "wine/port.h"
24 25 26

#include <stdlib.h>
#include <string.h>
27
#include <stdarg.h>
28
#include <stdio.h>
29 30 31
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
32 33 34
#include <ctype.h>
#include <assert.h>

35 36
#define COBJMACROS

37
#include "windef.h"
38
#include "winbase.h"
39 40
#include "winerror.h"
#include "winreg.h"
41
#include "winuser.h"
42 43 44 45
#include "shlwapi.h"
#include "ddeml.h"

#include "shell32_main.h"
46
#include "pidl.h"
47
#include "shresdef.h"
48

49
#include "wine/debug.h"
50

51
WINE_DEFAULT_DEBUG_CHANNEL(exec);
52

53 54
static const WCHAR wszOpen[] = {'o','p','e','n',0};
static const WCHAR wszExe[] = {'.','e','x','e',0};
55
static const WCHAR wszILPtr[] = {':','%','p',0};
56
static const WCHAR wszShell[] = {'\\','s','h','e','l','l','\\',0};
57 58
static const WCHAR wszFolder[] = {'F','o','l','d','e','r',0};
static const WCHAR wszEmpty[] = {0};
59

60 61
#define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)

62 63 64
typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
			    const SHELLEXECUTEINFOW *sei, LPSHELLEXECUTEINFOW sei_out);

65

66
/***********************************************************************
67 68
 *	SHELL_ArgifyW [Internal]
 *
69
 * this function is supposed to expand the escape sequences found in the registry
70
 * some diving reported that the following were used:
71
 * + %1, %2...  seem to report to parameter of index N in ShellExecute pmts
72 73 74 75
 *	%1 file
 *	%2 printer
 *	%3 driver
 *	%4 port
Francois Gouget's avatar
Francois Gouget committed
76
 * %I address of a global item ID (explorer switch /idlist)
77 78 79
 * %L seems to be %1 as long filename followed by the 8+3 variation
 * %S ???
 * %* all following parameters (see batfile)
80
 *
81
 */
82
static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len)
83
{
84 85
    WCHAR   xlpFile[1024];
    BOOL    done = FALSE;
86
    BOOL    found_p1 = FALSE;
87 88
    PWSTR   res = out;
    PCWSTR  cmd;
89
    DWORD   used = 0;
90

91 92 93
    TRACE("%p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt),
          debugstr_w(lpFile), pidl, args);

94 95 96 97 98 99 100
    while (*fmt)
    {
        if (*fmt == '%')
        {
            switch (*++fmt)
            {
            case '\0':
101
            case '%':
102 103 104
                used++;
                if (used < len)
                    *res++ = '%';
105
                break;
106 107 108 109 110 111 112 113 114 115

            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '0':
116
            case '*':
117 118 119 120
                if (args)
                {
                    if (*fmt == '*')
                    {
121 122 123
                        used++;
                        if (used < len)
                            *res++ = '"';
124
                        while(*args)
125 126 127 128 129 130 131 132 133 134
                        {
                            used++;
                            if (used < len)
                                *res++ = *args++;
                            else
                                args++;
                        }
                        used++;
                        if (used < len)
                            *res++ = '"';
135 136 137 138
                    }
                    else
                    {
                        while(*args && !isspace(*args))
139 140 141 142 143 144 145
                        {
                            used++;
                            if (used < len)
                                *res++ = *args++;
                            else
                                args++;
                        }
146 147 148 149 150 151 152 153

                        while(isspace(*args))
                            ++args;
                    }
                    break;
                }
                /* else fall through */
            case '1':
154
                if (!done || (*fmt == '1'))
155
                {
156
                    /*FIXME Is the call to SearchPathW() really needed? We already have separated out the parameter string in args. */
157
                    if (SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
158 159 160 161
                        cmd = xlpFile;
                    else
                        cmd = lpFile;

162 163
                    used += strlenW(cmd);
                    if (used < len)
164
                    {
165 166
                        strcpyW(res, cmd);
                        res += strlenW(cmd);
167
                    }
168
                }
169
                found_p1 = TRUE;
170
                break;
171

172
            /*
173
             * IE uses this a lot for activating things such as windows media
174 175 176
             * player. This is not verified to be fully correct but it appears
             * to work just fine.
             */
177
            case 'l':
178
            case 'L':
179
		if (lpFile) {
180 181 182 183 184 185
		    used += strlenW(lpFile);
		    if (used < len)
		    {
			strcpyW(res, lpFile);
			res += strlenW(lpFile);
		    }
186
		}
187
		found_p1 = TRUE;
188 189 190 191 192
                break;

            case 'i':
            case 'I':
		if (pidl) {
193
		    INT chars = 0;
194
		    /* %p should not exceed 8, maybe 16 when looking forward to 64bit.
195 196 197
		     * allowing a buffer of 100 should more than exceed all needs */
		    WCHAR buf[100];
		    LPVOID  pv;
198 199
		    HGLOBAL hmem = SHAllocShared(pidl, ILGetSize(pidl), 0);
		    pv = SHLockShared(hmem, 0);
200 201
		    chars = sprintfW(buf, wszILPtr, pv);
		    if (chars >= sizeof(buf)/sizeof(WCHAR))
202
			ERR("pidl format buffer too small!\n");
203 204 205 206 207 208
		    used += chars;
		    if (used < len)
		    {
			strcpyW(res,buf);
			res += chars;
		    }
209 210
		    SHUnlockShared(pv);
		}
211
                found_p1 = TRUE;
212 213
                break;

214 215
	    default:
                /*
216
                 * Check if this is an env-variable here...
217 218 219 220 221 222 223 224 225 226 227 228 229 230
                 */

                /* Make sure that we have at least one more %.*/
                if (strchrW(fmt, '%'))
                {
                    WCHAR   tmpBuffer[1024];
                    PWSTR   tmpB = tmpBuffer;
                    WCHAR   tmpEnvBuff[MAX_PATH];
                    DWORD   envRet;

                    while (*fmt != '%')
                        *tmpB++ = *fmt++;
                    *tmpB++ = 0;

231
                    TRACE("Checking %s to be an env-var\n", debugstr_w(tmpBuffer));
232 233 234

                    envRet = GetEnvironmentVariableW(tmpBuffer, tmpEnvBuff, MAX_PATH);
                    if (envRet == 0 || envRet > MAX_PATH)
235 236 237 238 239 240 241 242
                    {
                        used += strlenW(tmpBuffer);
                        if (used < len)
                        {
                            strcpyW( res, tmpBuffer );
                            res += strlenW(tmpBuffer);
                        }
                    }
243
                    else
244 245 246 247 248 249 250 251
                    {
                        used += strlenW(tmpEnvBuff);
                        if (used < len)
                        {
                            strcpyW( res, tmpEnvBuff );
                            res += strlenW(tmpEnvBuff);
                        }
                    }
252 253 254
                }
                done = TRUE;
                break;
255
            }
256 257 258 259 260
            /* Don't skip past terminator (catch a single '%' at the end) */
            if (*fmt != '\0')
            {
                fmt++;
            }
261 262
        }
        else
263 264 265 266 267 268 269
        {
            used ++;
            if (used < len) 
                *res++ = *fmt++;
            else
                fmt++;
        }
270
    }
271

272 273 274 275 276 277
    used ++;
    if (res - out < len)
        *res = '\0';
    else
        out[len-1] = '\0';

278 279 280
    TRACE("used %i of %i space\n",used,len);
    if (out_len)
        *out_len = used;
281

282
    return found_p1;
Duane Clark's avatar
Duane Clark committed
283 284
}

285
static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize)
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
{
    STRRET strret;
    IShellFolder* desktop;

    HRESULT hr = SHGetDesktopFolder(&desktop);

    if (SUCCEEDED(hr)) {
	hr = IShellFolder_GetDisplayNameOf(desktop, pidl, SHGDN_FORPARSING, &strret);

	if (SUCCEEDED(hr))
	    StrRetToStrNW(pszPath, uOutSize, &strret, pidl);

	IShellFolder_Release(desktop);
    }

    return hr;
}

Duane Clark's avatar
Duane Clark committed
304
/*************************************************************************
305
 *	SHELL_ExecuteW [Internal]
Duane Clark's avatar
Duane Clark committed
306 307
 *
 */
Kevin Koltzau's avatar
Kevin Koltzau committed
308
static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
309
			    const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out)
Duane Clark's avatar
Duane Clark committed
310
{
311
    STARTUPINFOW  startup;
Duane Clark's avatar
Duane Clark committed
312
    PROCESS_INFORMATION info;
313
    UINT_PTR retval = SE_ERR_NOASSOC;
314 315
    UINT gcdret = 0;
    WCHAR curdir[MAX_PATH];
316
    DWORD dwCreationFlags;
317
    const WCHAR *lpDirectory = NULL;
Duane Clark's avatar
Duane Clark committed
318

319
    TRACE("Execute %s from directory %s\n", debugstr_w(lpCmd), debugstr_w(psei->lpDirectory));
320 321 322 323 324 325 326 327 328 329

    /* make sure we don't fail the CreateProcess if the calling app passes in
     * a bad working directory */
    if (psei->lpDirectory && psei->lpDirectory[0])
    {
        DWORD attr = GetFileAttributesW(psei->lpDirectory);
        if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY)
            lpDirectory = psei->lpDirectory;
    }

330 331
    /* ShellExecute specifies the command from psei->lpDirectory
     * if present. Not from the current dir as CreateProcess does */
332
    if( lpDirectory )
333
        if( ( gcdret = GetCurrentDirectoryW( MAX_PATH, curdir)))
334 335
            if( !SetCurrentDirectoryW( lpDirectory))
                ERR("cannot set directory %s\n", debugstr_w(lpDirectory));
336 337
    ZeroMemory(&startup,sizeof(STARTUPINFOW));
    startup.cb = sizeof(STARTUPINFOW);
Duane Clark's avatar
Duane Clark committed
338
    startup.dwFlags = STARTF_USESHOWWINDOW;
339
    startup.wShowWindow = psei->nShow;
340 341 342 343
    dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
    if (psei->fMask & SEE_MASK_NO_CONSOLE)
        dwCreationFlags |= CREATE_NEW_CONSOLE;
    if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env,
344
                       lpDirectory, &startup, &info))
Duane Clark's avatar
Duane Clark committed
345
    {
346 347 348
        /* Give 30 seconds to the app to come up, if desired. Probably only needed
           when starting app immediately before making a DDE connection. */
        if (shWait)
349
            if (WaitForInputIdle( info.hProcess, 30000 ) == WAIT_FAILED)
350
                WARN("WaitForInputIdle failed: Error %d\n", GetLastError() );
351
        retval = 33;
352 353
        if (psei->fMask & SEE_MASK_NOCLOSEPROCESS)
            psei_out->hProcess = info.hProcess;
354 355 356 357
        else
            CloseHandle( info.hProcess );
        CloseHandle( info.hThread );
    }
358
    else if ((retval = GetLastError()) >= 32)
359
    {
360
        TRACE("CreateProcess returned error %ld\n", retval);
361
        retval = ERROR_BAD_FORMAT;
Duane Clark's avatar
Duane Clark committed
362 363
    }

364
    TRACE("returning %lu\n", retval);
365

366
    psei_out->hInstApp = (HINSTANCE)retval;
367
    if( gcdret )
368 369 370
        if( !SetCurrentDirectoryW( curdir))
            ERR("cannot return to directory %s\n", debugstr_w(curdir));

Duane Clark's avatar
Duane Clark committed
371
    return retval;
372 373
}

374 375

/***********************************************************************
376
 *           SHELL_BuildEnvW	[Internal]
377 378 379 380
 *
 * Build the environment for the new process, adding the specified
 * path to the PATH variable. Returned pointer must be freed by caller.
 */
381
static void *SHELL_BuildEnvW( const WCHAR *path )
382
{
383 384 385 386
    static const WCHAR wPath[] = {'P','A','T','H','=',0};
    WCHAR *strings, *new_env;
    WCHAR *p, *p2;
    int total = strlenW(path) + 1;
387 388
    BOOL got_path = FALSE;

389
    if (!(strings = GetEnvironmentStringsW())) return NULL;
390 391 392
    p = strings;
    while (*p)
    {
393 394
        int len = strlenW(p) + 1;
        if (!strncmpiW( p, wPath, 5 )) got_path = TRUE;
395 396 397 398 399 400
        total += len;
        p += len;
    }
    if (!got_path) total += 5;  /* we need to create PATH */
    total++;  /* terminating null */

401
    if (!(new_env = HeapAlloc( GetProcessHeap(), 0, total * sizeof(WCHAR) )))
402
    {
403
        FreeEnvironmentStringsW( strings );
404 405 406 407 408 409
        return NULL;
    }
    p = strings;
    p2 = new_env;
    while (*p)
    {
410 411 412
        int len = strlenW(p) + 1;
        memcpy( p2, p, len * sizeof(WCHAR) );
        if (!strncmpiW( p, wPath, 5 ))
413 414
        {
            p2[len - 1] = ';';
415 416
            strcpyW( p2 + len, path );
            p2 += strlenW(path) + 1;
417 418 419 420 421 422
        }
        p += len;
        p2 += len;
    }
    if (!got_path)
    {
423 424 425
        strcpyW( p2, wPath );
        strcatW( p2, path );
        p2 += strlenW(p2) + 1;
426 427
    }
    *p2 = 0;
428
    FreeEnvironmentStringsW( strings );
429 430 431 432
    return new_env;
}


433
/***********************************************************************
434
 *           SHELL_TryAppPathW	[Internal]
435 436 437 438 439 440
 *
 * Helper function for SHELL_FindExecutable
 * @param lpResult - pointer to a buffer of size MAX_PATH
 * On entry: szName is a filename (probably without path separators).
 * On exit: if szName found in "App Path", place full path in lpResult, and return true
 */
441
static BOOL SHELL_TryAppPathW( LPCWSTR szName, LPWSTR lpResult, WCHAR **env)
442
{
443 444 445
    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',
	'\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','A','p','p',' ','P','a','t','h','s','\\',0};
    static const WCHAR wPath[] = {'P','a','t','h',0};
446
    HKEY hkApp = 0;
447
    WCHAR buffer[1024];
448
    LONG len;
449
    LONG res;
450 451
    BOOL found = FALSE;

452
    if (env) *env = NULL;
453 454 455
    strcpyW(buffer, wszKeyAppPaths);
    strcatW(buffer, szName);
    res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp);
456
    if (res) goto end;
457

458
    len = MAX_PATH*sizeof(WCHAR);
459
    res = RegQueryValueW(hkApp, NULL, lpResult, &len);
460
    if (res) goto end;
461 462
    found = TRUE;

463 464 465
    if (env)
    {
        DWORD count = sizeof(buffer);
466 467
        if (!RegQueryValueExW(hkApp, wPath, NULL, NULL, (LPBYTE)buffer, &count) && buffer[0])
            *env = SHELL_BuildEnvW( buffer );
468 469
    }

470 471 472 473 474
end:
    if (hkApp) RegCloseKey(hkApp);
    return found;
}

475
/*************************************************************************
476
 *	SHELL_FindExecutableByVerb [Internal]
477 478 479
 *
 * called from SHELL_FindExecutable or SHELL_execute_class
 * in/out:
480
 *      classname a buffer, big enough, to get the key name to do actually the
481 482 483
 *              command   "WordPad.Document.1\\shell\\open\\command"
 *              passed as "WordPad.Document.1"
 * in:
484
 *      lpVerb the operation on it (open)
485 486 487 488 489 490 491 492
 *      commandlen the size of command buffer (in bytes)
 * out:
 *      command a buffer, to store the command to do the
 *              operation on the file
 *      key a buffer, big enough, to get the key name to do actually the
 *              command "WordPad.Document.1\\shell\\open\\command"
 *              Can be NULL
 */
493
static UINT SHELL_FindExecutableByVerb(LPCWSTR lpVerb, LPWSTR key, LPWSTR classname, LPWSTR command, LONG commandlen)
494 495
{
    static const WCHAR wCommand[] = {'\\','c','o','m','m','a','n','d',0};
496 497 498
    HKEY hkeyClass;
    WCHAR verb[MAX_PATH];

499
    if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, 0x02000000, &hkeyClass))
500
        return SE_ERR_NOASSOC;
501
    if (!HCR_GetDefaultVerbW(hkeyClass, lpVerb, verb, sizeof(verb)/sizeof(verb[0])))
502
        return SE_ERR_NOASSOC;
503
    RegCloseKey(hkeyClass);
504 505

    /* Looking for ...buffer\shell\<verb>\command */
506 507 508
    strcatW(classname, wszShell);
    strcatW(classname, verb);
    strcatW(classname, wCommand);
509

510
    if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, command,
511 512 513
                       &commandlen) == ERROR_SUCCESS)
    {
	commandlen /= sizeof(WCHAR);
514
        if (key) strcpyW(key, classname);
515 516 517 518 519 520 521 522 523 524 525 526 527
#if 0
        LPWSTR tmp;
        WCHAR param[256];
	LONG paramlen = sizeof(param);
        static const WCHAR wSpace[] = {' ',0};

        /* FIXME: it seems all Windows version don't behave the same here.
         * the doc states that this ddeexec information can be found after
         * the exec names.
         * on Win98, it doesn't appear, but I think it does on Win2k
         */
	/* Get the parameters needed by the application
	   from the associated ddeexec key */
528
	tmp = strstrW(classname, wCommand);
529
	tmp[0] = '\0';
530 531
	strcatW(classname, wDdeexec);
	if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, param,
532 533 534 535 536 537 538 539 540 541 542 543 544 545
				     &paramlen) == ERROR_SUCCESS)
	{
	    paramlen /= sizeof(WCHAR);
            strcatW(command, wSpace);
            strcatW(command, param);
            commandlen += paramlen;
	}
#endif

	command[commandlen] = '\0';

	return 33; /* FIXME see SHELL_FindExecutable() */
    }

546
    return SE_ERR_NOASSOC;
547 548
}

549 550 551 552
/*************************************************************************
 *	SHELL_FindExecutable [Internal]
 *
 * Utility for code sharing between FindExecutable and ShellExecute
553
 * in:
554
 *      lpFile the name of a file
555
 *      lpVerb the operation on it (open)
556 557 558 559 560 561 562
 * out:
 *      lpResult a buffer, big enough :-(, to store the command to do the
 *              operation on the file
 *      key a buffer, big enough, to get the key name to do actually the
 *              command (it'll be used afterwards for more information
 *              on the operation)
 */
563
static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb,
564
                                 LPWSTR lpResult, int resultLen, LPWSTR key, WCHAR **env, LPITEMIDLIST pidl, LPCWSTR args)
565
{
566 567 568 569
    static const WCHAR wWindows[] = {'w','i','n','d','o','w','s',0};
    static const WCHAR wPrograms[] = {'p','r','o','g','r','a','m','s',0};
    static const WCHAR wExtensions[] = {'e','x','e',' ','p','i','f',' ','b','a','t',' ','c','m','d',' ','c','o','m',0};
    WCHAR *extension = NULL; /* pointer to file extension */
570 571
    WCHAR classname[256];     /* registry name for this file type */
    LONG  classnamelen = sizeof(classname); /* length of above */
572
    WCHAR command[1024];     /* command from registry */
573
    WCHAR wBuffer[256];      /* Used to GetProfileString */
574
    UINT  retval = SE_ERR_NOASSOC;
575
    WCHAR *tok;              /* token pointer */
576
    WCHAR xlpFile[256];      /* result of SearchPath */
577
    DWORD attribs;           /* file attributes */
578

579
    TRACE("%s\n", debugstr_w(lpFile));
580

581 582 583
    if (!lpResult)
        return ERROR_INVALID_PARAMETER;

584
    xlpFile[0] = '\0';
585
    lpResult[0] = '\0'; /* Start off with an empty return string */
586
    if (key) *key = '\0';
587

588
    /* trap NULL parameters on entry */
589
    if (!lpFile)
590
    {
591 592
        WARN("(lpFile=%s,lpResult=%s): NULL parameter\n",
             debugstr_w(lpFile), debugstr_w(lpResult));
593
        return ERROR_FILE_NOT_FOUND; /* File not found. Close enough, I guess. */
594
    }
595

596
    if (SHELL_TryAppPathW( lpFile, lpResult, env ))
597
    {
598
        TRACE("found %s via App Paths\n", debugstr_w(lpResult));
599 600 601
        return 33;
    }

602
    if (SearchPathW(lpPath, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
603
    {
604
        TRACE("SearchPathW returned non-zero\n");
605
        lpFile = xlpFile;
606 607 608 609 610 611 612
        /* The file was found in the application-supplied default directory (or the system search path) */
    }
    else if (lpPath && SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
    {
        TRACE("SearchPathW returned non-zero\n");
        lpFile = xlpFile;
        /* The file was found in one of the directories in the system-wide search path */
613
    }
614

615 616
    attribs = GetFileAttributesW(lpFile);
    if (attribs!=INVALID_FILE_ATTRIBUTES && (attribs&FILE_ATTRIBUTE_DIRECTORY))
617
    {
618
       strcpyW(classname, wszFolder);
619
    }
620
    else
621
    {
622 623 624 625 626 627
        /* Did we get something? Anything? */
        if (xlpFile[0]==0)
        {
            TRACE("Returning SE_ERR_FNF\n");
            return SE_ERR_FNF;
        }
628 629 630 631 632 633 634
        /* First thing we need is the file's extension */
        extension = strrchrW(xlpFile, '.'); /* Assume last "." is the one; */
        /* File->Run in progman uses */
        /* .\FILE.EXE :( */
        TRACE("xlpFile=%s,extension=%s\n", debugstr_w(xlpFile), debugstr_w(extension));

        if (extension == NULL || extension[1]==0)
635
        {
636 637
            WARN("Returning SE_ERR_NOASSOC\n");
            return SE_ERR_NOASSOC;
638
        }
639

640 641
        /* Three places to check: */
        /* 1. win.ini, [windows], programs (NB no leading '.') */
642
        /* 2. Registry, HKEY_CLASS_ROOT\<classname>\shell\open\command */
643 644 645 646 647 648 649 650 651 652 653 654 655
        /* 3. win.ini, [extensions], extension (NB no leading '.' */
        /* All I know of the order is that registry is checked before */
        /* extensions; however, it'd make sense to check the programs */
        /* section first, so that's what happens here. */

        /* See if it's a program - if GetProfileString fails, we skip this
         * section. Actually, if GetProfileString fails, we've probably
         * got a lot more to worry about than running a program... */
        if (GetProfileStringW(wWindows, wPrograms, wExtensions, wBuffer, sizeof(wBuffer)/sizeof(WCHAR)) > 0)
        {
            CharLowerW(wBuffer);
            tok = wBuffer;
            while (*tok)
656
            {
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
                WCHAR *p = tok;
                while (*p && *p != ' ' && *p != '\t') p++;
                if (*p)
                {
                    *p++ = 0;
                    while (*p == ' ' || *p == '\t') p++;
                }

                if (strcmpiW(tok, &extension[1]) == 0) /* have to skip the leading "." */
                {
                    strcpyW(lpResult, xlpFile);
                    /* Need to perhaps check that the file has a path
                     * attached */
                    TRACE("found %s\n", debugstr_w(lpResult));
                    return 33;
672
                    /* Greater than 32 to indicate success */
673 674
                }
                tok = p;
675 676
            }
        }
677

678
        /* Check registry */
679 680
        if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, classname,
                           &classnamelen) == ERROR_SUCCESS)
681
        {
682 683 684 685 686
            classnamelen /= sizeof(WCHAR);
	    if (classnamelen == sizeof(classname)/sizeof(WCHAR))
		classnamelen--;
            classname[classnamelen] = '\0';
            TRACE("File type: %s\n", debugstr_w(classname));
687
        }
688 689
        else
        {
690
            *classname = '\0';
691
        }
692
    }
693

694
    if (*classname)
695
    {
696 697
        /* pass the verb string to SHELL_FindExecutableByVerb() */
        retval = SHELL_FindExecutableByVerb(lpVerb, key, classname, command, sizeof(command));
698 699

	if (retval > 32)
700
	{
701 702 703 704
	    DWORD finishedLen;
	    SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen);
	    if (finishedLen > resultLen)
		ERR("Argify buffer not large enough.. truncated\n");
705 706 707 708 709 710 711 712 713 714 715 716

	    /* Remove double quotation marks and command line arguments */
	    if (*lpResult == '"')
	    {
		WCHAR *p = lpResult;
		while (*(p + 1) != '"')
		{
		    *p = *(p + 1);
		    p++;
		}
		*p = '\0';
	    }
717 718
            else
            {
719
                /* Truncate on first space */
720 721 722 723 724
		WCHAR *p = lpResult;
		while (*p != ' ' && *p != '\0')
                    p++;
                *p='\0';
            }
725 726 727 728
	}
    }
    else /* Check win.ini */
    {
729 730
	static const WCHAR wExtensions[] = {'e','x','t','e','n','s','i','o','n','s',0};

731 732
	/* Toss the leading dot */
	extension++;
733
	if (GetProfileStringW(wExtensions, extension, wszEmpty, command, sizeof(command)/sizeof(WCHAR)) > 0)
734
        {
735
            if (strlenW(command) != 0)
736
            {
737 738
                strcpyW(lpResult, command);
                tok = strchrW(lpResult, '^'); /* should be ^.extension? */
739 740 741
                if (tok != NULL)
                {
                    tok[0] = '\0';
742 743 744
                    strcatW(lpResult, xlpFile); /* what if no dir in xlpFile? */
                    tok = strchrW(command, '^'); /* see above */
                    if ((tok != NULL) && (strlenW(tok)>5))
745
                    {
746
                        strcatW(lpResult, &tok[5]);
747 748 749 750 751 752
                    }
                }
                retval = 33; /* FIXME - see above */
            }
        }
    }
753

754
    TRACE("returning %s\n", debugstr_w(lpResult));
755 756 757 758 759 760
    return retval;
}

/******************************************************************
 *		dde_cb
 *
761
 * callback for the DDE connection. not really useful
762
 */
763
static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv,
764 765
                                HSZ hsz1, HSZ hsz2, HDDEDATA hData,
                                ULONG_PTR dwData1, ULONG_PTR dwData2)
766
{
767
    TRACE("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
768
           uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
769
    return NULL;
770 771 772 773 774 775 776
}

/******************************************************************
 *		dde_connect
 *
 * ShellExecute helper. Used to do an operation with a DDE connection
 *
777
 * Handles both the direct connection (try #1), and if it fails,
778 779 780
 * launching an application and trying (#2) to connect to it
 *
 */
781
static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec,
782
                            const WCHAR* lpFile, WCHAR *env,
783
			    LPCWSTR szCommandline, LPITEMIDLIST pidl, SHELL_ExecuteW32 execfunc,
784
                            const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out)
785
{
786 787
    static const WCHAR wApplication[] = {'\\','a','p','p','l','i','c','a','t','i','o','n',0};
    static const WCHAR wTopic[] = {'\\','t','o','p','i','c',0};
788 789
    WCHAR       regkey[256];
    WCHAR *     endkey = regkey + strlenW(key);
790 791 792
    WCHAR       app[256], topic[256], ifexec[256], static_res[256];
    WCHAR *     dynamic_res=NULL;
    WCHAR *     res;
793
    LONG        applen, topiclen, ifexeclen;
794
    WCHAR *     exec;
795 796
    DWORD       ddeInst = 0;
    DWORD       tid;
797
    DWORD       resultLen, endkeyLen;
798 799
    HSZ         hszApp, hszTopic;
    HCONV       hConv;
800
    HDDEDATA    hDdeData;
801
    unsigned    ret = SE_ERR_NOASSOC;
802
    BOOL unicode = !(GetVersion() & 0x80000000);
803

804 805 806 807 808
    if (strlenW(key) + 1 > sizeof(regkey) / sizeof(regkey[0]))
    {
        FIXME("input parameter %s larger than buffer\n", debugstr_w(key));
        return 2;
    }
809
    strcpyW(regkey, key);
810 811 812 813 814 815
    endkeyLen = sizeof(regkey) / sizeof(regkey[0]) - (endkey - regkey);
    if (strlenW(wApplication) + 1 > endkeyLen)
    {
        FIXME("endkey %s overruns buffer\n", debugstr_w(wApplication));
        return 2;
    }
816
    strcpyW(endkey, wApplication);
817
    applen = sizeof(app);
818
    if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, app, &applen) != ERROR_SUCCESS)
819
    {
820 821 822 823 824 825 826 827 828
        WCHAR command[1024], fullpath[MAX_PATH];
        static const WCHAR wSo[] = { '.','s','o',0 };
        int sizeSo = sizeof(wSo)/sizeof(WCHAR);
        LPWSTR ptr = NULL;
        DWORD ret = 0;

        /* Get application command from start string and find filename of application */
        if (*start == '"')
        {
829 830 831 832 833 834
            if (strlenW(start + 1) + 1 > sizeof(command) / sizeof(command[0]))
            {
                FIXME("size of input parameter %s larger than buffer\n",
                      debugstr_w(start + 1));
                return 2;
            }
835 836 837 838 839 840 841
            strcpyW(command, start+1);
            if ((ptr = strchrW(command, '"')))
                *ptr = 0;
            ret = SearchPathW(NULL, command, wszExe, sizeof(fullpath)/sizeof(WCHAR), fullpath, &ptr);
        }
        else
        {
842 843 844
            LPCWSTR p;
            LPWSTR space;
            for (p=start; (space=strchrW(p, ' ')); p=space+1)
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
            {
                int idx = space-start;
                memcpy(command, start, idx*sizeof(WCHAR));
                command[idx] = '\0';
                if ((ret = SearchPathW(NULL, command, wszExe, sizeof(fullpath)/sizeof(WCHAR), fullpath, &ptr)))
                    break;
            }
            if (!ret)
                ret = SearchPathW(NULL, start, wszExe, sizeof(fullpath)/sizeof(WCHAR), fullpath, &ptr);
        }

        if (!ret)
        {
            ERR("Unable to find application path for command %s\n", debugstr_w(start));
            return ERROR_ACCESS_DENIED;
        }
861 862 863 864 865
        if (strlenW(ptr) + 1 > sizeof(app) / sizeof(app[0]))
        {
            FIXME("size of found path %s larger than buffer\n", debugstr_w(ptr));
            return 2;
        }
866 867 868 869 870 871 872 873 874 875 876
        strcpyW(app, ptr);

        /* Remove extensions (including .so) */
        ptr = app + strlenW(app) - (sizeSo-1);
        if (strlenW(app) >= sizeSo &&
            !strcmpW(ptr, wSo))
            *ptr = 0;

        ptr = strrchrW(app, '.');
        assert(ptr);
        *ptr = 0;
877
    }
878

879 880 881 882 883
    if (strlenW(wTopic) + 1 > endkeyLen)
    {
        FIXME("endkey %s overruns buffer\n", debugstr_w(wTopic));
        return 2;
    }
884
    strcpyW(endkey, wTopic);
885
    topiclen = sizeof(topic);
886
    if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, topic, &topiclen) != ERROR_SUCCESS)
887
    {
888 889
        static const WCHAR wSystem[] = {'S','y','s','t','e','m',0};
        strcpyW(topic, wSystem);
890
    }
891

892
    if (unicode)
893
    {
894 895 896 897 898 899 900
        if (DdeInitializeW(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR)
            return 2;
    }
    else
    {
        if (DdeInitializeA(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR)
            return 2;
901
    }
902

903 904
    hszApp = DdeCreateStringHandleW(ddeInst, app, CP_WINUNICODE);
    hszTopic = DdeCreateStringHandleW(ddeInst, topic, CP_WINUNICODE);
905

906 907 908 909
    hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
    exec = ddeexec;
    if (!hConv)
    {
910
        static const WCHAR wIfexec[] = {'\\','i','f','e','x','e','c',0};
911
        TRACE("Launching %s\n", debugstr_w(start));
912
        ret = execfunc(start, env, TRUE, psei, psei_out);
913
        if (ret <= 32)
914 915 916 917 918 919 920
        {
            TRACE("Couldn't launch\n");
            goto error;
        }
        hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
        if (!hConv)
        {
921
            TRACE("Couldn't connect. ret=%d\n", ret);
922 923 924
            DdeUninitialize(ddeInst);
            SetLastError(ERROR_DDE_FAIL);
            return 30; /* whatever */
925
        }
926 927 928 929 930
        if (strlenW(wIfexec) + 1 > endkeyLen)
        {
            FIXME("endkey %s overruns buffer\n", debugstr_w(wIfexec));
            return 2;
        }
931
        strcpyW(endkey, wIfexec);
932
        ifexeclen = sizeof(ifexec);
933
        if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, ifexec, &ifexeclen) == ERROR_SUCCESS)
934 935 936 937
        {
            exec = ifexec;
        }
    }
938

939 940 941 942 943 944 945 946
    SHELL_ArgifyW(static_res, sizeof(static_res)/sizeof(WCHAR), exec, lpFile, pidl, szCommandline, &resultLen);
    if (resultLen > sizeof(static_res)/sizeof(WCHAR))
    {
        res = dynamic_res = HeapAlloc(GetProcessHeap(), 0, resultLen * sizeof(WCHAR));
        SHELL_ArgifyW(dynamic_res, resultLen, exec, lpFile, pidl, szCommandline, NULL);
    }
    else
        res = static_res;
947
    TRACE("%s %s => %s\n", debugstr_w(exec), debugstr_w(lpFile), debugstr_w(res));
948

949 950 951
    /* It's documented in the KB 330337 that IE has a bug and returns
     * error DMLERR_NOTPROCESSED on XTYP_EXECUTE request.
     */
952 953
    if (unicode)
        hDdeData = DdeClientTransaction((LPBYTE)res, (strlenW(res) + 1) * sizeof(WCHAR), hConv, 0L, 0,
954
                                         XTYP_EXECUTE, 30000, &tid);
955 956 957 958 959 960 961 962 963
    else
    {
        DWORD lenA = WideCharToMultiByte(CP_ACP, 0, res, -1, NULL, 0, NULL, NULL);
        char *resA = HeapAlloc(GetProcessHeap(), 0, lenA);
        WideCharToMultiByte(CP_ACP, 0, res, -1, resA, lenA, NULL, NULL);
        hDdeData = DdeClientTransaction( (LPBYTE)resA, lenA, hConv, 0L, 0,
                                         XTYP_EXECUTE, 10000, &tid );
        HeapFree(GetProcessHeap(), 0, resA);
    }
964 965 966 967 968 969
    if (hDdeData)
        DdeFreeDataHandle(hDdeData);
    else
        WARN("DdeClientTransaction failed with error %04x\n", DdeGetLastError(ddeInst));
    ret = 33;

970 971
    HeapFree(GetProcessHeap(), 0, dynamic_res);

972
    DdeDisconnect(hConv);
973

974 975
 error:
    DdeUninitialize(ddeInst);
976

977 978 979
    return ret;
}

Duane Clark's avatar
Duane Clark committed
980 981 982
/*************************************************************************
 *	execute_from_key [Internal]
 */
983
static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWSTR szCommandline,
984
                             LPCWSTR executable_name,
985
			     SHELL_ExecuteW32 execfunc,
986
                             LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out)
987
{
988 989 990 991
    static const WCHAR wCommand[] = {'c','o','m','m','a','n','d',0};
    static const WCHAR wDdeexec[] = {'d','d','e','e','x','e','c',0};
    WCHAR cmd[256], param[1024], ddeexec[256];
    LONG cmdlen = sizeof(cmd), ddeexeclen = sizeof(ddeexec);
992
    UINT_PTR retval = SE_ERR_NOASSOC;
993 994
    DWORD resultLen;
    LPWSTR tmp;
995

996 997 998
    TRACE("%s %s %s %s %s\n", debugstr_w(key), debugstr_w(lpFile), debugstr_w(env),
           debugstr_w(szCommandline), debugstr_w(executable_name));

999
    cmd[0] = '\0';
1000
    param[0] = '\0';
1001

1002
    /* Get the application from the registry */
1003
    if (RegQueryValueW(HKEY_CLASSES_ROOT, key, cmd, &cmdlen) == ERROR_SUCCESS)
1004 1005 1006 1007 1008
    {
        TRACE("got cmd: %s\n", debugstr_w(cmd));

        /* Is there a replace() function anywhere? */
        cmdlen /= sizeof(WCHAR);
1009 1010
	if (cmdlen >= sizeof(cmd)/sizeof(WCHAR))
	    cmdlen = sizeof(cmd)/sizeof(WCHAR)-1;
1011
        cmd[cmdlen] = '\0';
1012
        SHELL_ArgifyW(param, sizeof(param)/sizeof(WCHAR), cmd, lpFile, psei->lpIDList, szCommandline, &resultLen);
1013 1014
        if (resultLen > sizeof(param)/sizeof(WCHAR))
            ERR("Argify buffer not large enough, truncating\n");
1015
    }
1016

1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
    /* Get the parameters needed by the application
       from the associated ddeexec key */
    tmp = strstrW(key, wCommand);
    assert(tmp);
    strcpyW(tmp, wDdeexec);

    if (RegQueryValueW(HKEY_CLASSES_ROOT, key, ddeexec, &ddeexeclen) == ERROR_SUCCESS)
    {
        TRACE("Got ddeexec %s => %s\n", debugstr_w(key), debugstr_w(ddeexec));
        if (!param[0]) strcpyW(param, executable_name);
        retval = dde_connect(key, param, ddeexec, lpFile, env, szCommandline, psei->lpIDList, execfunc, psei, psei_out);
    }
    else if (param[0])
    {
1031
        TRACE("executing: %s\n", debugstr_w(param));
1032 1033 1034
        retval = execfunc(param, env, FALSE, psei, psei_out);
    }
    else
1035
        WARN("Nothing appropriate found for %s\n", debugstr_w(key));
1036 1037 1038 1039 1040 1041 1042 1043

    return retval;
}

/*************************************************************************
 * FindExecutableA			[SHELL32.@]
 */
HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
{
    HINSTANCE retval;
    WCHAR *wFile = NULL, *wDirectory = NULL;
    WCHAR wResult[MAX_PATH];

    if (lpFile) __SHCloneStrAtoW(&wFile, lpFile);
    if (lpDirectory) __SHCloneStrAtoW(&wDirectory, lpDirectory);

    retval = FindExecutableW(wFile, wDirectory, wResult);
    WideCharToMultiByte(CP_ACP, 0, wResult, -1, lpResult, MAX_PATH, NULL, NULL);
1054 1055
    SHFree( wFile );
    SHFree( wDirectory );
1056 1057

    TRACE("returning %s\n", lpResult);
Kevin Koltzau's avatar
Kevin Koltzau committed
1058
    return retval;
1059 1060 1061 1062
}

/*************************************************************************
 * FindExecutableW			[SHELL32.@]
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
 *
 * This function returns the executable associated with the specified file
 * for the default verb.
 *
 * PARAMS
 *  lpFile   [I] The file to find the association for. This must refer to
 *               an existing file otherwise FindExecutable fails and returns
 *               SE_ERR_FNF.
 *  lpResult [O] Points to a buffer into which the executable path is
 *               copied. This parameter must not be NULL otherwise
 *               FindExecutable() segfaults. The buffer must be of size at
 *               least MAX_PATH characters.
 *
 * RETURNS
 *  A value greater than 32 on success, less than or equal to 32 otherwise.
 *  See the SE_ERR_* constants.
 *
 * NOTES
 *  On Windows XP and 2003, FindExecutable() seems to first convert the
 *  filename into 8.3 format, thus taking into account only the first three
 *  characters of the extension, and expects to find an association for those.
 *  However other Windows versions behave sanely.
1085 1086
 */
HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult)
1087
{
1088
    UINT_PTR retval = SE_ERR_NOASSOC;
1089
    WCHAR old_dir[1024];
1090

1091
    TRACE("File %s, Dir %s\n", debugstr_w(lpFile), debugstr_w(lpDirectory));
1092 1093

    lpResult[0] = '\0'; /* Start off with an empty return string */
1094 1095
    if (lpFile == NULL)
	return (HINSTANCE)SE_ERR_FNF;
1096 1097 1098

    if (lpDirectory)
    {
1099 1100
        GetCurrentDirectoryW(sizeof(old_dir)/sizeof(WCHAR), old_dir);
        SetCurrentDirectoryW(lpDirectory);
1101
    }
1102

1103
    retval = SHELL_FindExecutable(lpDirectory, lpFile, wszOpen, lpResult, MAX_PATH, NULL, NULL, NULL, NULL);
1104

1105
    TRACE("returning %s\n", debugstr_w(lpResult));
1106
    if (lpDirectory)
1107
        SetCurrentDirectoryW(old_dir);
1108
    return (HINSTANCE)retval;
1109 1110
}

1111
/* FIXME: is this already implemented somewhere else? */
1112
static HKEY ShellExecute_GetClassKey( const SHELLEXECUTEINFOW *sei )
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
{
    LPCWSTR ext = NULL, lpClass = NULL;
    LPWSTR cls = NULL;
    DWORD type = 0, sz = 0;
    HKEY hkey = 0;
    LONG r;

    if (sei->fMask & SEE_MASK_CLASSALL)
        return sei->hkeyClass;
 
    if (sei->fMask & SEE_MASK_CLASSNAME)
        lpClass = sei->lpClass;
    else
    {
        ext = PathFindExtensionW( sei->lpFile );
        TRACE("ext = %s\n", debugstr_w( ext ) );
        if (!ext)
            return hkey;

        r = RegOpenKeyW( HKEY_CLASSES_ROOT, ext, &hkey );
        if (r != ERROR_SUCCESS )
            return hkey;

        r = RegQueryValueExW( hkey, NULL, 0, &type, NULL, &sz );
        if ( r == ERROR_SUCCESS && type == REG_SZ )
        {
            sz += sizeof (WCHAR);
            cls = HeapAlloc( GetProcessHeap(), 0, sz );
            cls[0] = 0;
            RegQueryValueExW( hkey, NULL, 0, &type, (LPBYTE) cls, &sz );
        }

        RegCloseKey( hkey );
        lpClass = cls;
    }

    TRACE("class = %s\n", debugstr_w(lpClass) );

    hkey = 0;
    if ( lpClass )
        RegOpenKeyW( HKEY_CLASSES_ROOT, lpClass, &hkey );

    HeapFree( GetProcessHeap(), 0, cls );

    return hkey;
}

static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei )
{
    LPCITEMIDLIST pidllast = NULL;
    IDataObject *dataobj = NULL;
    IShellFolder *shf = NULL;
    LPITEMIDLIST pidl = NULL;
    HRESULT r;

    if (sei->fMask & SEE_MASK_CLASSALL)
        pidl = sei->lpIDList;
    else
    {
        WCHAR fullpath[MAX_PATH];
1173
        BOOL ret;
1174 1175

        fullpath[0] = 0;
1176 1177
        ret = GetFullPathNameW( sei->lpFile, MAX_PATH, fullpath, NULL );
        if (!ret)
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
            goto end;

        pidl = ILCreateFromPathW( fullpath );
    }

    r = SHBindToParent( pidl, &IID_IShellFolder, (LPVOID*)&shf, &pidllast );
    if ( FAILED( r ) )
        goto end;

    IShellFolder_GetUIObjectOf( shf, NULL, 1, &pidllast,
                                &IID_IDataObject, NULL, (LPVOID*) &dataobj );

end:
    if ( pidl != sei->lpIDList )
        ILFree( pidl );
    if ( shf )
        IShellFolder_Release( shf );
    return dataobj;
}

static HRESULT shellex_run_context_menu_default( IShellExtInit *obj,
                                                 LPSHELLEXECUTEINFOW sei )
{
    IContextMenu *cm = NULL;
    CMINVOKECOMMANDINFOEX ici;
    MENUITEMINFOW info;
    WCHAR string[0x80];
    INT i, n, def = -1;
    HMENU hmenu = 0;
    HRESULT r;

    TRACE("%p %p\n", obj, sei );

    r = IShellExtInit_QueryInterface( obj, &IID_IContextMenu, (LPVOID*) &cm );
    if ( FAILED( r ) )
        return r;

    hmenu = CreateMenu();
    if ( !hmenu )
        goto end;

    /* the number of the last menu added is returned in r */
    r = IContextMenu_QueryContextMenu( cm, hmenu, 0, 0x20, 0x7fff, CMF_DEFAULTONLY );
    if ( FAILED( r ) )
        goto end;

    n = GetMenuItemCount( hmenu );
    for ( i = 0; i < n; i++ )
    {
        memset( &info, 0, sizeof info );
        info.cbSize = sizeof info;
        info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
        info.dwTypeData = string;
        info.cch = sizeof string;
        string[0] = 0;
        GetMenuItemInfoW( hmenu, i, TRUE, &info );

        TRACE("menu %d %s %08x %08lx %08x %08x\n", i, debugstr_w(string),
            info.fState, info.dwItemData, info.fType, info.wID );
        if ( ( !sei->lpVerb && (info.fState & MFS_DEFAULT) ) ||
             ( sei->lpVerb && !lstrcmpiW( sei->lpVerb, string ) ) )
        {
            def = i;
            break;
        }
    }

    r = E_FAIL;
    if ( def == -1 )
        goto end;

    memset( &ici, 0, sizeof ici );
    ici.cbSize = sizeof ici;
1251
    ici.fMask = CMIC_MASK_UNICODE | (sei->fMask & (SEE_MASK_NOASYNC|SEE_MASK_ASYNCOK|SEE_MASK_FLAG_NO_UI));
1252 1253 1254 1255 1256 1257 1258
    ici.nShow = sei->nShow;
    ici.lpVerb = MAKEINTRESOURCEA( def );
    ici.hwnd = sei->hwnd;
    ici.lpParametersW = sei->lpParameters;
    
    r = IContextMenu_InvokeCommand( cm, (LPCMINVOKECOMMANDINFO) &ici );

1259
    TRACE("invoke command returned %08x\n", r );
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285

end:
    if ( hmenu )
        DestroyMenu( hmenu );
    if ( cm )
        IContextMenu_Release( cm );
    return r;
}

static HRESULT shellex_load_object_and_run( HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei )
{
    IDataObject *dataobj = NULL;
    IObjectWithSite *ows = NULL;
    IShellExtInit *obj = NULL;
    HRESULT r;

    TRACE("%p %s %p\n", hkey, debugstr_guid( guid ), sei );

    r = CoInitialize( NULL );
    if ( FAILED( r ) )
        goto end;

    r = CoCreateInstance( guid, NULL, CLSCTX_INPROC_SERVER,
                           &IID_IShellExtInit, (LPVOID*)&obj );
    if ( FAILED( r ) )
    {
1286
        ERR("failed %08x\n", r );
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346
        goto end;
    }

    dataobj = shellex_get_dataobj( sei );
    if ( !dataobj )
    {
        ERR("failed to get data object\n");
        goto end;
    }

    r = IShellExtInit_Initialize( obj, NULL, dataobj, hkey );
    if ( FAILED( r ) )
        goto end;

    r = IShellExtInit_QueryInterface( obj, &IID_IObjectWithSite, (LPVOID*) &ows );
    if ( FAILED( r ) )
        goto end;

    IObjectWithSite_SetSite( ows, NULL );

    r = shellex_run_context_menu_default( obj, sei );

end:
    if ( ows )
        IObjectWithSite_Release( ows );
    if ( dataobj )
        IDataObject_Release( dataobj );
    if ( obj )
        IShellExtInit_Release( obj );
    CoUninitialize();
    return r;
}


/*************************************************************************
 *	ShellExecute_FromContextMenu [Internal]
 */
static LONG ShellExecute_FromContextMenu( LPSHELLEXECUTEINFOW sei )
{
    static const WCHAR szcm[] = { 's','h','e','l','l','e','x','\\',
        'C','o','n','t','e','x','t','M','e','n','u','H','a','n','d','l','e','r','s',0 };
    HKEY hkey, hkeycm = 0;
    WCHAR szguid[39];
    HRESULT hr;
    GUID guid;
    DWORD i;
    LONG r;

    TRACE("%s\n", debugstr_w(sei->lpFile) );

    hkey = ShellExecute_GetClassKey( sei );
    if ( !hkey )
        return ERROR_FUNCTION_FAILED;

    r = RegOpenKeyW( hkey, szcm, &hkeycm );
    if ( r == ERROR_SUCCESS )
    {
        i = 0;
        while ( 1 )
        {
1347
            r = RegEnumKeyW( hkeycm, i++, szguid, sizeof(szguid)/sizeof(szguid[0]) );
1348 1349
            if ( r != ERROR_SUCCESS )
                break;
1350

1351
            hr = CLSIDFromString( szguid, &guid );
1352 1353 1354 1355 1356 1357 1358
            if (SUCCEEDED(hr))
            {
                /* stop at the first one that succeeds in running */
                hr = shellex_load_object_and_run( hkey, &guid, sei );
                if ( SUCCEEDED( hr ) )
                    break;
            }
1359 1360 1361 1362 1363 1364 1365 1366 1367
        }
        RegCloseKey( hkeycm );
    }

    if ( hkey != sei->hkeyClass )
        RegCloseKey( hkey );
    return r;
}

1368 1369
static UINT_PTR SHELL_quote_and_execute( LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR lpstrProtocol, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc );

1370 1371
static UINT_PTR SHELL_execute_class( LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc )
{
1372
    static const WCHAR wQuote[] = {'"',0};
1373
    static const WCHAR wSpace[] = {' ',0};
1374
    WCHAR execCmd[1024], classname[1024];
1375 1376 1377 1378 1379 1380
    /* launch a document by fileclass like 'WordPad.Document.1' */
    /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */
    /* FIXME: wcmd should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */
    ULONG cmask=(psei->fMask & SEE_MASK_CLASSALL);
    DWORD resultLen;
    BOOL done;
1381
    UINT_PTR rslt;
1382

1383 1384 1385 1386
  /* FIXME: remove following block when SHELL_quote_and_execute supports hkeyClass parameter */
  if (cmask != SEE_MASK_CLASSNAME)
  {
    WCHAR wcmd[1024];
1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
    HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? psei->hkeyClass : NULL,
                           (cmask == SEE_MASK_CLASSNAME) ? psei->lpClass: NULL,
                           psei->lpVerb,
                           execCmd, sizeof(execCmd));

    /* FIXME: get the extension of lpFile, check if it fits to the lpClass */
    TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(execCmd), debugstr_w(wszApplicationName));

    wcmd[0] = '\0';
    done = SHELL_ArgifyW(wcmd, sizeof(wcmd)/sizeof(WCHAR), execCmd, wszApplicationName, psei->lpIDList, NULL, &resultLen);
    if (!done && wszApplicationName[0])
    {
        strcatW(wcmd, wSpace);
1400 1401 1402 1403 1404 1405 1406 1407
        if (*wszApplicationName != '"')
        {
            strcatW(wcmd, wQuote);
            strcatW(wcmd, wszApplicationName);
            strcatW(wcmd, wQuote);
        }
        else
            strcatW(wcmd, wszApplicationName);
1408 1409 1410 1411
    }
    if (resultLen > sizeof(wcmd)/sizeof(WCHAR))
        ERR("Argify buffer not large enough... truncating\n");
    return execfunc(wcmd, NULL, FALSE, psei, psei_out);
1412 1413
  }

1414 1415
    strcpyW(classname, psei->lpClass);
    rslt = SHELL_FindExecutableByVerb(psei->lpVerb, NULL, classname, execCmd, sizeof(execCmd));
1416

1417
    TRACE("SHELL_FindExecutableByVerb returned %u (%s, %s)\n", (unsigned int)rslt, debugstr_w(classname), debugstr_w(execCmd));
1418 1419
    if (33 > rslt)
        return rslt;
1420
    rslt = SHELL_quote_and_execute( execCmd, wszEmpty, classname,
1421 1422 1423
                                      wszApplicationName, NULL, psei,
                                      psei_out, execfunc );
    return rslt;
1424 1425
}

1426 1427 1428 1429 1430 1431 1432
static BOOL SHELL_translate_idlist( LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen )
{
    static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0};
    WCHAR buffer[MAX_PATH];
    BOOL appKnownSingular = FALSE;

    /* last chance to translate IDList: now also allow CLSID paths */
1433
    if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW(sei->lpIDList, buffer, sizeof(buffer)/sizeof(WCHAR)))) {
1434 1435
        if (buffer[0]==':' && buffer[1]==':') {
            /* open shell folder for the specified class GUID */
1436
            if (strlenW(buffer) + 1 > parametersLen)
1437 1438 1439
                ERR("parameters len exceeds buffer size (%i > %i), truncating\n",
                    lstrlenW(buffer) + 1, parametersLen);
            lstrcpynW(wszParameters, buffer, parametersLen);
1440
            if (strlenW(wExplorer) > dwApplicationNameLen)
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471
                ERR("application len exceeds buffer size (%i > %i), truncating\n",
                    lstrlenW(wExplorer) + 1, dwApplicationNameLen);
            lstrcpynW(wszApplicationName, wExplorer, dwApplicationNameLen);
            appKnownSingular = TRUE;

            sei->fMask &= ~SEE_MASK_INVOKEIDLIST;
        } else {
            WCHAR target[MAX_PATH];
            DWORD attribs;
            DWORD resultLen;
            /* Check if we're executing a directory and if so use the
               handler for the Folder class */
            strcpyW(target, buffer);
            attribs = GetFileAttributesW(buffer);
            if (attribs != INVALID_FILE_ATTRIBUTES &&
                (attribs & FILE_ATTRIBUTE_DIRECTORY) &&
                HCR_GetExecuteCommandW(0, wszFolder,
                                       sei->lpVerb,
                                       buffer, sizeof(buffer))) {
                SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen,
                              buffer, target, sei->lpIDList, NULL, &resultLen);
                if (resultLen > dwApplicationNameLen)
                    ERR("Argify buffer not large enough... truncating\n");
                appKnownSingular = FALSE;
            }
            sei->fMask &= ~SEE_MASK_INVOKEIDLIST;
        }
    }
    return appKnownSingular;
}

1472
static UINT_PTR SHELL_quote_and_execute( LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR wszKeyname, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc )
1473 1474 1475 1476
{
    static const WCHAR wQuote[] = {'"',0};
    static const WCHAR wSpace[] = {' ',0};
    UINT_PTR retval;
1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487
    DWORD len;
    WCHAR *wszQuotedCmd;

    /* Length of quotes plus length of command plus NULL terminator */
    len = 2 + lstrlenW(wcmd) + 1;
    if (wszParameters[0])
    {
        /* Length of space plus length of parameters */
        len += 1 + lstrlenW(wszParameters);
    }
    wszQuotedCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1488 1489 1490 1491 1492 1493 1494 1495 1496 1497
    /* Must quote to handle case where cmd contains spaces,
     * else security hole if malicious user creates executable file "C:\\Program"
     */
    strcpyW(wszQuotedCmd, wQuote);
    strcatW(wszQuotedCmd, wcmd);
    strcatW(wszQuotedCmd, wQuote);
    if (wszParameters[0]) {
        strcatW(wszQuotedCmd, wSpace);
        strcatW(wszQuotedCmd, wszParameters);
    }
1498 1499 1500
    TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(wszKeyname));
    if (*wszKeyname)
        retval = execute_from_key(wszKeyname, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out);
1501 1502
    else
        retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out);
1503
    HeapFree(GetProcessHeap(), 0, wszQuotedCmd);
1504 1505 1506
    return retval;
}

1507
static UINT_PTR SHELL_execute_url( LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc )
1508 1509 1510
{
    static const WCHAR wShell[] = {'\\','s','h','e','l','l','\\',0};
    static const WCHAR wCommand[] = {'\\','c','o','m','m','a','n','d',0};
1511 1512
    UINT_PTR retval;
    WCHAR *lpstrProtocol;
1513 1514
    LPCWSTR lpstrRes;
    INT iSize;
1515
    DWORD len;
1516 1517 1518 1519 1520 1521 1522 1523

    lpstrRes = strchrW(lpFile, ':');
    if (lpstrRes)
        iSize = lpstrRes - lpFile;
    else
        iSize = strlenW(lpFile);

    TRACE("Got URL: %s\n", debugstr_w(lpFile));
1524
    /* Looking for ...<protocol>\shell\<lpVerb>\command */
1525
    len = iSize + lstrlenW(wShell) + lstrlenW(wCommand) + 1;
1526
    if (psei->lpVerb && *psei->lpVerb)
1527 1528 1529 1530
        len += lstrlenW(psei->lpVerb);
    else
        len += lstrlenW(wszOpen);
    lpstrProtocol = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1531 1532 1533
    memcpy(lpstrProtocol, lpFile, iSize*sizeof(WCHAR));
    lpstrProtocol[iSize] = '\0';
    strcatW(lpstrProtocol, wShell);
1534
    strcatW(lpstrProtocol, psei->lpVerb && *psei->lpVerb ? psei->lpVerb: wszOpen);
1535 1536
    strcatW(lpstrProtocol, wCommand);

1537 1538 1539 1540
    retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters,
                              wcmd, execfunc, psei, psei_out);
    HeapFree(GetProcessHeap(), 0, lpstrProtocol);
    return retval;
1541 1542
}

1543 1544 1545 1546 1547
static void do_error_dialog( UINT_PTR retval, HWND hwnd )
{
    WCHAR msg[2048];
    int error_code=GetLastError();

1548 1549 1550 1551
    if (retval == SE_ERR_NOASSOC)
        LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, sizeof(msg)/sizeof(WCHAR));
    else
        FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, 0, msg, sizeof(msg)/sizeof(WCHAR), NULL);
1552 1553 1554 1555

    MessageBoxW(hwnd, msg, NULL, MB_ICONERROR);
}

1556
/*************************************************************************
1557
 *	SHELL_execute [Internal]
1558
 */
1559
static BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc )
1560
{
1561 1562 1563
    static const WCHAR wSpace[] = {' ',0};
    static const WCHAR wWww[] = {'w','w','w',0};
    static const WCHAR wHttp[] = {'h','t','t','p',':','/','/',0};
1564 1565
    static const DWORD unsupportedFlags =
        SEE_MASK_INVOKEIDLIST  | SEE_MASK_ICON         | SEE_MASK_HOTKEY |
1566
        SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT |
1567
        SEE_MASK_UNICODE       | SEE_MASK_ASYNCOK      | SEE_MASK_HMONITOR;
1568

1569 1570
    WCHAR parametersBuffer[1024], dirBuffer[MAX_PATH], wcmdBuffer[1024];
    WCHAR *wszApplicationName, *wszParameters, *wszDir, *wcmd;
1571
    DWORD dwApplicationNameLen = MAX_PATH+2;
1572
    DWORD parametersLen = sizeof(parametersBuffer) / sizeof(WCHAR);
1573
    DWORD dirLen = sizeof(dirBuffer) / sizeof(WCHAR);
1574
    DWORD wcmdLen = sizeof(wcmdBuffer) / sizeof(WCHAR);
1575
    DWORD len;
1576
    SHELLEXECUTEINFOW sei_tmp;	/* modifiable copy of SHELLEXECUTEINFO struct */
1577
    WCHAR wfileName[MAX_PATH];
1578
    WCHAR *env;
1579
    WCHAR wszKeyname[256];
1580
    LPCWSTR lpFile;
1581
    UINT_PTR retval = SE_ERR_NOASSOC;
1582
    BOOL appKnownSingular = FALSE;
1583

1584
    /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */
1585
    sei_tmp = *sei;
1586

1587
    TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n",
1588 1589 1590
            sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb),
            debugstr_w(sei_tmp.lpFile), debugstr_w(sei_tmp.lpParameters),
            debugstr_w(sei_tmp.lpDirectory), sei_tmp.nShow,
1591 1592
            ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ?
                debugstr_w(sei_tmp.lpClass) : "not used");
1593

1594
    sei->hProcess = NULL;
1595

1596
    /* make copies of all path/command strings */
1597
    if (!sei_tmp.lpFile)
1598 1599
    {
        wszApplicationName = HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
1600
        *wszApplicationName = '\0';
1601
    }
1602 1603
    else if (*sei_tmp.lpFile == '\"')
    {
1604 1605 1606 1607
        DWORD l = strlenW(sei_tmp.lpFile+1);
        if(l >= dwApplicationNameLen) dwApplicationNameLen = l+1;
        wszApplicationName = HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
        memcpy(wszApplicationName, sei_tmp.lpFile+1, (l+1)*sizeof(WCHAR));
1608 1609
        if (wszApplicationName[l-1] == '\"')
            wszApplicationName[l-1] = '\0';
1610
        appKnownSingular = TRUE;
1611
        TRACE("wszApplicationName=%s\n",debugstr_w(wszApplicationName));
1612 1613 1614 1615 1616
    } else {
        DWORD l = strlenW(sei_tmp.lpFile)+1;
        if(l > dwApplicationNameLen) dwApplicationNameLen = l+1;
        wszApplicationName = HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
        memcpy(wszApplicationName, sei_tmp.lpFile, l*sizeof(WCHAR));
1617
    }
1618

1619
    wszParameters = parametersBuffer;
1620
    if (sei_tmp.lpParameters)
1621 1622 1623 1624 1625 1626 1627
    {
        len = lstrlenW(sei_tmp.lpParameters) + 1;
        if (len > parametersLen)
        {
            wszParameters = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
            parametersLen = len;
        }
1628
	strcpyW(wszParameters, sei_tmp.lpParameters);
1629
    }
1630
    else
1631
	*wszParameters = '\0';
1632

1633
    wszDir = dirBuffer;
1634
    if (sei_tmp.lpDirectory)
1635 1636 1637 1638 1639 1640 1641
    {
        len = lstrlenW(sei_tmp.lpDirectory) + 1;
        if (len > dirLen)
        {
            wszDir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
            dirLen = len;
        }
1642
	strcpyW(wszDir, sei_tmp.lpDirectory);
1643
    }
1644 1645 1646 1647 1648
    else
	*wszDir = '\0';

    /* adjust string pointers to point to the new buffers */
    sei_tmp.lpFile = wszApplicationName;
1649
    sei_tmp.lpParameters = wszParameters;
1650 1651
    sei_tmp.lpDirectory = wszDir;

1652
    if (sei_tmp.fMask & unsupportedFlags)
1653
    {
1654
        FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags);
1655 1656 1657
    }

    /* process the IDList */
1658
    if (sei_tmp.fMask & SEE_MASK_IDLIST)
1659
    {
1660 1661 1662 1663 1664 1665
	IShellExecuteHookW* pSEH;

	HRESULT hr = SHBindToParent(sei_tmp.lpIDList, &IID_IShellExecuteHookW, (LPVOID*)&pSEH, NULL);

	if (SUCCEEDED(hr))
	{
1666
	    hr = IShellExecuteHookW_Execute(pSEH, &sei_tmp);
1667 1668 1669

	    IShellExecuteHookW_Release(pSEH);

1670 1671
	    if (hr == S_OK) {
                HeapFree(GetProcessHeap(), 0, wszApplicationName);
1672 1673
                if (wszParameters != parametersBuffer)
                    HeapFree(GetProcessHeap(), 0, wszParameters);
1674 1675
                if (wszDir != dirBuffer)
                    HeapFree(GetProcessHeap(), 0, wszDir);
1676
		return TRUE;
1677
            }
1678 1679
	}

1680
        SHGetPathFromIDListW(sei_tmp.lpIDList, wszApplicationName);
1681
        appKnownSingular = TRUE;
1682
        TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName));
1683 1684
    }

1685 1686 1687
    if ( ERROR_SUCCESS == ShellExecute_FromContextMenu( &sei_tmp ) )
    {
        sei->hInstApp = (HINSTANCE) 33;
1688
        HeapFree(GetProcessHeap(), 0, wszApplicationName);
1689 1690
        if (wszParameters != parametersBuffer)
            HeapFree(GetProcessHeap(), 0, wszParameters);
1691 1692
        if (wszDir != dirBuffer)
            HeapFree(GetProcessHeap(), 0, wszDir);
1693 1694 1695
        return TRUE;
    }

1696
    if (sei_tmp.fMask & SEE_MASK_CLASSALL)
1697
    {
1698 1699
        retval = SHELL_execute_class( wszApplicationName, &sei_tmp, sei,
                                      execfunc );
1700 1701
        if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
            do_error_dialog(retval, sei_tmp.hwnd);
1702
        HeapFree(GetProcessHeap(), 0, wszApplicationName);
1703 1704
        if (wszParameters != parametersBuffer)
            HeapFree(GetProcessHeap(), 0, wszParameters);
1705 1706
        if (wszDir != dirBuffer)
            HeapFree(GetProcessHeap(), 0, wszDir);
1707
        return retval > 32;
1708 1709
    }

1710 1711 1712
    /* Has the IDList not yet been translated? */
    if (sei_tmp.fMask & SEE_MASK_IDLIST)
    {
1713 1714 1715 1716
        appKnownSingular = SHELL_translate_idlist( &sei_tmp, wszParameters,
                                                   parametersLen,
                                                   wszApplicationName,
                                                   dwApplicationNameLen );
1717 1718
    }

1719 1720
    /* convert file URLs */
    if (UrlIsFileUrlW(sei_tmp.lpFile))
1721 1722
    {
        LPWSTR buf;
1723 1724 1725 1726
        DWORD size;

        size = MAX_PATH;
        buf = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1727 1728
        if (!buf || FAILED(PathCreateFromUrlW(sei_tmp.lpFile, buf, &size, 0))) {
            HeapFree(GetProcessHeap(), 0, buf);
1729
            return SE_ERR_OOM;
1730
        }
1731 1732

        HeapFree(GetProcessHeap(), 0, wszApplicationName);
1733
        dwApplicationNameLen = lstrlenW(buf) + 1;
1734 1735 1736
        wszApplicationName = buf;
        sei_tmp.lpFile = wszApplicationName;
    }
1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753
    else /* or expand environment strings (not both!) */
    {
        len = ExpandEnvironmentStringsW(sei_tmp.lpFile, NULL, 0);
        if (len>0)
        {
            LPWSTR buf;
            buf = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));

            ExpandEnvironmentStringsW(sei_tmp.lpFile, buf, len + 1);
            HeapFree(GetProcessHeap(), 0, wszApplicationName);
            dwApplicationNameLen = len + 1;
            wszApplicationName = buf;
            /* appKnownSingular unmodified */

            sei_tmp.lpFile = wszApplicationName;
        }
    }
1754 1755

    if (*sei_tmp.lpDirectory)
1756 1757 1758 1759 1760 1761 1762 1763
    {
        len = ExpandEnvironmentStringsW(sei_tmp.lpDirectory, NULL, 0);
        if (len > 0)
        {
            LPWSTR buf;
            len++;
            buf = HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
            ExpandEnvironmentStringsW(sei_tmp.lpDirectory, buf, len);
1764 1765 1766 1767
            if (wszDir != dirBuffer)
                HeapFree(GetProcessHeap(), 0, wszDir);
            wszDir = buf;
            sei_tmp.lpDirectory = wszDir;
1768 1769
        }
    }
1770

Duane Clark's avatar
Duane Clark committed
1771
    /* Else, try to execute the filename */
1772
    TRACE("execute:%s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir));
1773 1774

    /* separate out command line arguments from executable file name */
1775
    if (!*sei_tmp.lpParameters && !appKnownSingular) {
1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800
	/* If the executable path is quoted, handle the rest of the command line as parameters. */
	if (sei_tmp.lpFile[0] == '"') {
	    LPWSTR src = wszApplicationName/*sei_tmp.lpFile*/ + 1;
	    LPWSTR dst = wfileName;
	    LPWSTR end;

	    /* copy the unquoted executable path to 'wfileName' */
	    while(*src && *src!='"')
		*dst++ = *src++;

	    *dst = '\0';

	    if (*src == '"') {
		end = ++src;

		while(isspace(*src))
		    ++src;
	    } else
		end = src;

	    /* copy the parameter string to 'wszParameters' */
	    strcpyW(wszParameters, src);

	    /* terminate previous command string after the quote character */
	    *end = '\0';
1801
            lpFile = wfileName;
1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
	}
	else
	{
	    /* If the executable name is not quoted, we have to use this search loop here,
	       that in CreateProcess() is not sufficient because it does not handle shell links. */
	    WCHAR buffer[MAX_PATH], xlpFile[MAX_PATH];
	    LPWSTR space, s;

	    LPWSTR beg = wszApplicationName/*sei_tmp.lpFile*/;
	    for(s=beg; (space=strchrW(s, ' ')); s=space+1) {
		int idx = space-sei_tmp.lpFile;
1813
		memcpy(buffer, sei_tmp.lpFile, idx * sizeof(WCHAR));
1814 1815 1816
		buffer[idx] = '\0';

		/*FIXME This finds directory paths if the targeted file name contains spaces. */
1817
		if (SearchPathW(*sei_tmp.lpDirectory? sei_tmp.lpDirectory: NULL, buffer, wszExe, sizeof(xlpFile)/sizeof(xlpFile[0]), xlpFile, NULL))
1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831
		{
		    /* separate out command from parameter string */
		    LPCWSTR p = space + 1;

		    while(isspaceW(*p))
			++p;

		    strcpyW(wszParameters, p);
		    *space = '\0';

		    break;
		}
	    }

1832
            lpFile = sei_tmp.lpFile;
1833 1834
	}
    } else
1835
        lpFile = sei_tmp.lpFile;
1836

1837 1838 1839 1840 1841 1842 1843 1844 1845
    wcmd = wcmdBuffer;
    len = lstrlenW(wszApplicationName) + 1;
    if (sei_tmp.lpParameters[0])
        len += 1 + lstrlenW(wszParameters);
    if (len > wcmdLen)
    {
        wcmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
        wcmdLen = len;
    }
1846
    strcpyW(wcmd, wszApplicationName);
1847
    if (sei_tmp.lpParameters[0]) {
1848 1849
        strcatW(wcmd, wSpace);
        strcatW(wcmd, wszParameters);
1850 1851
    }

1852
    retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei);
1853 1854
    if (retval > 32) {
        HeapFree(GetProcessHeap(), 0, wszApplicationName);
1855 1856
        if (wszParameters != parametersBuffer)
            HeapFree(GetProcessHeap(), 0, wszParameters);
1857 1858
        if (wszDir != dirBuffer)
            HeapFree(GetProcessHeap(), 0, wszDir);
1859 1860
        if (wcmd != wcmdBuffer)
            HeapFree(GetProcessHeap(), 0, wcmd);
Duane Clark's avatar
Duane Clark committed
1861
        return TRUE;
1862
    }
1863

Duane Clark's avatar
Duane Clark committed
1864
    /* Else, try to find the executable */
1865
    wcmd[0] = '\0';
1866
    retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, wszKeyname, &env, sei_tmp.lpIDList, sei_tmp.lpParameters);
1867 1868
    if (retval > 32)  /* Found */
    {
1869
        retval = SHELL_quote_and_execute( wcmd, wszParameters, wszKeyname,
1870 1871
                                          wszApplicationName, env, &sei_tmp,
                                          sei, execfunc );
1872
        HeapFree( GetProcessHeap(), 0, env );
1873
    }
1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891
    else if (PathIsDirectoryW(lpFile))
    {
        static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r',0};
        static const WCHAR wQuote[] = {'"',0};
        WCHAR wExec[MAX_PATH];
        WCHAR * lpQuotedFile = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * (strlenW(lpFile) + 3) );

        if (lpQuotedFile)
        {
            retval = SHELL_FindExecutable( sei_tmp.lpDirectory, wExplorer,
                                           wszOpen, wExec, MAX_PATH,
                                           NULL, &env, NULL, NULL );
            if (retval > 32)
            {
                strcpyW(lpQuotedFile, wQuote);
                strcatW(lpQuotedFile, lpFile);
                strcatW(lpQuotedFile, wQuote);
                retval = SHELL_quote_and_execute( wExec, lpQuotedFile,
1892
                                                  wszKeyname,
1893 1894 1895 1896 1897 1898 1899 1900 1901
                                                  wszApplicationName, env,
                                                  &sei_tmp, sei, execfunc );
                HeapFree( GetProcessHeap(), 0, env );
            }
            HeapFree( GetProcessHeap(), 0, lpQuotedFile );
        }
        else
            retval = 0; /* Out of memory */
    }
1902
    else if (PathIsURLW(lpFile))    /* File not found, check for URL */
1903
    {
1904
        retval = SHELL_execute_url( lpFile, wcmd, &sei_tmp, sei, execfunc );
1905 1906
    }
    /* Check if file specified is in the form www.??????.*** */
1907
    else if (!strncmpiW(lpFile, wWww, 3))
1908
    {
1909
        /* if so, prefix lpFile with http:// and call ShellExecute */
1910 1911 1912
        WCHAR lpstrTmpFile[256];
        strcpyW(lpstrTmpFile, wHttp);
        strcatW(lpstrTmpFile, lpFile);
Kevin Koltzau's avatar
Kevin Koltzau committed
1913
        retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0);
1914 1915
    }

1916
    TRACE("retval %lu\n", retval);
1917

1918
    HeapFree(GetProcessHeap(), 0, wszApplicationName);
1919 1920
    if (wszParameters != parametersBuffer)
        HeapFree(GetProcessHeap(), 0, wszParameters);
1921 1922
    if (wszDir != dirBuffer)
        HeapFree(GetProcessHeap(), 0, wszDir);
1923 1924
    if (wcmd != wcmdBuffer)
        HeapFree(GetProcessHeap(), 0, wcmd);
1925

1926
    sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval);
1927 1928 1929

    if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
        do_error_dialog(retval, sei_tmp.hwnd);
1930
    return retval > 32;
1931 1932
}

1933 1934 1935
/*************************************************************************
 * ShellExecuteA			[SHELL32.290]
 */
1936 1937
HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile,
                               LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd)
1938
{
1939 1940
    SHELLEXECUTEINFOA sei;

1941
    TRACE("%p,%s,%s,%s,%s,%d\n",
1942
          hWnd, debugstr_a(lpVerb), debugstr_a(lpFile),
1943
          debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd);
1944

1945
    sei.cbSize = sizeof(sei);
1946
    sei.fMask = SEE_MASK_FLAG_NO_UI;
1947
    sei.hwnd = hWnd;
1948
    sei.lpVerb = lpVerb;
1949 1950 1951 1952 1953 1954 1955 1956
    sei.lpFile = lpFile;
    sei.lpParameters = lpParameters;
    sei.lpDirectory = lpDirectory;
    sei.nShow = iShowCmd;
    sei.lpIDList = 0;
    sei.lpClass = 0;
    sei.hkeyClass = 0;
    sei.dwHotKey = 0;
1957
    sei.hProcess = 0;
1958

1959
    ShellExecuteExA (&sei);
1960 1961 1962 1963 1964 1965 1966
    return sei.hInstApp;
}

/*************************************************************************
 * ShellExecuteExA				[SHELL32.292]
 *
 */
1967
BOOL WINAPI DECLSPEC_HOTPATCH ShellExecuteExA (LPSHELLEXECUTEINFOA sei)
1968
{
1969 1970 1971
    SHELLEXECUTEINFOW seiW;
    BOOL ret;
    WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL, *wClass = NULL;
1972 1973 1974

    TRACE("%p\n", sei);

1975
    memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW));
1976 1977

    if (sei->lpVerb)
1978
	seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb);
1979 1980

    if (sei->lpFile)
1981
        seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile);
1982 1983

    if (sei->lpParameters)
1984
        seiW.lpParameters = __SHCloneStrAtoW(&wParameters, sei->lpParameters);
1985 1986

    if (sei->lpDirectory)
1987
        seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory);
1988

1989
    if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME && sei->lpClass)
1990
        seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass);
1991
    else
1992
        seiW.lpClass = NULL;
1993

1994
    ret = SHELL_execute( &seiW, SHELL_ExecuteW );
1995

1996 1997
    sei->hInstApp = seiW.hInstApp;

1998 1999 2000
    if (sei->fMask & SEE_MASK_NOCLOSEPROCESS)
        sei->hProcess = seiW.hProcess;

2001 2002 2003 2004 2005
    SHFree(wVerb);
    SHFree(wFile);
    SHFree(wParameters);
    SHFree(wDirectory);
    SHFree(wClass);
2006 2007

    return ret;
2008 2009
}

2010 2011 2012 2013
/*************************************************************************
 * ShellExecuteExW				[SHELL32.293]
 *
 */
2014
BOOL WINAPI DECLSPEC_HOTPATCH ShellExecuteExW (LPSHELLEXECUTEINFOW sei)
2015
{
2016
    return SHELL_execute( sei, SHELL_ExecuteW );
2017 2018
}

2019 2020 2021
/*************************************************************************
 * ShellExecuteW			[SHELL32.294]
 * from shellapi.h
2022
 * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpVerb,
2023
 * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
2024
 */
2025
HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile,
2026 2027
                               LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd)
{
2028
    SHELLEXECUTEINFOW sei;
2029

2030 2031
    TRACE("\n");
    sei.cbSize = sizeof(sei);
2032
    sei.fMask = SEE_MASK_FLAG_NO_UI;
2033
    sei.hwnd = hwnd;
2034
    sei.lpVerb = lpVerb;
2035 2036 2037 2038 2039 2040 2041 2042
    sei.lpFile = lpFile;
    sei.lpParameters = lpParameters;
    sei.lpDirectory = lpDirectory;
    sei.nShow = nShowCmd;
    sei.lpIDList = 0;
    sei.lpClass = 0;
    sei.hkeyClass = 0;
    sei.dwHotKey = 0;
2043
    sei.hProcess = 0;
2044

2045
    SHELL_execute( &sei, SHELL_ExecuteW );
2046 2047
    return sei.hInstApp;
}
2048

2049 2050 2051 2052 2053
/*************************************************************************
 * WOWShellExecute			[SHELL32.@]
 *
 * FIXME: the callback function most likely doesn't work the same way on Windows.
 */
2054
HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb,LPCSTR lpFile,
2055 2056 2057 2058 2059 2060
                                 LPCSTR lpParameters,LPCSTR lpDirectory, INT iShowCmd, void *callback)
{
    SHELLEXECUTEINFOW seiW;
    WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL;
    HANDLE hProcess = 0;

2061
    seiW.lpVerb = lpVerb ? __SHCloneStrAtoW(&wVerb, lpVerb) : NULL;
2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084
    seiW.lpFile = lpFile ? __SHCloneStrAtoW(&wFile, lpFile) : NULL;
    seiW.lpParameters = lpParameters ? __SHCloneStrAtoW(&wParameters, lpParameters) : NULL;
    seiW.lpDirectory = lpDirectory ? __SHCloneStrAtoW(&wDirectory, lpDirectory) : NULL;

    seiW.cbSize = sizeof(seiW);
    seiW.fMask = 0;
    seiW.hwnd = hWnd;
    seiW.nShow = iShowCmd;
    seiW.lpIDList = 0;
    seiW.lpClass = 0;
    seiW.hkeyClass = 0;
    seiW.dwHotKey = 0;
    seiW.hProcess = hProcess;

    SHELL_execute( &seiW, callback );

    SHFree(wVerb);
    SHFree(wFile);
    SHFree(wParameters);
    SHFree(wDirectory);
    return seiW.hInstApp;
}

2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099
/*************************************************************************
 * OpenAs_RunDLLA          [SHELL32.@]
 */
void WINAPI OpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow)
{
    FIXME("%p, %p, %s, %d\n", hwnd, hinst, debugstr_a(cmdline), cmdshow);
}

/*************************************************************************
 * OpenAs_RunDLLW          [SHELL32.@]
 */
void WINAPI OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow)
{
    FIXME("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow);
}