[SHELL32][CMD][SHLWAPI] Use the openas verb when invoking unknown file types (#7981)

CORE-20184
This commit is contained in:
Whindmar Saksit 2025-05-30 18:46:24 +02:00 committed by GitHub
parent 6dd8e04438
commit d663eb4466
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 249 additions and 170 deletions

View file

@ -266,10 +266,10 @@ typedef BOOL (WINAPI *MYEX)(LPSHELLEXECUTEINFO lpExecInfo);
HANDLE RunFile(DWORD flags, LPTSTR filename, LPTSTR params,
LPTSTR directory, INT show)
{
SHELLEXECUTEINFO sei;
SHELLEXECUTEINFO sei = { sizeof(sei), flags | SEE_MASK_FLAG_DDEWAIT };
HMODULE hShell32;
MYEX hShExt;
BOOL ret;
UINT err;
TRACE ("RunFile(%s)\n", debugstr_aw(filename));
hShell32 = LoadLibrary(_T("SHELL32.DLL"));
@ -289,19 +289,17 @@ HANDLE RunFile(DWORD flags, LPTSTR filename, LPTSTR params,
TRACE ("RunFile: ShellExecuteExA/W is at %x\n", hShExt);
memset(&sei, 0, sizeof sei);
sei.cbSize = sizeof sei;
sei.fMask = flags;
sei.lpFile = filename;
sei.lpParameters = params;
sei.lpDirectory = directory;
sei.nShow = show;
ret = hShExt(&sei);
TRACE ("RunFile: ShellExecuteExA/W returned 0x%p\n", ret);
err = hShExt(&sei) ? ERROR_SUCCESS : GetLastError();
TRACE ("RunFile: ShellExecuteExA/W returned error %#x\n", err);
FreeLibrary(hShell32);
return ret ? sei.hProcess : NULL;
SetLastError(err);
return err ? NULL : sei.hProcess;
}
@ -431,6 +429,7 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
/* exec the program */
PROCESS_INFORMATION prci;
STARTUPINFO stui;
UINT execerror = ERROR_FILE_NOT_FOUND;
/* build command line for CreateProcess(): FullName + " " + rest */
BOOL quoted = !!_tcschr(First, _T(' '));
@ -450,8 +449,8 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
memset(&stui, 0, sizeof(stui));
stui.cb = sizeof(stui);
stui.lpTitle = Full;
stui.dwFlags = STARTF_USESHOWWINDOW;
stui.wShowWindow = SW_SHOWDEFAULT;
stui.dwFlags = 0;
stui.wShowWindow = SW_SHOWNORMAL;
/* Set the console to standard mode */
SetConsoleMode(ConStreamGetOSHandle(StdIn),
@ -470,14 +469,12 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
{
CloseHandle(prci.hThread);
}
else
else if (GetLastError() == ERROR_BAD_EXE_FORMAT)
{
// See if we can run this with ShellExecute() ie myfile.xls
prci.hProcess = RunFile(SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE,
szFullName,
rest,
NULL,
SW_SHOWNORMAL);
HANDLE hProcess = RunFile(SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE,
szFullName, rest, NULL, SW_SHOWNORMAL);
execerror = hProcess ? ERROR_SUCCESS : GetLastError();
}
*FirstEnd = _T('\0');
@ -499,10 +496,13 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
}
CloseHandle(prci.hProcess);
}
else
else if (execerror)
{
TRACE ("[ShellExecute failed!: %s]\n", debugstr_aw(Full));
error_bad_command(first);
if (execerror == ERROR_NO_ASSOCIATION)
error_cant_exec_program();
else
error_bad_command(first);
dwExitCode = 1;
}

View file

@ -216,6 +216,7 @@ VOID error_parameter_format(TCHAR ch);
VOID error_invalid_switch(TCHAR ch);
VOID error_invalid_parameter_format(PCTSTR s);
VOID error_out_of_memory(VOID);
VOID error_cant_exec_program(VOID);
VOID error_syntax(PCTSTR s);
VOID msg_pause(VOID);

View file

@ -141,6 +141,13 @@ VOID error_out_of_memory(VOID)
nErrorLevel = 1;
}
VOID error_cant_exec_program(VOID)
{
/* TODO: Windows uses the custom string "The system cannot execute the specified program" here */
ErrorMessage(ERROR_NO_ASSOCIATION, NULL);
nErrorLevel = 1;
}
VOID error_invalid_parameter_format(PCWSTR s)
{

View file

@ -5,8 +5,8 @@ Signature = "$Windows NT$"
HKLM,"SOFTWARE\Classes",,0x00000010
; Default key
HKCR,"*","",0x00000000,""
HKCR,"SystemFileAssociations","",0x00000000,""
HKCR,"*","AlwaysShowExt",0x00000000,""
HKCR,"SystemFileAssociations",,0x00000010
; Open With List
HKCR,"*\OpenWithList\excel.exe","",0x00000000,""
@ -16,6 +16,12 @@ HKCR,"*\OpenWithList\notepad.exe","",0x00000000,""
HKCR,"*\OpenWithList\winword.exe","",0x00000000,""
HKCR,"*\OpenWithList\wordpad.exe","",0x00000000,""
; Unknown
HKCR,"Unknown","AlwaysShowExt"
HKCR,"Unknown","QueryClassStore"
HKCR,"Unknown\shell","",,"openas"
HKCR,"Unknown\shell\openas\command","",0x00020000,"%SystemRoot%\system32\rundll32.exe %SystemRoot%\system32\shell32.dll,OpenAs_RunDLL %1"
; Folders
HKCR,"Folder","",0x00000000,"Folder"
;HKCR,"Folder\DefaultIcon","",0x00000000,"%SystemRoot%\system32\shell32.dll,-4"

View file

@ -58,10 +58,10 @@ static const struct _StaticInvokeCommandMap_
SHORT DfmCmd;
} g_StaticInvokeCmdMap[] =
{
{ "RunAs", 0 }, // Unimplemented
{ "Print", 0 }, // Unimplemented
{ "Preview", 0 }, // Unimplemented
{ "Open", FCIDM_SHVIEW_OPEN },
{ "runas", 0 }, // Unimplemented
{ "print", 0 }, // Unimplemented
{ "preview", 0 }, // Unimplemented
{ "open", FCIDM_SHVIEW_OPEN },
{ CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER, (SHORT)DFM_CMD_NEWFOLDER },
{ "cut", FCIDM_SHVIEW_CUT, /* ? */ },
{ "copy", FCIDM_SHVIEW_COPY, (SHORT)DFM_CMD_COPY },
@ -139,6 +139,36 @@ EXTERN_C HRESULT SHELL32_EnumDefaultVerbList(LPCWSTR List, UINT Index, LPWSTR Ve
return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
}
static HRESULT GetFriendlyVerb(_In_ PCWSTR pszVerb, _Out_ PWSTR pszBuf, _In_ SIZE_T cchMax)
{
static const struct { PCWSTR pszVerb; WORD iResId; } map[] =
{
// { L"open", IDS_OPEN_VERB }, These two have already been handled
// { L"explore", IDS_EXPLORE_VERB },
{ L"edit", IDS_EDIT_VERB },
{ L"print", IDS_PRINT_VERB },
{ L"runas", IDS_RUNAS_VERB },
{ L"openas", IDS_OPEN_VERB },
{ L"find", IDS_FIND_VERB },
};
for (SIZE_T i = 0; i < _countof(map); ++i)
{
if (!_wcsicmp(pszVerb, map[i].pszVerb) &&
LoadStringW(shell32_hInstance, map[i].iResId, pszBuf, cchMax))
{
return S_OK;
}
}
// Try to make a friendly verb based on the verb subkey
if (pszVerb[0] < 127 && !StrChrW(pszVerb, '&') && SUCCEEDED(StringCchCopyW(pszBuf + 1, --cchMax, pszVerb)))
{
*pszBuf = L'&';
return S_OK; // This can be changed to S_FALSE if the caller needs to know we faked it
}
return E_FAIL;
}
class CDefaultContextMenu :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IContextMenu3,
@ -572,9 +602,8 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
{
UINT ntver = RosGetProcessEffectiveVersion();
MENUITEMINFOW mii = { sizeof(mii) };
UINT idResource;
WCHAR wszDispVerb[80]; // The limit on XP. If the friendly string is longer, it falls back to the verb key.
UINT fState;
UINT fState, idVerbRes;
UINT cIds = 0, indexFirst = *pIndexMenu, indexDefault;
int iDefVerbIndex = -1;
@ -587,6 +616,7 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
{
StaticShellEntry& info = m_StaticEntries.GetNext(it);
BOOL forceFirstPos = FALSE;
bool hide = false;
fState = MFS_ENABLED;
@ -599,86 +629,59 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
if (info.Verb.CompareNoCase(L"open") == 0)
{
idResource = IDS_OPEN_VERB;
idVerbRes = IDS_OPEN_VERB; // TODO: This string should include '&'
fState |= MFS_DEFAULT; /* override default when open verb is found */
forceFirstPos++;
}
else if (info.Verb.CompareNoCase(L"explore") == 0)
{
idResource = IDS_EXPLORE_VERB;
idVerbRes = IDS_EXPLORE_VERB; // TODO: This string should include '&'
if (uFlags & CMF_EXPLORE)
{
fState |= MFS_DEFAULT;
forceFirstPos++;
}
}
else if (info.Verb.CompareNoCase(L"runas") == 0)
idResource = IDS_RUNAS_VERB;
else if (info.Verb.CompareNoCase(L"edit") == 0)
idResource = IDS_EDIT_VERB;
else if (info.Verb.CompareNoCase(L"find") == 0)
idResource = IDS_FIND_VERB;
else if (info.Verb.CompareNoCase(L"print") == 0)
idResource = IDS_PRINT_VERB;
else if (info.Verb.CompareNoCase(L"printto") == 0)
continue;
hide = true;
else
idResource = 0;
idVerbRes = 0;
/* By default use verb for menu item name */
mii.dwTypeData = (LPWSTR)info.Verb.GetString();
WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
HRESULT hr;
hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString());
HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString());
if (FAILED_UNEXPECTEDLY(hr))
{
continue;
}
hide = true;
UINT cmdFlags = 0;
bool hide = false;
HKEY hkVerb;
if (idResource > 0)
if (RegOpenKeyExW(info.hkClass, wszKey, 0, KEY_READ, &hkVerb) == ERROR_SUCCESS)
{
if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
{
if (LoadStringW(shell32_hInstance, idResource, wszDispVerb, _countof(wszDispVerb)))
mii.dwTypeData = wszDispVerb; /* use translated verb */
else
ERR("Failed to load string\n");
}
DWORD cbVerb = sizeof(wszDispVerb);
LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL);
if (res || !*wszDispVerb)
res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL);
if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) != ERROR_SUCCESS)
hkVerb = NULL;
if ((res == ERROR_SUCCESS && *wszDispVerb) ||
(idVerbRes && LoadStringW(shell32_hInstance, idVerbRes, wszDispVerb, _countof(wszDispVerb))) ||
SUCCEEDED(GetFriendlyVerb(info.Verb, wszDispVerb, _countof(wszDispVerb))))
{
mii.dwTypeData = wszDispVerb;
}
}
}
else
{
if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) == ERROR_SUCCESS)
{
if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
{
DWORD cbVerb = sizeof(wszDispVerb);
LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL);
if (res || !*wszDispVerb)
res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL);
if (res == ERROR_SUCCESS && *wszDispVerb)
{
/* use description for the menu entry */
mii.dwTypeData = wszDispVerb;
}
}
}
else
{
hkVerb = NULL;
}
hkVerb = NULL;
}
if (hkVerb)
{
if (!(uFlags & CMF_EXTENDEDVERBS))
if (!hide && !(uFlags & CMF_EXTENDEDVERBS))
hide = RegValueExists(hkVerb, L"Extended");
if (!hide)
@ -687,6 +690,9 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
if (!hide && !(uFlags & CMF_DISABLEDVERBS))
hide = RegValueExists(hkVerb, L"LegacyDisable");
if (DWORD dwRest = (hide ? 0 : RegGetDword(hkVerb, NULL, L"SuppressionPolicy", 0)))
hide = SHRestricted((RESTRICTIONS)dwRest);
if (RegValueExists(hkVerb, L"NeverDefault"))
fState &= ~MFS_DEFAULT;
@ -924,6 +930,11 @@ CDefaultContextMenu::QueryContextMenu(
// TODO: DFM_MERGECONTEXTMENU_TOP
// TODO: Remove duplicate verbs. This will be easier when the static items handling
// has been moved to CLSID_ShellFileDefExt so we only have to deal with ShellEx.
// This is a Windows XP+ feature. On an unknown file type, Windows 2000 will
// display both "Open" (openas from Unknown) and "Open with..." (openas from *).
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
}
@ -1278,8 +1289,6 @@ CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic
LPCWSTR FlagsName;
WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
HRESULT hr;
DWORD wFlags;
DWORD cbVerb;
if (!m_site)
return 0;
@ -1306,14 +1315,7 @@ CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic
hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->Verb.GetString());
if (FAILED_UNEXPECTEDLY(hr))
return 0;
cbVerb = sizeof(wFlags);
if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
{
return wFlags;
}
return 0;
return RegGetDword(pEntry->hkClass, wszKey, FlagsName, 0);
}
HRESULT

View file

@ -81,6 +81,67 @@ HRESULT SHELL32_GetDllFromRundll32CommandLine(LPCWSTR pszCmd, LPWSTR pszOut, SIZ
return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
}
static HRESULT SH32_EvaluateValidExecApp(_Inout_ PWSTR pszCmd, _In_ SIZE_T cchMax)
{
// FIXME: SHEvaluateSystemCommandTemplate is not implemented yet, using a minimal version.
if (!PathGetAppFromCommandLine(pszCmd, pszCmd, cchMax))
return E_FAIL;
UINT fPRF = PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS | PRF_DONTFINDLNK;
WCHAR szCurrDir[MAX_PATH];
LPCWSTR pszDirsArr[2] = { szCurrDir, NULL }, *ppszDirs = NULL;
if (GetCurrentDirectoryW(_countof(szCurrDir), szCurrDir))
ppszDirs = pszDirsArr;
if (PathResolveW(pszCmd, ppszDirs, fPRF | (ppszDirs ? PRF_FIRSTDIRDEF : 0)))
return S_OK;
return E_FAIL;
}
HRESULT SH32_InvokeOpenWith(_In_ PCWSTR pszPath, _In_ LPCMINVOKECOMMANDINFO pici, _Out_ HANDLE *phProcess)
{
if (!pszPath || !pici)
return HResultFromWin32(ERROR_INVALID_PARAMETER);
HRESULT hr = HResultFromWin32(ERROR_NO_ASSOCIATION);
SHELLEXECUTEINFOW sei = { sizeof(sei), CmicFlagsToSeeFlags(pici->fMask), pici->hwnd };
sei.fMask |= SEE_MASK_CLASSKEY | SEE_MASK_NOZONECHECKS;
sei.lpFile = pszPath;
sei.nShow = pici->nShow;
if (phProcess)
{
sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
sei.hProcess = NULL;
}
if (!RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Unknown", 0, KEY_READ, &sei.hkeyClass))
{
// Use the internal dialog only if HKCR\Unknown\shell\openas\command exists but is invalid.
WCHAR szCmd[MAX_PATH * 2];
DWORD cch = _countof(szCmd);
hr = AssocQueryStringByKeyW(ASSOCF_NOTRUNCATE | ASSOCF_NOFIXUPS |
ASSOCF_IGNOREBASECLASS | ASSOCF_INIT_IGNOREUNKNOWN,
ASSOCSTR_COMMAND, sei.hkeyClass, NULL, szCmd, &cch);
if (SUCCEEDED(hr) && FAILED(SH32_EvaluateValidExecApp(szCmd, _countof(szCmd))))
{
OPENASINFO info = { pszPath, NULL, OAIF_EXEC | OAIF_REGISTER_EXT | OAIF_ALLOW_REGISTRATION };
hr = SHOpenWithDialog(sei.hwnd, &info);
}
else
{
hr = ShellExecuteExW(&sei) ? S_OK : HResultFromWin32(GetLastError());
}
RegCloseKey(sei.hkeyClass);
}
else if (!(pici->fMask & CMIC_MASK_FLAG_NO_UI))
{
SHELL_ErrorBox(sei.hwnd, hr);
}
if (phProcess)
*phProcess = sei.hProcess;
return hr;
}
class COpenWithList
{
public:
@ -865,6 +926,7 @@ class COpenWithDialog
const OPENASINFO *m_pInfo;
COpenWithList *m_pAppList;
UINT m_InFlags;
BOOL m_bListAllocated;
HWND m_hDialog, m_hTreeView;
HTREEITEM m_hRecommend;
@ -1031,13 +1093,22 @@ VOID COpenWithDialog::Init(HWND hwnd)
m_hDialog = hwnd;
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)this);
UINT fDisallow = 0;
PCWSTR pszExt = PathFindExtensionW(m_pInfo->pcszFile);
// Don't allow registration for "" nor "." nor ".exe" etc.
if (!pszExt || !pszExt[0] || !pszExt[1] || PathIsExeW(m_pInfo->pcszFile))
fDisallow |= OAIF_ALLOW_REGISTRATION | OAIF_FORCE_REGISTRATION;
if (SHRestricted(REST_NOFILEASSOCIATE))
fDisallow |= OAIF_ALLOW_REGISTRATION | OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT;
/* Handle register checkbox */
m_InFlags = m_pInfo->oaifInFlags & ~fDisallow;
HWND hRegisterCheckbox = GetDlgItem(hwnd, 14003);
if (!(m_pInfo->oaifInFlags & OAIF_ALLOW_REGISTRATION))
if (!(m_InFlags & OAIF_ALLOW_REGISTRATION))
EnableWindow(hRegisterCheckbox, FALSE);
if (m_pInfo->oaifInFlags & OAIF_FORCE_REGISTRATION)
if (m_InFlags & OAIF_FORCE_REGISTRATION)
SendMessage(hRegisterCheckbox, BM_SETCHECK, BST_CHECKED, 0);
if (m_pInfo->oaifInFlags & OAIF_HIDE_REGISTRATION)
if (m_InFlags & OAIF_HIDE_REGISTRATION)
ShowWindow(hRegisterCheckbox, SW_HIDE);
if (m_pInfo->pcszFile)
@ -1111,7 +1182,7 @@ VOID COpenWithDialog::Accept()
if (pApp)
{
/* Set programm as default handler */
if (IsDlgButtonChecked(m_hDialog, 14003) == BST_CHECKED)
if (IsDlgButtonChecked(m_hDialog, 14003) == BST_CHECKED && (m_InFlags & OAIF_REGISTER_EXT))
{
m_pAppList->SetDefaultHandler(pApp, m_pInfo->pcszFile);
// FIXME: Update DefaultIcon registry
@ -1119,7 +1190,7 @@ VOID COpenWithDialog::Accept()
}
/* Execute program */
if (m_pInfo->oaifInFlags & OAIF_EXEC)
if (m_InFlags & OAIF_EXEC)
m_pAppList->Execute(pApp, m_pInfo->pcszFile);
EndDialog(m_hDialog, 1);
@ -1394,15 +1465,7 @@ COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
if (m_idCmdFirst + LOWORD(lpici->lpVerb) == idChooseApp)
{
DoChooseApp:
OPENASINFO info;
LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
info.pcszFile = m_wszPath;
info.oaifInFlags = OAIF_EXEC;
if (pwszExt[0])
info.oaifInFlags |= OAIF_REGISTER_EXT | OAIF_ALLOW_REGISTRATION;
info.pcszClass = NULL;
hr = SHOpenWithDialog(lpici->hwnd, &info);
hr = SH32_InvokeOpenWith(m_wszPath, lpici, NULL);
}
else
{

View file

@ -591,7 +591,7 @@ HRESULT CQueryAssociations::GetValue(HKEY hkey, const WCHAR *name, void **data,
DWORD size;
LONG ret;
ret = RegQueryValueExW(hkey, name, 0, NULL, NULL, &size);
ret = SHQueryValueExW(hkey, name, 0, NULL, NULL, &size);
if (ret != ERROR_SUCCESS)
return HRESULT_FROM_WIN32(ret);
@ -602,7 +602,7 @@ HRESULT CQueryAssociations::GetValue(HKEY hkey, const WCHAR *name, void **data,
if (!*data)
return E_OUTOFMEMORY;
ret = RegQueryValueExW(hkey, name, 0, NULL, (LPBYTE)*data, &size);
ret = SHQueryValueExW(hkey, name, 0, NULL, (LPBYTE)*data, &size);
if (ret != ERROR_SUCCESS)
{
HeapFree(GetProcessHeap(), 0, *data);

View file

@ -1480,6 +1480,7 @@ BOOL SHELL_FS_HideExtension(LPCWSTR szPath)
}
}
}
// TODO: else if "AlwaysShowExt"
return doHide;
}

View file

@ -47,6 +47,19 @@ static BOOL SHELL_InRunDllProcess(VOID)
return s_bInDllProcess;
}
static UINT_PTR InvokeOpenWith(HWND hWndOwner, SHELLEXECUTEINFOW &sei)
{
extern HRESULT SH32_InvokeOpenWith(PCWSTR, LPCMINVOKECOMMANDINFO, HANDLE *);
HANDLE *phProc = (sei.fMask & SEE_MASK_NOCLOSEPROCESS) ? &sei.hProcess : NULL;
UINT fCmic = (sei.fMask & SEE_CMIC_COMMON_BASICFLAGS) | CMIC_MASK_FLAG_NO_UI;
CMINVOKECOMMANDINFO ici = { sizeof(ici), fCmic, hWndOwner };
ici.nShow = SW_SHOW;
HRESULT hr = SH32_InvokeOpenWith(sei.lpFile, &ici, phProc);
SetLastError(ERROR_NO_ASSOCIATION);
return SUCCEEDED(hr) ? 42 : SE_ERR_NOASSOC;
}
static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum)
{
bool firstCharQuote = false;
@ -697,14 +710,14 @@ static UINT SHELL_FindExecutableByVerb(LPCWSTR lpVerb, LPWSTR key, LPWSTR classn
HKEY hkeyClass;
WCHAR verb[MAX_PATH];
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, 0x02000000, &hkeyClass))
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, KEY_READ, &hkeyClass))
return SE_ERR_NOASSOC;
if (!HCR_GetDefaultVerbW(hkeyClass, lpVerb, verb, ARRAY_SIZE(verb)))
return SE_ERR_NOASSOC;
RegCloseKey(hkeyClass);
/* Looking for ...buffer\shell\<verb>\command */
wcscat(classname, L"\\shell\\");
wcscat(classname, L"\\shell\\"); // FIXME: Use HCR_GetExecuteCommandW or AssocAPI
wcscat(classname, verb);
wcscat(classname, L"\\command");
@ -1361,48 +1374,21 @@ HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpR
/* FIXME: is this already implemented somewhere else? */
static HKEY ShellExecute_GetClassKey(const SHELLEXECUTEINFOW *sei)
{
LPCWSTR ext = NULL, lpClass = NULL;
CHeapPtr<WCHAR, CLocalAllocator> cls;
DWORD type = 0, sz = 0;
HKEY hkey = 0;
LONG r;
if (sei->fMask & SEE_MASK_CLASSALL)
if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSKEY)
return sei->hkeyClass;
HKEY hKey = NULL;
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.Allocate(sz / sizeof(WCHAR));
cls[0] = 0;
RegQueryValueExW(hkey, NULL, 0, &type, (LPBYTE)(LPWSTR)cls, &sz);
}
RegCloseKey( hkey );
lpClass = cls;
TRACE("class = %s\n", debugstr_w(sei->lpClass));
RegOpenKeyExW(HKEY_CLASSES_ROOT, sei->lpClass, 0, KEY_READ, &hKey);
return hKey;
}
TRACE("class = %s\n", debugstr_w(lpClass));
hkey = 0;
if (lpClass)
RegOpenKeyW( HKEY_CLASSES_ROOT, lpClass, &hkey);
return hkey;
PCWSTR ext = PathFindExtensionW(sei->lpFile);
TRACE("ext = %s\n", debugstr_w(ext));
if (!StrIsNullOrEmpty(ext) && SUCCEEDED(HCR_GetProgIdKeyOfExtension(ext, &hKey, FALSE)))
return hKey;
return NULL;
}
static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei, CComPtr<IDataObject>& dataObj)
@ -1410,7 +1396,7 @@ static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei, CComPtr<IDataObject
CComHeapPtr<ITEMIDLIST> allocatedPidl;
LPITEMIDLIST pidl = NULL;
if (sei->fMask & SEE_MASK_CLASSALL)
if (sei->fMask & SEE_MASK_CLASSALL) // FIXME: This makes no sense? SEE_MASK_IDLIST?
{
pidl = (LPITEMIDLIST)sei->lpIDList;
}
@ -1680,7 +1666,7 @@ static LONG ShellExecute_FromContextMenuHandlers( 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_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR wszKeyname, 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)
{
@ -1905,7 +1891,7 @@ static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEIN
if (psei->lpVerb && *psei->lpVerb)
len += lstrlenW(psei->lpVerb);
else
len += lstrlenW(L"open");
len += lstrlenW(L"open"); // FIXME: Use HCR_GetExecuteCommandW or AssocAPI
lpstrProtocol.Allocate(len);
memcpy(lpstrProtocol, lpFile, iSize * sizeof(WCHAR));
lpstrProtocol[iSize] = '\0';
@ -1919,13 +1905,12 @@ static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEIN
return retval;
}
static void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename)
static void do_error_dialog(UINT_PTR retval, HWND hwnd, PCWSTR filename)
{
WCHAR msg[2048];
DWORD_PTR msgArguments[3] = { (DWORD_PTR)filename, 0, 0 };
DWORD error_code;
const DWORD error_code = GetLastError();
error_code = GetLastError();
if (retval == SE_ERR_NOASSOC)
LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, ARRAY_SIZE(msg));
else
@ -1938,6 +1923,7 @@ static void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename)
(va_list*)msgArguments);
MessageBoxW(hwnd, msg, NULL, MB_ICONERROR);
SetLastError(error_code); // Restore
}
static WCHAR *expand_environment( const WCHAR *str )
@ -1971,6 +1957,7 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
BOOL appKnownSingular = FALSE;
/* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */
sei->hProcess = NULL;
SHELLEXECUTEINFOW sei_tmp = *sei;
TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n",
@ -1980,8 +1967,6 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ?
debugstr_w(sei_tmp.lpClass) : "not used");
sei->hProcess = NULL;
/* make copies of all path/command strings */
CHeapPtr<WCHAR, CLocalAllocator> wszApplicationName;
DWORD dwApplicationNameLen = MAX_PATH + 2;
@ -2330,7 +2315,8 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
/* if so, prefix lpFile with http:// and call ShellExecute */
WCHAR lpstrTmpFile[256];
strcpyW(lpstrTmpFile, L"http://");
strcatW(lpstrTmpFile, lpFile);
strcatW(lpstrTmpFile, lpFile); // FIXME: Possible buffer overflow
// FIXME: This will not correctly return the hProcess to the caller
retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0);
}
@ -2338,18 +2324,10 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
{
OPENASINFO Info;
//FIXME
// need full path
Info.pcszFile = wszApplicationName;
Info.pcszClass = NULL;
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);
if (retval == SE_ERR_NOASSOC && !(sei->fMask & SEE_MASK_CLASSALL))
retval = InvokeOpenWith(sei_tmp.hwnd, *sei);
if (retval <= 32)
do_error_dialog(retval, sei_tmp.hwnd, lpFile);
}
sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval);
@ -2393,7 +2371,11 @@ static DWORD
ShellExecute_Normal(_Inout_ LPSHELLEXECUTEINFOW sei)
{
// FIXME
return SHELL_execute(sei, SHELL_ExecuteW) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
if (SHELL_execute(sei, SHELL_ExecuteW))
return ERROR_SUCCESS;
DWORD err = GetLastError();
assert(err);
return err ? err : ERROR_FILE_NOT_FOUND;
}
static VOID
@ -2592,14 +2574,8 @@ EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFil
EXTERN_C void WINAPI
OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow)
{
OPENASINFO info;
OPENASINFO info = { cmdline, NULL, OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC };
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);
}
@ -2700,7 +2676,7 @@ HRESULT WINAPI ShellExecCmdLine(
if (dwSeclFlags & SECL_RUNAS)
{
dwSize = 0;
hr = AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize);
hr = AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_COMMAND, lpCommand, L"runas", NULL, &dwSize);
if (SUCCEEDED(hr) && dwSize != 0)
{
pszVerb = L"runas";

View file

@ -54,6 +54,20 @@ RegKeyExists(HKEY hKey, LPCWSTR Path)
return ret;
}
inline UINT
RegQueryDword(HKEY hKey, PCWSTR pszPath, PCWSTR pszName, DWORD *pnVal)
{
DWORD cb = sizeof(*pnVal);
return RegGetValueW(hKey, pszPath, pszName, RRF_RT_REG_DWORD, NULL, pnVal, &cb);
}
inline DWORD
RegGetDword(HKEY hKey, PCWSTR pszPath, PCWSTR pszName, DWORD nDefVal)
{
DWORD nVal;
return RegQueryDword(hKey, pszPath, pszName, &nVal) == ERROR_SUCCESS ? nVal : nDefVal;
}
inline DWORD
RegSetOrDelete(HKEY hKey, LPCWSTR Name, DWORD Type, LPCVOID Data, DWORD Size)
{

View file

@ -40,7 +40,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell);
/* Default IQueryAssociations::Init() flags */
#ifdef __REACTOS__
#define SHLWAPI_DEF_ASSOCF (ASSOCF_INIT_BYEXENAME | ASSOCF_INIT_DEFAULTTOSTAR | \
ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_NOREMAPCLSID)
ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_NOREMAPCLSID | \
ASSOCF_INIT_IGNOREUNKNOWN)
#else
#define SHLWAPI_DEF_ASSOCF (ASSOCF_INIT_BYEXENAME|ASSOCF_INIT_DEFAULTTOSTAR| \
ASSOCF_INIT_DEFAULTTOFOLDER)
@ -377,8 +378,12 @@ HRESULT WINAPI AssocQueryKeyW(ASSOCF cfFlags, ASSOCKEY assockey, LPCWSTR pszAsso
hRet = AssocCreate( CLSID_QueryAssociations, &IID_IQueryAssociations, (void **)&lpAssoc );
if (FAILED(hRet)) return hRet;
#ifdef __REACTOS__
hRet = IQueryAssociations_Init(lpAssoc, cfFlags & SHLWAPI_DEF_ASSOCF, pszAssoc, NULL, NULL);
#else
cfFlags &= SHLWAPI_DEF_ASSOCF;
hRet = IQueryAssociations_Init(lpAssoc, cfFlags, pszAssoc, NULL, NULL);
#endif
if (SUCCEEDED(hRet))
hRet = IQueryAssociations_GetKey(lpAssoc, cfFlags, assockey, pszExtra, phkeyOut);
@ -542,8 +547,12 @@ HRESULT WINAPI AssocQueryStringByKeyW(ASSOCF cfFlags, ASSOCSTR str, HKEY hkAssoc
hRet = AssocCreate( CLSID_QueryAssociations, &IID_IQueryAssociations, (void **)&lpAssoc );
if (FAILED(hRet)) return hRet;
#ifdef __REACTOS__
hRet = IQueryAssociations_Init(lpAssoc, cfFlags & SHLWAPI_DEF_ASSOCF, 0, hkAssoc, NULL);
#else
cfFlags &= SHLWAPI_DEF_ASSOCF;
hRet = IQueryAssociations_Init(lpAssoc, cfFlags, 0, hkAssoc, NULL);
#endif
if (SUCCEEDED(hRet))
hRet = IQueryAssociations_GetString(lpAssoc, cfFlags, str, pszExtra,