*
* 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
*/
#include "precomp.h"
+#include <undocshell.h>
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)
* - 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;
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;
static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize)
{
STRRET strret;
- IShellFolder *desktop;
+ CComPtr<IShellFolder> desktop;
HRESULT hr = SHGetDesktopFolder(&desktop);
if (SUCCEEDED(hr))
StrRetToStrNW(pszPath, uOutSize, &strret, pidl);
-
- desktop->Release();
}
return hr;
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);
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 */
}
}
- SHELL_ArgifyW(static_res, sizeof(static_res)/sizeof(WCHAR), exec, lpFile, pidl, szCommandline, &resultLen);
+ 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<WCHAR *>(HeapAlloc(GetProcessHeap(), 0, resultLen * sizeof(WCHAR)));
- SHELL_ArgifyW(dynamic_res, resultLen, exec, lpFile, pidl, szCommandline, NULL);
+ SHELL_ArgifyW(dynamic_res, resultLen, exec, lpFile, pidl, szCommandline, NULL, NULL);
}
else
res = static_res;
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");
}
{
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));
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)
static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei )
{
LPCITEMIDLIST pidllast = NULL;
- IDataObject *dataobj = NULL;
- IShellFolder *shf = NULL;
+ CComPtr<IDataObject> dataobj;
+ CComPtr<IShellFolder> shf;
LPITEMIDLIST pidl = NULL;
HRESULT r;
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<IContextMenu> cm = NULL;
CMINVOKECOMMANDINFOEX ici;
MENUITEMINFOW info;
WCHAR string[0x80];
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;
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;
if (!dataobj)
{
ERR("failed to get data object\n");
+ r = E_FAIL;
goto end;
}
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;
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);
+ 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" ");
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;
DWORD error_code;
error_code = GetLastError();
-
if (retval == SE_ERR_NOASSOC)
LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, sizeof(msg) / sizeof(WCHAR));
else
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]
*/
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));
/* process the IDList */
if (sei_tmp.fMask & SEE_MASK_IDLIST)
{
- IShellExecuteHookW* pSEH;
+ CComPtr<IShellExecuteHookW> pSEH;
HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_PPV_ARG(IShellExecuteHookW, &pSEH), NULL);
{
hr = pSEH->Execute(&sei_tmp);
- pSEH->Release();
-
if (hr == S_OK)
{
HeapFree(GetProcessHeap(), 0, wszApplicationName);
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;
}
HeapFree(GetProcessHeap(), 0, wszApplicationName);
- dwApplicationNameLen = lstrlenW(buf) + 1;
wszApplicationName = buf;
sei_tmp.lpFile = wszApplicationName;
}
ExpandEnvironmentStringsW(sei_tmp.lpFile, buf, len + 1);
HeapFree(GetProcessHeap(), 0, wszApplicationName);
- dwApplicationNameLen = len + 1;
wszApplicationName = buf;
/* appKnownSingular unmodified */
{
end = ++src;
- while(isspace(*src))
+ while(isspaceW(*src))
++src;
}
else
}
/*************************************************************************
- * 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 (PathIsURLW(lpCommand) || UrlIsW(lpCommand, URLIS_APPLIABLE))
+ {
+ 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);
+ }
+ 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);
}