X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=dll%2Fwin32%2Fshell32%2Fshlexec.cpp;h=55b3d051fefacfe195b94ce508a71d310ecb2e97;hp=ad53a037c45f1fc0337d6f4899ad34d43981106e;hb=bfcbda227f99c1b59e8ed71f5e0f59f793d496a1;hpb=509461cd33ba865c1cd067f669d8f4178674f669 diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index ad53a037c45..55b3d051fef 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -3,6 +3,7 @@ * * Copyright 1998 Marcus Meissner * Copyright 2002 Eric Pouech + * Copyright 2018 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,16 +20,20 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include -#include +#include "precomp.h" +#include WINE_DEFAULT_DEBUG_CHANNEL(exec); static const WCHAR wszOpen[] = L"open"; static const WCHAR wszExe[] = L".exe"; +static const WCHAR wszCom[] = L".com"; #define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY) +typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, + const SHELLEXECUTEINFOW *sei, LPSHELLEXECUTEINFOW sei_out); + static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum) { bool firstCharQuote = false; @@ -176,7 +181,7 @@ static void ParseTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, * - use rules from here http://www.autohotkey.net/~deleyd/parameters/parameters.htm */ -static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len) +static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len, const WCHAR* lpDir) { WCHAR xlpFile[1024]; BOOL done = FALSE; @@ -263,7 +268,7 @@ static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* if (!done || (*fmt == '1')) { /*FIXME Is the call to SearchPathW() really needed? We already have separated out the parameter string in args. */ - if (SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile) / sizeof(WCHAR), xlpFile, NULL)) + if (SearchPathW(lpDir, lpFile, wszExe, sizeof(xlpFile) / sizeof(WCHAR), xlpFile, NULL)) cmd = xlpFile; else cmd = lpFile; @@ -383,7 +388,12 @@ static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* } } - *res = '\0'; + used++; + if (res - out < static_cast(len)) + *res = '\0'; + else + out[len-1] = '\0'; + TRACE("used %i of %i space\n", used, len); if (out_len) *out_len = used; @@ -397,7 +407,7 @@ static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize) { STRRET strret; - IShellFolder *desktop; + CComPtr desktop; HRESULT hr = SHGetDesktopFolder(&desktop); @@ -407,8 +417,6 @@ static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR psz if (SUCCEEDED(hr)) StrRetToStrNW(pszPath, uOutSize, &strret, pidl); - - desktop->Release(); } return hr; @@ -452,9 +460,12 @@ static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, startup.dwFlags = STARTF_USESHOWWINDOW; startup.wShowWindow = psei->nShow; dwCreationFlags = CREATE_UNICODE_ENVIRONMENT; - - if (psei->fMask & SEE_MASK_NO_CONSOLE) + if (!(psei->fMask & SEE_MASK_NO_CONSOLE)) dwCreationFlags |= CREATE_NEW_CONSOLE; + startup.lpTitle = (LPWSTR)(psei->fMask & (SEE_MASK_HASLINKNAME | SEE_MASK_HASTITLE) ? psei->lpClass : NULL); + + if (psei->fMask & SEE_MASK_HASLINKNAME) + startup.dwFlags |= STARTF_TITLEISLINKNAME; if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env, lpDirectory, &startup, &info)) @@ -568,7 +579,15 @@ static BOOL SHELL_TryAppPathW( LPCWSTR szName, LPWSTR lpResult, WCHAR **env) wcscpy(buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); wcscat(buffer, szName); res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); - if (res) goto end; + if (res) + { + // Add ".exe" extension, if extension does not exists + if (PathAddExtensionW(buffer, wszExe)) + { + res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); + } + if (res) goto end; + } len = MAX_PATH * sizeof(WCHAR); res = RegQueryValueW(hkApp, NULL, lpResult, &len); @@ -587,28 +606,46 @@ end: return found; } -static UINT SHELL_FindExecutableByOperation(LPCWSTR lpOperation, LPWSTR key, LPWSTR filetype, LPWSTR command, LONG commandlen) +/************************************************************************* + * SHELL_FindExecutableByVerb [Internal] + * + * called from SHELL_FindExecutable or SHELL_execute_class + * in/out: + * classname a buffer, big enough, to get the key name to do actually the + * command "WordPad.Document.1\\shell\\open\\command" + * passed as "WordPad.Document.1" + * in: + * lpVerb the operation on it (open) + * 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 + */ +static UINT SHELL_FindExecutableByVerb(LPCWSTR lpVerb, LPWSTR key, LPWSTR classname, LPWSTR command, LONG commandlen) { static const WCHAR wCommand[] = L"\\command"; HKEY hkeyClass; WCHAR verb[MAX_PATH]; - if (RegOpenKeyExW(HKEY_CLASSES_ROOT, filetype, 0, 0x02000000, &hkeyClass)) + if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, 0x02000000, &hkeyClass)) return SE_ERR_NOASSOC; - if (!HCR_GetDefaultVerbW(hkeyClass, lpOperation, verb, sizeof(verb) / sizeof(verb[0]))) + if (!HCR_GetDefaultVerbW(hkeyClass, lpVerb, verb, sizeof(verb) / sizeof(verb[0]))) return SE_ERR_NOASSOC; RegCloseKey(hkeyClass); /* Looking for ...buffer\shell\\command */ - wcscat(filetype, L"\\shell\\"); - wcscat(filetype, verb); - wcscat(filetype, wCommand); + wcscat(classname, L"\\shell\\"); + wcscat(classname, verb); + wcscat(classname, wCommand); - if (RegQueryValueW(HKEY_CLASSES_ROOT, filetype, command, + if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, command, &commandlen) == ERROR_SUCCESS) { commandlen /= sizeof(WCHAR); - if (key) wcscpy(key, filetype); + if (key) wcscpy(key, classname); #if 0 LPWSTR tmp; WCHAR param[256]; @@ -622,10 +659,10 @@ static UINT SHELL_FindExecutableByOperation(LPCWSTR lpOperation, LPWSTR key, LPW */ /* Get the parameters needed by the application from the associated ddeexec key */ - tmp = strstrW(filetype, wCommand); + tmp = strstrW(classname, wCommand); tmp[0] = '\0'; - wcscat(filetype, wDdeexec); - if (RegQueryValueW(HKEY_CLASSES_ROOT, filetype, param, + wcscat(classname, wDdeexec); + if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, param, ¶mlen) == ERROR_SUCCESS) { paramlen /= sizeof(WCHAR); @@ -649,7 +686,7 @@ static UINT SHELL_FindExecutableByOperation(LPCWSTR lpOperation, LPWSTR key, LPW * Utility for code sharing between FindExecutable and ShellExecute * in: * lpFile the name of a file - * lpOperation the operation on it (open) + * lpVerb the operation on it (open) * out: * lpResult a buffer, big enough :-(, to store the command to do the * operation on the file @@ -657,12 +694,12 @@ static UINT SHELL_FindExecutableByOperation(LPCWSTR lpOperation, LPWSTR key, LPW * command (it'll be used afterwards for more information * on the operation) */ -static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOperation, +static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, LPWSTR lpResult, DWORD resultLen, LPWSTR key, WCHAR **env, LPITEMIDLIST pidl, LPCWSTR args) { WCHAR *extension = NULL; /* pointer to file extension */ - WCHAR filetype[256]; /* registry name for this filetype */ - LONG filetypelen = sizeof(filetype); /* length of above */ + WCHAR classname[256]; /* registry name for this file type */ + LONG classnamelen = sizeof(classname); /* length of above */ WCHAR command[1024]; /* command from registry */ WCHAR wBuffer[256]; /* Used to GetProfileString */ UINT retval = SE_ERR_NOASSOC; @@ -697,14 +734,19 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOpera { TRACE("SearchPathW returned non-zero\n"); lpFile = xlpFile; - /* Hey, isn't this value ignored? Why make this call? Shouldn't we return here? --dank*/ + /* 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 */ } attribs = GetFileAttributesW(lpFile); if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY)) { - wcscpy(filetype, L"Folder"); - filetypelen = 6; /* strlen("Folder") */ + wcscpy(classname, L"Folder"); } else { @@ -728,7 +770,7 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOpera /* Three places to check: */ /* 1. win.ini, [windows], programs (NB no leading '.') */ - /* 2. Registry, HKEY_CLASS_ROOT\\shell\open\command */ + /* 2. Registry, HKEY_CLASS_ROOT\\shell\open\command */ /* 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 */ @@ -765,33 +807,31 @@ static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOpera } /* Check registry */ - if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, filetype, - &filetypelen) == ERROR_SUCCESS) + if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, classname, + &classnamelen) == ERROR_SUCCESS) { - filetypelen /= sizeof(WCHAR); - if (filetypelen == sizeof(filetype) / sizeof(WCHAR)) - filetypelen--; + classnamelen /= sizeof(WCHAR); + if (classnamelen == sizeof(classname) / sizeof(WCHAR)) + classnamelen--; - filetype[filetypelen] = '\0'; - TRACE("File type: %s\n", debugstr_w(filetype)); + classname[classnamelen] = '\0'; + TRACE("File type: %s\n", debugstr_w(classname)); } else { - *filetype = '\0'; - filetypelen = 0; + *classname = '\0'; } } - if (*filetype) + if (*classname) { - /* pass the operation string to SHELL_FindExecutableByOperation() */ - filetype[filetypelen] = '\0'; - retval = SHELL_FindExecutableByOperation(lpOperation, key, filetype, command, sizeof(command)); + /* pass the verb string to SHELL_FindExecutableByVerb() */ + retval = SHELL_FindExecutableByVerb(lpVerb, key, classname, command, sizeof(command)); if (retval > 32) { DWORD finishedLen; - SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen); + SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen, lpPath); if (finishedLen > resultLen) ERR("Argify buffer not large enough.. truncated\n"); /* Remove double quotation marks and command line arguments */ @@ -874,20 +914,34 @@ static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec { WCHAR regkey[256]; WCHAR * endkey = regkey + wcslen(key); - WCHAR app[256], topic[256], ifexec[256], res[256]; + WCHAR app[256], topic[256], ifexec[256], static_res[256]; + WCHAR * dynamic_res=NULL; + WCHAR * res; LONG applen, topiclen, ifexeclen; WCHAR * exec; DWORD ddeInst = 0; DWORD tid; - DWORD resultLen; + DWORD resultLen, endkeyLen; HSZ hszApp, hszTopic; HCONV hConv; HDDEDATA hDdeData; unsigned ret = SE_ERR_NOASSOC; BOOL unicode = !(GetVersion() & 0x80000000); + if (strlenW(key) + 1 > sizeof(regkey) / sizeof(regkey[0])) + { + FIXME("input parameter %s larger than buffer\n", debugstr_w(key)); + return 2; + } wcscpy(regkey, key); - wcscpy(endkey, L"\\application"); + static const WCHAR wApplication[] = L"\\application"; + endkeyLen = sizeof(regkey) / sizeof(regkey[0]) - (endkey - regkey); + if (strlenW(wApplication) + 1 > endkeyLen) + { + FIXME("endkey %s overruns buffer\n", debugstr_w(wApplication)); + return 2; + } + wcscpy(endkey, wApplication); applen = sizeof(app); if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, app, &applen) != ERROR_SUCCESS) { @@ -900,6 +954,12 @@ static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec /* Get application command from start string and find filename of application */ if (*start == '"') { + 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; + } wcscpy(command, start + 1); if ((ptr = wcschr(command, '"'))) * ptr = 0; @@ -907,8 +967,9 @@ static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec } else { - LPWSTR p, space; - for (p = (LPWSTR)start; (space = const_cast(strchrW(p, ' '))); p = space + 1) + LPCWSTR p; + LPWSTR space; + for (p = start; (space = const_cast(strchrW(p, ' '))); p = space + 1) { int idx = space - start; memcpy(command, start, idx * sizeof(WCHAR)); @@ -925,6 +986,11 @@ static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec ERR("Unable to find application path for command %s\n", debugstr_w(start)); return ERROR_ACCESS_DENIED; } + if (strlenW(ptr) + 1 > sizeof(app) / sizeof(app[0])) + { + FIXME("size of found path %s larger than buffer\n", debugstr_w(ptr)); + return 2; + } wcscpy(app, ptr); /* Remove extensions (including .so) */ @@ -938,7 +1004,13 @@ static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec *ptr = 0; } - wcscpy(endkey, L"\\topic"); + static const WCHAR wTopic[] = L"\\topic"; + if (strlenW(wTopic) + 1 > endkeyLen) + { + FIXME("endkey %s overruns buffer\n", debugstr_w(wTopic)); + return 2; + } + wcscpy(endkey, wTopic); topiclen = sizeof(topic); if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, topic, &topiclen) != ERROR_SUCCESS) { @@ -978,7 +1050,13 @@ static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec SetLastError(ERROR_DDE_FAIL); return 30; /* whatever */ } - strcpyW(endkey, L"\\ifexec"); + static const WCHAR wIfexec[] = L"\\ifexec"; + if (strlenW(wIfexec) + 1 > endkeyLen) + { + FIXME("endkey %s overruns buffer\n", debugstr_w(wIfexec)); + return 2; + } + strcpyW(endkey, wIfexec); ifexeclen = sizeof(ifexec); if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, ifexec, &ifexeclen) == ERROR_SUCCESS) { @@ -986,9 +1064,14 @@ static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec } } - SHELL_ArgifyW(res, sizeof(res) / sizeof(WCHAR), exec, lpFile, pidl, szCommandline, &resultLen); - if (resultLen > sizeof(res) / sizeof(WCHAR)) - ERR("Argify buffer not large enough, truncated\n"); + SHELL_ArgifyW(static_res, sizeof(static_res)/sizeof(WCHAR), exec, lpFile, pidl, szCommandline, &resultLen, NULL); + if (resultLen > sizeof(static_res)/sizeof(WCHAR)) + { + res = dynamic_res = static_cast(HeapAlloc(GetProcessHeap(), 0, resultLen * sizeof(WCHAR))); + SHELL_ArgifyW(dynamic_res, resultLen, exec, lpFile, pidl, szCommandline, NULL, NULL); + } + else + res = static_res; TRACE("%s %s => %s\n", debugstr_w(exec), debugstr_w(lpFile), debugstr_w(res)); /* It's documented in the KB 330337 that IE has a bug and returns @@ -1011,6 +1094,8 @@ static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec WARN("DdeClientTransaction failed with error %04x\n", DdeGetLastError(ddeInst)); ret = 33; + HeapFree(GetProcessHeap(), 0, dynamic_res); + DdeDisconnect(hConv); error: @@ -1049,7 +1134,8 @@ static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, if (cmdlen >= sizeof(cmd) / sizeof(WCHAR)) cmdlen = sizeof(cmd) / sizeof(WCHAR) - 1; cmd[cmdlen] = '\0'; - SHELL_ArgifyW(param, sizeof(param) / sizeof(WCHAR), cmd, lpFile, (LPITEMIDLIST)psei->lpIDList, szCommandline, &resultLen); + SHELL_ArgifyW(param, sizeof(param) / sizeof(WCHAR), cmd, lpFile, (LPITEMIDLIST)psei->lpIDList, szCommandline, &resultLen, + (psei->lpDirectory && *psei->lpDirectory) ? psei->lpDirectory : NULL); if (resultLen > sizeof(param) / sizeof(WCHAR)) ERR("Argify buffer not large enough, truncating\n"); } @@ -1127,6 +1213,7 @@ HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpR { UINT_PTR retval = SE_ERR_NOASSOC; WCHAR old_dir[1024]; + WCHAR res[MAX_PATH]; TRACE("File %s, Dir %s\n", debugstr_w(lpFile), debugstr_w(lpDirectory)); @@ -1140,7 +1227,9 @@ HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpR SetCurrentDirectoryW(lpDirectory); } - retval = SHELL_FindExecutable(lpDirectory, lpFile, wszOpen, lpResult, MAX_PATH, NULL, NULL, NULL, NULL); + retval = SHELL_FindExecutable(lpDirectory, lpFile, wszOpen, res, MAX_PATH, NULL, NULL, NULL, NULL); + if (retval > 32) + strcpyW(lpResult, res); TRACE("returning %s\n", debugstr_w(lpResult)); if (lpDirectory) @@ -1200,8 +1289,8 @@ static HKEY ShellExecute_GetClassKey(const SHELLEXECUTEINFOW *sei) static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei ) { LPCITEMIDLIST pidllast = NULL; - IDataObject *dataobj = NULL; - IShellFolder *shf = NULL; + CComPtr dataobj; + CComPtr shf; LPITEMIDLIST pidl = NULL; HRESULT r; @@ -1220,25 +1309,22 @@ static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei ) pidl = ILCreateFromPathW(fullpath); } - r = SHBindToParent(pidl, IID_IShellFolder, (LPVOID*)&shf, &pidllast); + r = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); if (FAILED(r)) goto end; - shf->GetUIObjectOf(NULL, 1, &pidllast, - IID_IDataObject, NULL, (LPVOID*) &dataobj); + shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IDataObject, &dataobj)); end: if (pidl != sei->lpIDList) ILFree(pidl); - if (shf) - shf->Release(); - return dataobj; + return dataobj.Detach(); } static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, LPSHELLEXECUTEINFOW sei) { - IContextMenu *cm = NULL; + CComPtr cm = NULL; CMINVOKECOMMANDINFOEX ici; MENUITEMINFOW info; WCHAR string[0x80]; @@ -1248,7 +1334,7 @@ static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, TRACE("%p %p\n", obj, sei); - r = obj->QueryInterface(IID_IContextMenu, (LPVOID*) &cm); + r = obj->QueryInterface(IID_PPV_ARG(IContextMenu, &cm)); if (FAILED(r)) return r; @@ -1288,7 +1374,7 @@ static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, memset(&ici, 0, sizeof ici); ici.cbSize = sizeof ici; - ici.fMask = CMIC_MASK_UNICODE | (sei->fMask & (SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); + ici.fMask = CMIC_MASK_UNICODE | (sei->fMask & (SEE_MASK_NO_CONSOLE | SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); ici.nShow = sei->nShow; ici.lpVerb = MAKEINTRESOURCEA(def); ici.hwnd = sei->hwnd; @@ -1301,13 +1387,12 @@ static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, end: if (hmenu) DestroyMenu( hmenu ); - if (cm) - cm->Release(); return r; } static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei) { + // Can not use CComPtr here because of CoUninitialize at the end, before the destructors would run. IDataObject *dataobj = NULL; IObjectWithSite *ows = NULL; IShellExtInit *obj = NULL; @@ -1320,7 +1405,7 @@ static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECU goto end; r = CoCreateInstance(*guid, NULL, CLSCTX_INPROC_SERVER, - IID_IShellExtInit, (LPVOID*)&obj); + IID_PPV_ARG(IShellExtInit, &obj)); if (FAILED(r)) { ERR("failed %08x\n", r); @@ -1331,6 +1416,7 @@ static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECU if (!dataobj) { ERR("failed to get data object\n"); + r = E_FAIL; goto end; } @@ -1338,7 +1424,7 @@ static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECU if (FAILED(r)) goto end; - r = obj->QueryInterface(IID_IObjectWithSite, (LPVOID*) &ows); + r = obj->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows)); if (FAILED(r)) goto end; @@ -1403,34 +1489,62 @@ static LONG ShellExecute_FromContextMenu( LPSHELLEXECUTEINFOW sei ) return r; } +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); + static UINT_PTR SHELL_execute_class(LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) { - WCHAR execCmd[1024], wcmd[1024]; + WCHAR execCmd[1024], classname[1024]; /* 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; + UINT_PTR rslt; - 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, (LPITEMIDLIST)psei->lpIDList, NULL, &resultLen); - if (!done && wszApplicationName[0]) + /* FIXME: remove following block when SHELL_quote_and_execute supports hkeyClass parameter */ + if (cmask != SEE_MASK_CLASSNAME) { - strcatW(wcmd, L" "); - strcatW(wcmd, wszApplicationName); + WCHAR wcmd[1024]; + 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, (LPITEMIDLIST)psei->lpIDList, NULL, &resultLen, + (psei->lpDirectory && *psei->lpDirectory) ? psei->lpDirectory : NULL); + if (!done && wszApplicationName[0]) + { + strcatW(wcmd, L" "); + if (*wszApplicationName != '"') + { + strcatW(wcmd, L"\""); + strcatW(wcmd, wszApplicationName); + strcatW(wcmd, L"\""); + } + else + strcatW(wcmd, wszApplicationName); + } + if (resultLen > sizeof(wcmd) / sizeof(WCHAR)) + ERR("Argify buffer not large enough... truncating\n"); + return execfunc(wcmd, NULL, FALSE, psei, psei_out); } - if (resultLen > sizeof(wcmd) / sizeof(WCHAR)) - ERR("Argify buffer not large enough... truncating\n"); - return execfunc(wcmd, NULL, FALSE, psei, psei_out); + + strcpyW(classname, psei->lpClass); + rslt = SHELL_FindExecutableByVerb(psei->lpVerb, NULL, classname, execCmd, sizeof(execCmd)); + + TRACE("SHELL_FindExecutableByVerb returned %u (%s, %s)\n", (unsigned int)rslt, debugstr_w(classname), debugstr_w(execCmd)); + if (33 > rslt) + return rslt; + rslt = SHELL_quote_and_execute( execCmd, L"", classname, + wszApplicationName, NULL, psei, + psei_out, execfunc ); + return rslt; + } static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen) @@ -1440,7 +1554,7 @@ static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters BOOL appKnownSingular = FALSE; /* last chance to translate IDList: now also allow CLSID paths */ - if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW((LPCITEMIDLIST)sei->lpIDList, buffer, sizeof(buffer)))) { + if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW((LPCITEMIDLIST)sei->lpIDList, buffer, sizeof(buffer)/sizeof(WCHAR)))) { if (buffer[0] == ':' && buffer[1] == ':') { /* open shell folder for the specified class GUID */ if (strlenW(buffer) + 1 > parametersLen) @@ -1468,7 +1582,8 @@ static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters sei->lpVerb, buffer, sizeof(buffer))) { SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen, - buffer, target, (LPITEMIDLIST)sei->lpIDList, NULL, &resultLen); + buffer, target, (LPITEMIDLIST)sei->lpIDList, NULL, &resultLen, + (sei->lpDirectory && *sei->lpDirectory) ? sei->lpDirectory : NULL); if (resultLen > dwApplicationNameLen) ERR("Argify buffer not large enough... truncating\n"); appKnownSingular = FALSE; @@ -1479,7 +1594,7 @@ static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters return appKnownSingular; } -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) +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) { UINT_PTR retval; DWORD len; @@ -1505,17 +1620,17 @@ static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPC strcatW(wszQuotedCmd, wszParameters); } - TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(lpstrProtocol)); + TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(wszKeyname)); - if (*lpstrProtocol) - retval = execute_from_key(lpstrProtocol, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out); + if (*wszKeyname) + retval = execute_from_key(wszKeyname, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out); else retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out); HeapFree(GetProcessHeap(), 0, wszQuotedCmd); return retval; } -static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) +static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) { static const WCHAR wShell[] = L"\\shell\\"; static const WCHAR wCommand[] = L"\\command"; @@ -1532,9 +1647,9 @@ static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wFile, LPCWSTR wcmd, L iSize = strlenW(lpFile); TRACE("Got URL: %s\n", debugstr_w(lpFile)); - /* Looking for ...protocol\shell\lpOperation\command */ + /* Looking for ...\shell\\command */ len = iSize + lstrlenW(wShell) + lstrlenW(wCommand) + 1; - if (psei->lpVerb) + if (psei->lpVerb && *psei->lpVerb) len += lstrlenW(psei->lpVerb); else len += lstrlenW(wszOpen); @@ -1542,30 +1657,22 @@ static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wFile, LPCWSTR wcmd, L memcpy(lpstrProtocol, lpFile, iSize * sizeof(WCHAR)); lpstrProtocol[iSize] = '\0'; strcatW(lpstrProtocol, wShell); - strcatW(lpstrProtocol, psei->lpVerb ? psei->lpVerb : wszOpen); + strcatW(lpstrProtocol, psei->lpVerb && *psei->lpVerb ? psei->lpVerb : wszOpen); strcatW(lpstrProtocol, wCommand); - /* Remove File Protocol from lpFile */ - /* In the case file://path/file */ - if (!strncmpiW(lpFile, wFile, iSize)) - { - lpFile += iSize; - while (*lpFile == ':') lpFile++; - } retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters, wcmd, execfunc, psei, psei_out); HeapFree(GetProcessHeap(), 0, lpstrProtocol); return retval; } -void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename) +static void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename) { WCHAR msg[2048]; DWORD_PTR msgArguments[3] = { (DWORD_PTR)filename, 0, 0 }; DWORD error_code; error_code = GetLastError(); - if (retval == SE_ERR_NOASSOC) LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, sizeof(msg) / sizeof(WCHAR)); else @@ -1580,10 +1687,30 @@ void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename) MessageBoxW(hwnd, msg, NULL, MB_ICONERROR); } +static WCHAR *expand_environment( const WCHAR *str ) +{ + WCHAR *buf; + DWORD len; + + len = ExpandEnvironmentStringsW(str, NULL, 0); + if (!len) return NULL; + + buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!buf) return NULL; + + len = ExpandEnvironmentStringsW(str, buf, len); + if (!len) + { + HeapFree(GetProcessHeap(), 0, buf); + return NULL; + } + return buf; +} + /************************************************************************* * SHELL_execute [Internal] */ -BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) +static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) { static const DWORD unsupportedFlags = SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY | @@ -1600,7 +1727,7 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) SHELLEXECUTEINFOW sei_tmp; /* modifiable copy of SHELLEXECUTEINFO struct */ WCHAR wfileName[MAX_PATH]; WCHAR *env; - WCHAR lpstrProtocol[256]; + WCHAR wszKeyname[256]; LPCWSTR lpFile; UINT_PTR retval = SE_ERR_NOASSOC; BOOL appKnownSingular = FALSE; @@ -1623,17 +1750,16 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen * sizeof(WCHAR)); *wszApplicationName = '\0'; } - else if (*sei_tmp.lpFile == '\"') + else if (*sei_tmp.lpFile == '\"' && sei_tmp.lpFile[(len = strlenW(sei_tmp.lpFile))-1] == '\"') { - DWORD l = strlenW(sei_tmp.lpFile + 1); - if(l >= dwApplicationNameLen) - dwApplicationNameLen = l + 1; + if(len-1 >= dwApplicationNameLen) + dwApplicationNameLen = len; wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen * sizeof(WCHAR)); - memcpy(wszApplicationName, sei_tmp.lpFile + 1, (l + 1)*sizeof(WCHAR)); + memcpy(wszApplicationName, sei_tmp.lpFile + 1, len * sizeof(WCHAR)); - if (wszApplicationName[l-1] == L'\"') - wszApplicationName[l-1] = L'\0'; + if(len > 2) + wszApplicationName[len-2] = '\0'; appKnownSingular = TRUE; TRACE("wszApplicationName=%s\n", debugstr_w(wszApplicationName)); @@ -1687,16 +1813,14 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) /* process the IDList */ if (sei_tmp.fMask & SEE_MASK_IDLIST) { - IShellExecuteHookW* pSEH; + CComPtr pSEH; - HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_IShellExecuteHookW, (LPVOID*)&pSEH, NULL); + HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_PPV_ARG(IShellExecuteHookW, &pSEH), NULL); if (SUCCEEDED(hr)) { hr = pSEH->Execute(&sei_tmp); - pSEH->Release(); - if (hr == S_OK) { HeapFree(GetProcessHeap(), 0, wszApplicationName); @@ -1713,6 +1837,27 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName)); } + if (sei_tmp.fMask & SEE_MASK_DOENVSUBST) + { + WCHAR *tmp; + + tmp = expand_environment(sei_tmp.lpFile); + if (!tmp) + { + return FALSE; + } + HeapFree(GetProcessHeap(), 0, wszApplicationName); + sei_tmp.lpFile = wszApplicationName = tmp; + + tmp = expand_environment(sei_tmp.lpDirectory); + if (!tmp) + { + return FALSE; + } + if (wszDir != dirBuffer) HeapFree(GetProcessHeap(), 0, wszDir); + sei_tmp.lpDirectory = wszDir = tmp; + } + if (ERROR_SUCCESS == ShellExecute_FromContextMenu(&sei_tmp)) { sei->hInstApp = (HINSTANCE) 33; @@ -1739,6 +1884,7 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK) + DBG_UNREFERENCED_LOCAL_VARIABLE(Info); do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName); } HeapFree(GetProcessHeap(), 0, wszApplicationName); @@ -1758,36 +1904,38 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) dwApplicationNameLen ); } - /* expand environment strings */ - len = ExpandEnvironmentStringsW(sei_tmp.lpFile, NULL, 0); - if (len > 0) + /* convert file URLs */ + if (UrlIsFileUrlW(sei_tmp.lpFile)) { LPWSTR buf; - buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); + DWORD size; + + size = MAX_PATH; + buf = static_cast(HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR))); + if (!buf || FAILED(PathCreateFromUrlW(sei_tmp.lpFile, buf, &size, 0))) + { + HeapFree(GetProcessHeap(), 0, buf); + return SE_ERR_OOM; + } - ExpandEnvironmentStringsW(sei_tmp.lpFile, buf, len + 1); HeapFree(GetProcessHeap(), 0, wszApplicationName); - dwApplicationNameLen = len + 1; wszApplicationName = buf; - /* appKnownSingular unmodified */ - sei_tmp.lpFile = wszApplicationName; } - - if (*sei_tmp.lpParameters) + else /* or expand environment strings (not both!) */ { - len = ExpandEnvironmentStringsW(sei_tmp.lpParameters, NULL, 0); + len = ExpandEnvironmentStringsW(sei_tmp.lpFile, NULL, 0); if (len > 0) { LPWSTR buf; - len++; - buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); - ExpandEnvironmentStringsW(sei_tmp.lpParameters, buf, len); - if (wszParameters != parametersBuffer) - HeapFree(GetProcessHeap(), 0, wszParameters); - wszParameters = buf; - parametersLen = len; - sei_tmp.lpParameters = wszParameters; + buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); + + ExpandEnvironmentStringsW(sei_tmp.lpFile, buf, len + 1); + HeapFree(GetProcessHeap(), 0, wszApplicationName); + wszApplicationName = buf; + /* appKnownSingular unmodified */ + + sei_tmp.lpFile = wszApplicationName; } } @@ -1830,7 +1978,7 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) { end = ++src; - while(isspace(*src)) + while(isspaceW(*src)) ++src; } else @@ -1841,6 +1989,7 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) /* terminate previous command string after the quote character */ *end = L'\0'; + lpFile = wfileName; } else { @@ -1872,13 +2021,11 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) } } - lstrcpynW(wfileName, sei_tmp.lpFile, sizeof(wfileName) / sizeof(WCHAR)); + lpFile = sei_tmp.lpFile; } } else - lstrcpynW(wfileName, sei_tmp.lpFile, sizeof(wfileName) / sizeof(WCHAR)); - - lpFile = wfileName; + lpFile = sei_tmp.lpFile; wcmd = wcmdBuffer; len = lstrlenW(wszApplicationName) + 3; @@ -1911,10 +2058,10 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) /* Else, try to find the executable */ wcmd[0] = L'\0'; - retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, lpstrProtocol, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters); + retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, wszKeyname, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters); if (retval > 32) /* Found */ { - retval = SHELL_quote_and_execute(wcmd, wszParameters, lpstrProtocol, + retval = SHELL_quote_and_execute(wcmd, wszParameters, wszKeyname, wszApplicationName, env, &sei_tmp, sei, execfunc); HeapFree(GetProcessHeap(), 0, env); @@ -1933,7 +2080,7 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) { swprintf(lpQuotedFile, L"\"%s\"", lpFile); retval = SHELL_quote_and_execute(wExec, lpQuotedFile, - lpstrProtocol, + wszKeyname, wszApplicationName, env, &sei_tmp, sei, execfunc); HeapFree(GetProcessHeap(), 0, env); @@ -1945,12 +2092,12 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) } else if (PathIsURLW(lpFile)) /* File not found, check for URL */ { - retval = SHELL_execute_url(lpFile, L"file", wcmd, &sei_tmp, sei, execfunc ); + retval = SHELL_execute_url(lpFile, wcmd, &sei_tmp, sei, execfunc ); } /* Check if file specified is in the form www.??????.*** */ else if (!strncmpiW(lpFile, L"www", 3)) { - /* if so, append lpFile http:// and call ShellExecute */ + /* if so, prefix lpFile with http:// and call ShellExecute */ WCHAR lpstrTmpFile[256]; strcpyW(lpstrTmpFile, L"http://"); strcatW(lpstrTmpFile, lpFile); @@ -1971,6 +2118,7 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK) + DBG_UNREFERENCED_LOCAL_VARIABLE(Info); do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName); } @@ -1990,19 +2138,19 @@ BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) /************************************************************************* * ShellExecuteA [SHELL32.290] */ -HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation, LPCSTR lpFile, +HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd) { SHELLEXECUTEINFOA sei; TRACE("%p,%s,%s,%s,%s,%d\n", - hWnd, debugstr_a(lpOperation), debugstr_a(lpFile), + hWnd, debugstr_a(lpVerb), debugstr_a(lpFile), debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd); sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_FLAG_NO_UI; sei.hwnd = hWnd; - sei.lpVerb = lpOperation; + sei.lpVerb = lpVerb; sei.lpFile = lpFile; sei.lpParameters = lpParameters; sei.lpDirectory = lpDirectory; @@ -2021,7 +2169,10 @@ HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation, LPCSTR lpFile, * ShellExecuteExA [SHELL32.292] * */ -BOOL WINAPI ShellExecuteExA(LPSHELLEXECUTEINFOA sei) +BOOL +WINAPI +DECLSPEC_HOTPATCH +ShellExecuteExA(LPSHELLEXECUTEINFOA sei) { SHELLEXECUTEINFOW seiW; BOOL ret; @@ -2068,7 +2219,10 @@ BOOL WINAPI ShellExecuteExA(LPSHELLEXECUTEINFOA sei) * ShellExecuteExW [SHELL32.293] * */ -BOOL WINAPI ShellExecuteExW(LPSHELLEXECUTEINFOW sei) +BOOL +WINAPI +DECLSPEC_HOTPATCH +ShellExecuteExW(LPSHELLEXECUTEINFOW sei) { return SHELL_execute(sei, SHELL_ExecuteW); } @@ -2076,10 +2230,10 @@ BOOL WINAPI ShellExecuteExW(LPSHELLEXECUTEINFOW sei) /************************************************************************* * ShellExecuteW [SHELL32.294] * from shellapi.h - * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, + * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd); */ -HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, +HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd) { SHELLEXECUTEINFOW sei; @@ -2088,7 +2242,7 @@ HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_FLAG_NO_UI; sei.hwnd = hwnd; - sei.lpVerb = lpOperation; + sei.lpVerb = lpVerb; sei.lpFile = lpFile; sei.lpParameters = lpParameters; sei.lpDirectory = lpDirectory; @@ -2108,14 +2262,14 @@ HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, * * FIXME: the callback function most likely doesn't work the same way on Windows. */ -EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpOperation, LPCSTR lpFile, +EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd, void *callback) { SHELLEXECUTEINFOW seiW; WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL; HANDLE hProcess = 0; - seiW.lpVerb = lpOperation ? __SHCloneStrAtoW(&wVerb, lpOperation) : NULL; + seiW.lpVerb = lpVerb ? __SHCloneStrAtoW(&wVerb, lpVerb) : NULL; seiW.lpFile = lpFile ? __SHCloneStrAtoW(&wFile, lpFile) : NULL; seiW.lpParameters = lpParameters ? __SHCloneStrAtoW(&wParameters, lpParameters) : NULL; seiW.lpDirectory = lpDirectory ? __SHCloneStrAtoW(&wDirectory, lpDirectory) : NULL; @@ -2140,17 +2294,211 @@ EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpOperation, LPCSTR } /************************************************************************* - * OpenAs_RunDLLA [SHELL32.@] + * OpenAs_RunDLLW [SHELL32.@] */ -EXTERN_C void WINAPI OpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow) +EXTERN_C void WINAPI +OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow) { - FIXME("%p, %p, %s, %d\n", hwnd, hinst, debugstr_a(cmdline), cmdshow); + OPENASINFO info; + TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow); + + ZeroMemory(&info, sizeof(info)); + info.pcszFile = cmdline; + info.pcszClass = NULL; + info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC; + + SHOpenWithDialog(hwnd, &info); } /************************************************************************* - * OpenAs_RunDLLW [SHELL32.@] + * OpenAs_RunDLLA [SHELL32.@] */ -EXTERN_C void WINAPI OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow) +EXTERN_C void WINAPI +OpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow) +{ + LPWSTR pszCmdLineW = NULL; + TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_a(cmdline), cmdshow); + + if (cmdline) + __SHCloneStrAtoW(&pszCmdLineW, cmdline); + OpenAs_RunDLLW(hwnd, hinst, pszCmdLineW, cmdshow); + SHFree(pszCmdLineW); +} + +/*************************************************************************/ + +static LPCWSTR +SplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0) { - FIXME("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow); + LPCWSTR pch; + size_t ich = 0; + if (*psz == L'"') + { + // 1st argument is quoted. the string in quotes is quoted 1st argument. + // [pch] --> [pszArg0+ich] + for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch) + { + if (*pch == L'"' && pch[1] == L'"') + { + // doubled double quotations found! + pszArg0[ich] = L'"'; + } + else if (*pch == L'"') + { + // single double quotation found! + ++pch; + break; + } + else + { + // otherwise + pszArg0[ich] = *pch; + } + } + } + else + { + // 1st argument is unquoted. non-space sequence is 1st argument. + // [pch] --> [pszArg0+ich] + for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch) + { + pszArg0[ich] = *pch; + } + } + pszArg0[ich] = 0; + + // skip space + while (iswspace(*pch)) + ++pch; + + return pch; +} + +HRESULT WINAPI ShellExecCmdLine( + HWND hwnd, + LPCWSTR pwszCommand, + LPCWSTR pwszStartDir, + int nShow, + LPVOID pUnused, + DWORD dwSeclFlags) +{ + SHELLEXECUTEINFOW info; + DWORD dwSize, dwError, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC; + LPCWSTR pszVerb = NULL; + WCHAR szFile[MAX_PATH], szFile2[MAX_PATH]; + HRESULT hr; + LPCWSTR pchParams; + LPWSTR lpCommand = NULL; + + if (pwszCommand == NULL) + RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, + 1, (ULONG_PTR*)pwszCommand); + + __SHCloneStrW(&lpCommand, pwszCommand); + StrTrimW(lpCommand, L" \t"); + + if (dwSeclFlags & SECL_NO_UI) + dwFlags |= SEE_MASK_FLAG_NO_UI; + if (dwSeclFlags & SECL_LOG_USAGE) + dwFlags |= SEE_MASK_FLAG_LOG_USAGE; + if (dwSeclFlags & SECL_USE_IDLIST) + dwFlags |= SEE_MASK_INVOKEIDLIST; + + if (dwSeclFlags & SECL_RUNAS) + { + dwSize = 0; + hr = AssocQueryStringW(0, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize); + if (SUCCEEDED(hr) && dwSize != 0) + { + pszVerb = L"runas"; + } + } + + if (UrlIsFileUrlW(lpCommand)) + { + StringCchCopyW(szFile, _countof(szFile), lpCommand); + pchParams = NULL; + } + else + { + pchParams = SplitParams(lpCommand, szFile, _countof(szFile)); + if (SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL) || + SearchPathW(NULL, szFile, wszExe, _countof(szFile2), szFile2, NULL) || + SearchPathW(NULL, szFile, wszCom, _countof(szFile2), szFile2, NULL) || + SearchPathW(pwszStartDir, szFile, NULL, _countof(szFile2), szFile2, NULL) || + SearchPathW(pwszStartDir, szFile, wszExe, _countof(szFile2), szFile2, NULL) || + SearchPathW(pwszStartDir, szFile, wszCom, _countof(szFile2), szFile2, NULL)) + { + StringCchCopyW(szFile, _countof(szFile), szFile2); + pchParams = NULL; + } + else if (SearchPathW(NULL, lpCommand, NULL, _countof(szFile2), szFile2, NULL) || + SearchPathW(NULL, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) || + SearchPathW(NULL, lpCommand, wszCom, _countof(szFile2), szFile2, NULL) || + SearchPathW(pwszStartDir, lpCommand, NULL, _countof(szFile2), szFile2, NULL) || + SearchPathW(pwszStartDir, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) || + SearchPathW(pwszStartDir, lpCommand, wszCom, _countof(szFile2), szFile2, NULL)) + { + StringCchCopyW(szFile, _countof(szFile), szFile2); + pchParams = NULL; + } + + if (!(dwSeclFlags & SECL_ALLOW_NONEXE)) + { + if (!GetBinaryTypeW(szFile, &dwType)) + { + SHFree(lpCommand); + + if (!(dwSeclFlags & SECL_NO_UI)) + { + WCHAR szText[128 + MAX_PATH], szFormat[128]; + LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); + StringCchPrintfW(szText, _countof(szText), szFormat, szFile); + MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); + } + return CO_E_APPNOTFOUND; + } + } + else + { + if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES) + { + SHFree(lpCommand); + + if (!(dwSeclFlags & SECL_NO_UI)) + { + WCHAR szText[128 + MAX_PATH], szFormat[128]; + LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); + StringCchPrintfW(szText, _countof(szText), szFormat, szFile); + MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); + } + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } + } + + ZeroMemory(&info, sizeof(info)); + info.cbSize = sizeof(info); + info.fMask = dwFlags; + info.hwnd = hwnd; + info.lpVerb = pszVerb; + info.lpFile = szFile; + info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL; + info.lpDirectory = pwszStartDir; + info.nShow = nShow; + if (ShellExecuteExW(&info)) + { + if (info.lpIDList) + CoTaskMemFree(info.lpIDList); + + SHFree(lpCommand); + + return S_OK; + } + + dwError = GetLastError(); + + SHFree(lpCommand); + + return HRESULT_FROM_WIN32(dwError); }