[SHELL32] CDefaultContextMenu must respect the default verb list (#7090)

This commit is contained in:
Whindmar Saksit 2024-07-09 20:01:50 +02:00 committed by GitHub
parent 5b78381a53
commit 2e08238c7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 18 deletions

View file

@ -14,6 +14,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
// FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
#define MAX_VERB 260
#define VERBKEY_CCHMAX 64 // Note: 63+\0 seems to be the limit on XP
static HRESULT
SHELL_GetRegCLSID(HKEY hKey, LPCWSTR SubKey, LPCWSTR Value, CLSID &clsid)
@ -90,6 +91,27 @@ UINT MapVerbToDfmCmd(_In_ LPCSTR verba)
return 0;
}
static inline bool IsVerbListSeparator(WCHAR Ch)
{
return Ch == L' ' || Ch == L','; // learn.microsoft.com/en-us/windows/win32/shell/context-menu-handlers
}
static int FindVerbInDefaultVerbList(LPCWSTR List, LPCWSTR Verb)
{
for (UINT index = 0; *List; ++index)
{
while (IsVerbListSeparator(*List))
List++;
LPCWSTR Start = List;
while (*List && !IsVerbListSeparator(*List))
List++;
// "List > Start" to verify that the list item is non-empty to avoid the edge case where Verb is "" and the list contains ",,"
if (!_wcsnicmp(Verb, Start, List - Start) && List > Start)
return index;
}
return -1;
}
class CDefaultContextMenu :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IContextMenu3,
@ -119,6 +141,7 @@ class CDefaultContextMenu :
UINT m_iIdDfltFirst; /* first default part id */
UINT m_iIdDfltLast; /* last default part id */
HWND m_hwnd; /* window passed to callback */
WCHAR m_DefVerbs[MAX_PATH];
HRESULT _DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam);
void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags);
@ -203,6 +226,7 @@ CDefaultContextMenu::CDefaultContextMenu() :
m_iIdDfltLast(0),
m_hwnd(NULL)
{
*m_DefVerbs = UNICODE_NULL;
}
CDefaultContextMenu::~CDefaultContextMenu()
@ -314,7 +338,7 @@ void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVe
void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey, UINT uFlags)
{
WCHAR wszName[40];
WCHAR wszName[VERBKEY_CCHMAX];
DWORD cchName, dwIndex = 0;
HKEY hShellKey;
@ -322,6 +346,12 @@ void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey, UINT uFlags)
if (lres != STATUS_SUCCESS)
return;
if (!*m_DefVerbs)
{
DWORD cb = sizeof(m_DefVerbs);
RegGetValueW(hShellKey, NULL, NULL, RRF_RT_REG_SZ, NULL, m_DefVerbs, &cb);
}
while(TRUE)
{
cchName = _countof(wszName);
@ -513,9 +543,10 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
UINT ntver = RosGetProcessEffectiveVersion();
MENUITEMINFOW mii = { sizeof(mii) };
UINT idResource;
WCHAR wszVerb[40];
WCHAR wszDispVerb[80]; // The limit on XP. If the friendly string is longer, it falls back to the verb key.
UINT fState;
UINT cIds = 0, indexFirst = *pIndexMenu;
UINT cIds = 0, indexFirst = *pIndexMenu, indexDefault;
int iDefVerbIndex = -1;
mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
mii.fType = MFT_STRING;
@ -582,8 +613,8 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
{
if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
{
if (LoadStringW(shell32_hInstance, idResource, wszVerb, _countof(wszVerb)))
mii.dwTypeData = wszVerb; /* use translated verb */
if (LoadStringW(shell32_hInstance, idResource, wszDispVerb, _countof(wszDispVerb)))
mii.dwTypeData = wszDispVerb; /* use translated verb */
else
ERR("Failed to load string\n");
}
@ -597,16 +628,15 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
{
if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
{
DWORD cbVerb = sizeof(wszVerb);
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);
LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszVerb, cbVerb, NULL, 0, NULL);
if (res || !*wszVerb)
res = RegLoadMUIStringW(hkVerb, NULL, wszVerb, cbVerb, NULL, 0, NULL);
if (res == ERROR_SUCCESS && *wszVerb)
if (res == ERROR_SUCCESS && *wszDispVerb)
{
/* use description for the menu entry */
mii.dwTypeData = wszVerb;
mii.dwTypeData = wszDispVerb;
}
}
}
@ -652,9 +682,33 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
(*pIndexMenu)++;
}
UINT pos = *pIndexMenu;
int verbIndex = hkVerb ? FindVerbInDefaultVerbList(m_DefVerbs, info.Verb) : -1;
if (verbIndex >= 0)
{
if (verbIndex < iDefVerbIndex || iDefVerbIndex < 0)
{
iDefVerbIndex = verbIndex;
fState |= MFS_DEFAULT;
forceFirstPos = TRUE;
}
else
{
fState &= ~MFS_DEFAULT; // We have already set a better default
pos = indexDefault;
}
}
else if (iDefVerbIndex >= 0)
{
fState &= ~MFS_DEFAULT; // We have already set the default
if (forceFirstPos)
pos = indexDefault;
forceFirstPos = FALSE;
}
mii.fState = fState;
mii.wID = iIdCmdFirst + cIds;
if (InsertMenuItemW(hMenu, forceFirstPos ? indexFirst : *pIndexMenu, TRUE, &mii))
if (InsertMenuItemW(hMenu, forceFirstPos ? indexFirst : pos, TRUE, &mii))
(*pIndexMenu)++;
if (cmdFlags & ECF_SEPARATORAFTER)
@ -662,6 +716,9 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR))
(*pIndexMenu)++;
}
if (fState & MFS_DEFAULT)
indexDefault = *pIndexMenu; // This is where we want to insert "high priority" verbs
}
cIds++; // Always increment the id because it acts as the index into m_StaticEntries

View file

@ -1222,7 +1222,6 @@ VOID COpenWithMenu::AddApp(PVOID pApp)
mii.fState = MFS_ENABLED;
mii.wID = m_idCmdLast;
mii.dwTypeData = const_cast<LPWSTR>(pwszName);
mii.cch = wcslen(mii.dwTypeData);
mii.dwItemData = (ULONG_PTR)pApp;
HICON hIcon = m_pAppList->GetIcon((COpenWithList::SApp*)pApp);
@ -1259,7 +1258,7 @@ HRESULT WINAPI COpenWithMenu::QueryContextMenu(
m_idCmdFirst = m_idCmdLast = idCmdFirst;
m_hSubMenu = NULL;
/* If we are going to be default item, we shouldn't be submenu */
/* We can only be a submenu if we are not the default */
if (DefaultPos != -1)
{
/* Load applications list */
@ -1298,13 +1297,14 @@ HRESULT WINAPI COpenWithMenu::QueryContextMenu(
mii.fType = MFT_STRING;
mii.dwTypeData = (LPWSTR)wszName;
mii.cch = wcslen(wszName);
mii.fState = MFS_ENABLED;
if (DefaultPos == -1)
{
mii.fState |= MFS_DEFAULT;
indexMenu = 0;
}
if (!InsertMenuItemW(hMenu, DefaultPos + 1, TRUE, &mii))
if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
return E_FAIL;
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, m_idCmdLast - m_idCmdFirst + 1);