[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

@ -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