shlexec.c 66.8 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
    dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
341
    if (!(psei->fMask & SEE_MASK_NO_CONSOLE))
342 343
        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_NO_CONSOLE|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
static void SHELL_translate_idlist( LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen )
1427 1428 1429 1430 1431
{
    static const WCHAR wExplorer[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0};
    WCHAR buffer[MAX_PATH];

    /* last chance to translate IDList: now also allow CLSID paths */
1432
    if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW(sei->lpIDList, buffer, sizeof(buffer)/sizeof(WCHAR)))) {
1433 1434
        if (buffer[0]==':' && buffer[1]==':') {
            /* open shell folder for the specified class GUID */
1435
            if (strlenW(buffer) + 1 > parametersLen)
1436 1437 1438
                ERR("parameters len exceeds buffer size (%i > %i), truncating\n",
                    lstrlenW(buffer) + 1, parametersLen);
            lstrcpynW(wszParameters, buffer, parametersLen);
1439
            if (strlenW(wExplorer) > dwApplicationNameLen)
1440 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
                ERR("application len exceeds buffer size (%i > %i), truncating\n",
                    lstrlenW(wExplorer) + 1, dwApplicationNameLen);
            lstrcpynW(wszApplicationName, wExplorer, dwApplicationNameLen);

            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");
            }
            sei->fMask &= ~SEE_MASK_INVOKEIDLIST;
        }
    }
}

1468
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 )
1469 1470 1471 1472
{
    static const WCHAR wQuote[] = {'"',0};
    static const WCHAR wSpace[] = {' ',0};
    UINT_PTR retval;
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
    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));
1484 1485 1486 1487 1488 1489 1490 1491 1492 1493
    /* 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);
    }
1494 1495 1496
    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);
1497 1498
    else
        retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out);
1499
    HeapFree(GetProcessHeap(), 0, wszQuotedCmd);
1500 1501 1502
    return retval;
}

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

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

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

1533 1534 1535 1536
    retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters,
                              wcmd, execfunc, psei, psei_out);
    HeapFree(GetProcessHeap(), 0, lpstrProtocol);
    return retval;
1537 1538
}

1539 1540 1541 1542 1543
static void do_error_dialog( UINT_PTR retval, HWND hwnd )
{
    WCHAR msg[2048];
    int error_code=GetLastError();

1544 1545 1546 1547
    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);
1548 1549 1550 1551

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

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

1564 1565
    WCHAR parametersBuffer[1024], dirBuffer[MAX_PATH], wcmdBuffer[1024];
    WCHAR *wszApplicationName, *wszParameters, *wszDir, *wcmd;
1566
    DWORD dwApplicationNameLen = MAX_PATH+2;
1567
    DWORD parametersLen = sizeof(parametersBuffer) / sizeof(WCHAR);
1568
    DWORD wcmdLen = sizeof(wcmdBuffer) / sizeof(WCHAR);
1569
    DWORD len;
1570
    SHELLEXECUTEINFOW sei_tmp;	/* modifiable copy of SHELLEXECUTEINFO struct */
1571
    WCHAR *env;
1572
    WCHAR wszKeyname[256];
1573
    LPCWSTR lpFile;
1574
    UINT_PTR retval = SE_ERR_NOASSOC;
1575

1576
    /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */
1577
    sei_tmp = *sei;
1578

1579
    TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n",
1580 1581 1582
            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,
1583 1584
            ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ?
                debugstr_w(sei_tmp.lpClass) : "not used");
1585

1586
    sei->hProcess = NULL;
1587

1588
    /* make copies of all path/command strings */
1589
    if (!sei_tmp.lpFile)
1590 1591
    {
        wszApplicationName = HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
1592
        *wszApplicationName = '\0';
1593
    }
1594
    else if (*sei_tmp.lpFile == '\"' && sei_tmp.lpFile[(len = strlenW(sei_tmp.lpFile))-1] == '\"')
1595
    {
1596
        if(len-1 >= dwApplicationNameLen) dwApplicationNameLen = len;
1597
        wszApplicationName = HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen*sizeof(WCHAR));
1598 1599 1600
        memcpy(wszApplicationName, sei_tmp.lpFile+1, len*sizeof(WCHAR));
        if(len > 2)
            wszApplicationName[len-2] = '\0';
1601
        TRACE("wszApplicationName=%s\n",debugstr_w(wszApplicationName));
1602 1603 1604 1605 1606
    } 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));
1607
    }
1608

1609
    wszParameters = parametersBuffer;
1610
    if (sei_tmp.lpParameters)
1611 1612 1613 1614 1615 1616 1617
    {
        len = lstrlenW(sei_tmp.lpParameters) + 1;
        if (len > parametersLen)
        {
            wszParameters = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
            parametersLen = len;
        }
1618
	strcpyW(wszParameters, sei_tmp.lpParameters);
1619
    }
1620
    else
1621
	*wszParameters = '\0';
1622

1623
    wszDir = dirBuffer;
1624
    if (sei_tmp.lpDirectory)
1625 1626
    {
        len = lstrlenW(sei_tmp.lpDirectory) + 1;
1627
        if (len > sizeof(dirBuffer) / sizeof(WCHAR))
1628
            wszDir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1629
	strcpyW(wszDir, sei_tmp.lpDirectory);
1630
    }
1631 1632 1633 1634 1635
    else
	*wszDir = '\0';

    /* adjust string pointers to point to the new buffers */
    sei_tmp.lpFile = wszApplicationName;
1636
    sei_tmp.lpParameters = wszParameters;
1637 1638
    sei_tmp.lpDirectory = wszDir;

1639
    if (sei_tmp.fMask & unsupportedFlags)
1640
    {
1641
        FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags);
1642 1643 1644
    }

    /* process the IDList */
1645
    if (sei_tmp.fMask & SEE_MASK_IDLIST)
1646
    {
1647 1648 1649 1650 1651 1652
	IShellExecuteHookW* pSEH;

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

	if (SUCCEEDED(hr))
	{
1653
	    hr = IShellExecuteHookW_Execute(pSEH, &sei_tmp);
1654 1655 1656

	    IShellExecuteHookW_Release(pSEH);

1657 1658
	    if (hr == S_OK) {
                HeapFree(GetProcessHeap(), 0, wszApplicationName);
1659 1660
                if (wszParameters != parametersBuffer)
                    HeapFree(GetProcessHeap(), 0, wszParameters);
1661 1662
                if (wszDir != dirBuffer)
                    HeapFree(GetProcessHeap(), 0, wszDir);
1663
		return TRUE;
1664
            }
1665 1666
	}

1667
        SHGetPathFromIDListW(sei_tmp.lpIDList, wszApplicationName);
1668
        TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName));
1669 1670
    }

1671 1672 1673
    if ( ERROR_SUCCESS == ShellExecute_FromContextMenu( &sei_tmp ) )
    {
        sei->hInstApp = (HINSTANCE) 33;
1674
        HeapFree(GetProcessHeap(), 0, wszApplicationName);
1675 1676
        if (wszParameters != parametersBuffer)
            HeapFree(GetProcessHeap(), 0, wszParameters);
1677 1678
        if (wszDir != dirBuffer)
            HeapFree(GetProcessHeap(), 0, wszDir);
1679 1680 1681
        return TRUE;
    }

1682
    if (sei_tmp.fMask & SEE_MASK_CLASSALL)
1683
    {
1684 1685
        retval = SHELL_execute_class( wszApplicationName, &sei_tmp, sei,
                                      execfunc );
1686 1687
        if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
            do_error_dialog(retval, sei_tmp.hwnd);
1688
        HeapFree(GetProcessHeap(), 0, wszApplicationName);
1689 1690
        if (wszParameters != parametersBuffer)
            HeapFree(GetProcessHeap(), 0, wszParameters);
1691 1692
        if (wszDir != dirBuffer)
            HeapFree(GetProcessHeap(), 0, wszDir);
1693
        return retval > 32;
1694 1695
    }

1696 1697 1698
    /* Has the IDList not yet been translated? */
    if (sei_tmp.fMask & SEE_MASK_IDLIST)
    {
1699 1700 1701 1702
        SHELL_translate_idlist( &sei_tmp, wszParameters,
                                parametersLen,
                                wszApplicationName,
                                dwApplicationNameLen );
1703 1704
    }

1705 1706
    /* convert file URLs */
    if (UrlIsFileUrlW(sei_tmp.lpFile))
1707 1708
    {
        LPWSTR buf;
1709 1710 1711 1712
        DWORD size;

        size = MAX_PATH;
        buf = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1713 1714
        if (!buf || FAILED(PathCreateFromUrlW(sei_tmp.lpFile, buf, &size, 0))) {
            HeapFree(GetProcessHeap(), 0, buf);
1715
            return SE_ERR_OOM;
1716
        }
1717 1718

        HeapFree(GetProcessHeap(), 0, wszApplicationName);
1719
        dwApplicationNameLen = lstrlenW(buf) + 1;
1720 1721 1722
        wszApplicationName = buf;
        sei_tmp.lpFile = wszApplicationName;
    }
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738
    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;

            sei_tmp.lpFile = wszApplicationName;
        }
    }
1739 1740

    if (*sei_tmp.lpDirectory)
1741 1742 1743 1744 1745 1746 1747 1748
    {
        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);
1749 1750 1751 1752
            if (wszDir != dirBuffer)
                HeapFree(GetProcessHeap(), 0, wszDir);
            wszDir = buf;
            sei_tmp.lpDirectory = wszDir;
1753 1754
        }
    }
1755

Duane Clark's avatar
Duane Clark committed
1756
    /* Else, try to execute the filename */
1757
    TRACE("execute:%s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir));
1758
    lpFile = sei_tmp.lpFile;
1759
    wcmd = wcmdBuffer;
1760
    len = lstrlenW(wszApplicationName) + 3;
1761 1762 1763 1764 1765 1766 1767
    if (sei_tmp.lpParameters[0])
        len += 1 + lstrlenW(wszParameters);
    if (len > wcmdLen)
    {
        wcmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
        wcmdLen = len;
    }
1768 1769 1770 1771 1772
    wcmd[0] = '\"';
    len = lstrlenW(wszApplicationName);
    memcpy(wcmd+1, wszApplicationName, len * sizeof(WCHAR));
    len++;
    wcmd[len++] = '\"';
1773
    wcmd[len] = 0;
1774
    if (sei_tmp.lpParameters[0]) {
1775 1776
        wcmd[len++] = ' ';
        strcpyW(wcmd+len, wszParameters);
1777 1778
    }

1779
    retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei);
1780 1781
    if (retval > 32) {
        HeapFree(GetProcessHeap(), 0, wszApplicationName);
1782 1783
        if (wszParameters != parametersBuffer)
            HeapFree(GetProcessHeap(), 0, wszParameters);
1784 1785
        if (wszDir != dirBuffer)
            HeapFree(GetProcessHeap(), 0, wszDir);
1786 1787
        if (wcmd != wcmdBuffer)
            HeapFree(GetProcessHeap(), 0, wcmd);
Duane Clark's avatar
Duane Clark committed
1788
        return TRUE;
1789
    }
1790

Duane Clark's avatar
Duane Clark committed
1791
    /* Else, try to find the executable */
1792
    wcmd[0] = '\0';
1793
    retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, wszKeyname, &env, sei_tmp.lpIDList, sei_tmp.lpParameters);
1794 1795
    if (retval > 32)  /* Found */
    {
1796
        retval = SHELL_quote_and_execute( wcmd, wszParameters, wszKeyname,
1797 1798
                                          wszApplicationName, env, &sei_tmp,
                                          sei, execfunc );
1799
        HeapFree( GetProcessHeap(), 0, env );
1800
    }
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818
    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,
1819
                                                  wszKeyname,
1820 1821 1822 1823 1824 1825 1826 1827 1828
                                                  wszApplicationName, env,
                                                  &sei_tmp, sei, execfunc );
                HeapFree( GetProcessHeap(), 0, env );
            }
            HeapFree( GetProcessHeap(), 0, lpQuotedFile );
        }
        else
            retval = 0; /* Out of memory */
    }
1829
    else if (PathIsURLW(lpFile))    /* File not found, check for URL */
1830
    {
1831
        retval = SHELL_execute_url( lpFile, wcmd, &sei_tmp, sei, execfunc );
1832 1833
    }
    /* Check if file specified is in the form www.??????.*** */
1834
    else if (!strncmpiW(lpFile, wWww, 3))
1835
    {
1836
        /* if so, prefix lpFile with http:// and call ShellExecute */
1837 1838 1839
        WCHAR lpstrTmpFile[256];
        strcpyW(lpstrTmpFile, wHttp);
        strcatW(lpstrTmpFile, lpFile);
Kevin Koltzau's avatar
Kevin Koltzau committed
1840
        retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0);
1841 1842
    }

1843
    TRACE("retval %lu\n", retval);
1844

1845
    HeapFree(GetProcessHeap(), 0, wszApplicationName);
1846 1847
    if (wszParameters != parametersBuffer)
        HeapFree(GetProcessHeap(), 0, wszParameters);
1848 1849
    if (wszDir != dirBuffer)
        HeapFree(GetProcessHeap(), 0, wszDir);
1850 1851
    if (wcmd != wcmdBuffer)
        HeapFree(GetProcessHeap(), 0, wcmd);
1852

1853
    sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval);
1854 1855 1856

    if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
        do_error_dialog(retval, sei_tmp.hwnd);
1857
    return retval > 32;
1858 1859
}

1860 1861 1862
/*************************************************************************
 * ShellExecuteA			[SHELL32.290]
 */
1863 1864
HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile,
                               LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd)
1865
{
1866 1867
    SHELLEXECUTEINFOA sei;

1868
    TRACE("%p,%s,%s,%s,%s,%d\n",
1869
          hWnd, debugstr_a(lpVerb), debugstr_a(lpFile),
1870
          debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd);
1871

1872
    sei.cbSize = sizeof(sei);
1873
    sei.fMask = SEE_MASK_FLAG_NO_UI;
1874
    sei.hwnd = hWnd;
1875
    sei.lpVerb = lpVerb;
1876 1877 1878 1879 1880 1881 1882 1883
    sei.lpFile = lpFile;
    sei.lpParameters = lpParameters;
    sei.lpDirectory = lpDirectory;
    sei.nShow = iShowCmd;
    sei.lpIDList = 0;
    sei.lpClass = 0;
    sei.hkeyClass = 0;
    sei.dwHotKey = 0;
1884
    sei.hProcess = 0;
1885

1886
    ShellExecuteExA (&sei);
1887 1888 1889 1890 1891 1892 1893
    return sei.hInstApp;
}

/*************************************************************************
 * ShellExecuteExA				[SHELL32.292]
 *
 */
1894
BOOL WINAPI DECLSPEC_HOTPATCH ShellExecuteExA (LPSHELLEXECUTEINFOA sei)
1895
{
1896 1897 1898
    SHELLEXECUTEINFOW seiW;
    BOOL ret;
    WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL, *wClass = NULL;
1899 1900 1901

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

1902
    memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW));
1903 1904

    if (sei->lpVerb)
1905
	seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb);
1906 1907

    if (sei->lpFile)
1908
        seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile);
1909 1910

    if (sei->lpParameters)
1911
        seiW.lpParameters = __SHCloneStrAtoW(&wParameters, sei->lpParameters);
1912 1913

    if (sei->lpDirectory)
1914
        seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory);
1915

1916
    if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME && sei->lpClass)
1917
        seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass);
1918
    else
1919
        seiW.lpClass = NULL;
1920

1921
    ret = SHELL_execute( &seiW, SHELL_ExecuteW );
1922

1923 1924
    sei->hInstApp = seiW.hInstApp;

1925 1926 1927
    if (sei->fMask & SEE_MASK_NOCLOSEPROCESS)
        sei->hProcess = seiW.hProcess;

1928 1929 1930 1931 1932
    SHFree(wVerb);
    SHFree(wFile);
    SHFree(wParameters);
    SHFree(wDirectory);
    SHFree(wClass);
1933 1934

    return ret;
1935 1936
}

1937 1938 1939 1940
/*************************************************************************
 * ShellExecuteExW				[SHELL32.293]
 *
 */
1941
BOOL WINAPI DECLSPEC_HOTPATCH ShellExecuteExW (LPSHELLEXECUTEINFOW sei)
1942
{
1943
    return SHELL_execute( sei, SHELL_ExecuteW );
1944 1945
}

1946 1947 1948
/*************************************************************************
 * ShellExecuteW			[SHELL32.294]
 * from shellapi.h
1949
 * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpVerb,
1950
 * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
1951
 */
1952
HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile,
1953 1954
                               LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd)
{
1955
    SHELLEXECUTEINFOW sei;
1956

1957 1958
    TRACE("\n");
    sei.cbSize = sizeof(sei);
1959
    sei.fMask = SEE_MASK_FLAG_NO_UI;
1960
    sei.hwnd = hwnd;
1961
    sei.lpVerb = lpVerb;
1962 1963 1964 1965 1966 1967 1968 1969
    sei.lpFile = lpFile;
    sei.lpParameters = lpParameters;
    sei.lpDirectory = lpDirectory;
    sei.nShow = nShowCmd;
    sei.lpIDList = 0;
    sei.lpClass = 0;
    sei.hkeyClass = 0;
    sei.dwHotKey = 0;
1970
    sei.hProcess = 0;
1971

1972
    SHELL_execute( &sei, SHELL_ExecuteW );
1973 1974
    return sei.hInstApp;
}
1975

1976 1977 1978 1979 1980
/*************************************************************************
 * WOWShellExecute			[SHELL32.@]
 *
 * FIXME: the callback function most likely doesn't work the same way on Windows.
 */
1981
HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb,LPCSTR lpFile,
1982 1983 1984 1985 1986 1987
                                 LPCSTR lpParameters,LPCSTR lpDirectory, INT iShowCmd, void *callback)
{
    SHELLEXECUTEINFOW seiW;
    WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL;
    HANDLE hProcess = 0;

1988
    seiW.lpVerb = lpVerb ? __SHCloneStrAtoW(&wVerb, lpVerb) : NULL;
1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011
    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;
}

2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
/*************************************************************************
 * 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);
}
2027 2028 2029 2030 2031 2032 2033 2034 2035

/*************************************************************************
 * RegenerateUserEnvironment          [SHELL32.@]
 */
BOOL WINAPI RegenerateUserEnvironment(WCHAR *wunknown, BOOL bunknown)
{
    FIXME("stub: %p, %d\n", wunknown, bunknown);
    return FALSE;
}