diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp b/dll/win32/shell32/CDefaultContextMenu.cpp index f30ef2caae8..817f6ab2cc9 100644 --- a/dll/win32/shell32/CDefaultContextMenu.cpp +++ b/dll/win32/shell32/CDefaultContextMenu.cpp @@ -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, 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 diff --git a/dll/win32/shell32/COpenWithMenu.cpp b/dll/win32/shell32/COpenWithMenu.cpp index 7aab4771f39..918d629d524 100644 --- a/dll/win32/shell32/COpenWithMenu.cpp +++ b/dll/win32/shell32/COpenWithMenu.cpp @@ -1222,7 +1222,6 @@ VOID COpenWithMenu::AddApp(PVOID pApp) mii.fState = MFS_ENABLED; mii.wID = m_idCmdLast; mii.dwTypeData = const_cast(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);