[SHELL32] Implement the desktop folder menu for Explorer tree (#7026)
authorWhindmar Saksit <whindsaks@proton.me>
Fri, 21 Jun 2024 17:19:49 +0000 (19:19 +0200)
committerGitHub <noreply@github.com>
Fri, 21 Jun 2024 17:19:49 +0000 (19:19 +0200)
Enumerates but does not actually allow you to get CF_HDROP if the desktop PIDL is present in the dataobject.

dll/win32/shell32/CDefaultContextMenu.cpp
dll/win32/shell32/CIDLDataObj.cpp
dll/win32/shell32/brfolder.cpp
dll/win32/shell32/folders/CDesktopFolder.cpp
dll/win32/shell32/shfldr.h
dll/win32/shell32/shlfolder.cpp

index f051c79..f30ef2c 100644 (file)
@@ -207,6 +207,11 @@ CDefaultContextMenu::CDefaultContextMenu() :
 
 CDefaultContextMenu::~CDefaultContextMenu()
 {
+    for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;)
+    {
+        const DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
+        IUnknown_SetSite(info.pCM.p, NULL);
+    }
     m_DynamicEntries.RemoveAll();
     m_StaticEntries.RemoveAll();
 
@@ -389,7 +394,7 @@ CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid)
         return hr;
     }
 
-    hr = pExtInit->Initialize(m_pidlFolder, m_pDataObj, hKey);
+    hr = pExtInit->Initialize(m_pDataObj ? NULL : m_pidlFolder, m_pDataObj, hKey);
     if (FAILED(hr))
     {
         WARN("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr);
@@ -459,7 +464,7 @@ CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey)
             }
         }
 
-        hr = LoadDynamicContextMenuHandler(hKey, clsid);
+        hr = LoadDynamicContextMenuHandler(hRootKey, clsid);
         if (FAILED(hr))
             WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid);
     }
index 1c2744d..4e390d8 100644 (file)
@@ -140,6 +140,7 @@ private:
     CSimpleArray<STGMEDIUM> m_Storage;
     UINT m_cfShellIDList;
     BOOL m_doasync;
+    bool m_FailGetHDrop;
 public:
     CIDLDataObj();
     ~CIDLDataObj();
@@ -173,6 +174,7 @@ CIDLDataObj::CIDLDataObj()
 {
     m_cfShellIDList = 0;
     m_doasync = FALSE;
+    m_FailGetHDrop = false;
 }
 
 CIDLDataObj::~CIDLDataObj()
@@ -205,6 +207,15 @@ HRESULT WINAPI CIDLDataObj::Initialize(HWND hwndOwner, PCIDLIST_ABSOLUTE pMyPidl
     HRESULT hr = SetData(&Format, &medium, TRUE);
     if (!FAILED_UNEXPECTEDLY(hr) && bAddAdditionalFormats)
     {
+        /* The Windows default shell IDataObject::GetData fails with DV_E_CLIPFORMAT if the desktop is present.
+         * Windows does return HDROP in EnumFormatEtc and does not fail until GetData is called.
+         * Failing GetData causes 7-Zip 23.01 to not add its menu to the desktop folder. */
+        for (UINT i = 0; i < cidlx; ++i)
+        {
+            if (ILIsEmpty(apidlx[i]) && ILIsEmpty(pMyPidl))
+                m_FailGetHDrop = true;
+        }
+
         Format.cfFormat = CF_HDROP;
         medium.hGlobal = RenderHDROP((LPITEMIDLIST)pMyPidl, (LPITEMIDLIST*)apidlx, cidlx);
         hr = SetData(&Format, &medium, TRUE);
@@ -245,6 +256,9 @@ HRESULT WINAPI CIDLDataObj::GetData(LPFORMATETC pformatetcIn, STGMEDIUM *pmedium
             fmt.dwAspect == pformatetcIn->dwAspect &&
             fmt.tymed == pformatetcIn->tymed)
         {
+            if (m_FailGetHDrop && fmt.cfFormat == CF_HDROP)
+                return DV_E_CLIPFORMAT;
+
             if (pformatetcIn->tymed != TYMED_HGLOBAL)
             {
                 UNIMPLEMENTED;
index 8158162..4a7b19b 100644 (file)
@@ -945,7 +945,7 @@ BrFolder_OnContextMenu(BrFolder &info, LPARAM lParam)
         return;
     info.pContextMenu = pcm;
     UINT cmf = ((GetKeyState(VK_SHIFT) < 0) ? CMF_EXTENDEDVERBS : 0) | CMF_CANRENAME;
-    hr = pcm->QueryContextMenu(hMenu, 0, ID_FIRSTCMD, ID_LASTCMD, CMF_NODEFAULT | cmf);
+    hr = pcm->QueryContextMenu(hMenu, 0, ID_FIRSTCMD, ID_LASTCMD, CMF_EXPLORE | cmf);
     if (hr > 0)
         _InsertMenuItemW(hMenu, 0, TRUE, 0, MFT_SEPARATOR, NULL, 0);
     _InsertMenuItemW(hMenu, 0, TRUE, IDC_TOGGLE, MFT_STRING,
@@ -961,8 +961,7 @@ BrFolder_OnContextMenu(BrFolder &info, LPARAM lParam)
     }
     else if (cmd != 0 && GetDfmCmd(pcm, ici.lpVerb) == DFM_CMD_RENAME)
     {
-        TreeView_SelectItem(info.hwndTreeView, hSelected);
-        TreeView_EditLabel(info.hwndTreeView, hSelected);
+        BrFolder_Rename(&info, hSelected);
     }
     else if (cmd != 0)
     {
index b98e65f..9051bc4 100644 (file)
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
+static BOOL IsSelf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
+{
+    return cidl == 0 || (cidl == 1 && apidl && _ILIsEmpty(apidl[0]));
+}
+
 STDMETHODIMP
 CDesktopFolder::ShellUrlParseDisplayName(
     HWND hwndOwner,
@@ -810,10 +815,10 @@ HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
 
     if (!ppvOut)
         return hr;
-
     *ppvOut = NULL;
 
-    if (cidl == 1 && !_ILIsSpecialFolder(apidl[0]))
+    BOOL self = IsSelf(cidl, apidl);
+    if (cidl == 1 && !_ILIsSpecialFolder(apidl[0]) && !self)
     {
         CComPtr<IShellFolder2> psf;
         HRESULT hr = _GetSFFromPidl(apidl[0], &psf);
@@ -825,7 +830,8 @@ HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
 
     if (IsEqualIID (riid, IID_IContextMenu))
     {
-        if (cidl > 0 && _ILIsSpecialFolder(apidl[0]))
+        // FIXME: m_regFolder vs AddFSClassKeysToArray is incorrect when the selection includes both regitems and FS items
+        if (!self && cidl > 0 && _ILIsSpecialFolder(apidl[0]))
         {
             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
         }
@@ -836,7 +842,12 @@ HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
             /* Otherwise operations like that involve items from both user and shared desktop will not work */
             HKEY hKeys[16];
             UINT cKeys = 0;
-            if (cidl > 0)
+            if (self)
+            {
+                AddClsidKeyToArray(CLSID_ShellDesktop, hKeys, &cKeys);
+                AddClassKeyToArray(L"Folder", hKeys, &cKeys);
+            }
+            else if (cidl > 0)
             {
                 AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys);
             }
@@ -1075,22 +1086,18 @@ HRESULT WINAPI CDesktopFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
 HRESULT WINAPI CDesktopFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
     enum { IDC_PROPERTIES };
-    /* no data object means no selection */
-    if (!pdtobj)
+    if (uMsg == DFM_INVOKECOMMAND && wParam == (pdtobj ? DFM_CMD_PROPERTIES : IDC_PROPERTIES))
     {
-        if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES)
-        {
-            return SHELL_ExecuteControlPanelCPL(hwndOwner, L"desk.cpl") ? S_OK : E_FAIL;
-        }
-        else if (uMsg == DFM_MERGECONTEXTMENU)
-        {
-            QCMINFO *pqcminfo = (QCMINFO *)lParam;
-            HMENU hpopup = CreatePopupMenu();
-            _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
-            pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
-            DestroyMenu(hpopup);
-            return S_OK;
-        }
+        return SHELL_ExecuteControlPanelCPL(hwndOwner, L"desk.cpl") ? S_OK : E_FAIL;
+    }
+    else if (uMsg == DFM_MERGECONTEXTMENU && !pdtobj) // Add Properties item when called for directory background
+    {
+        QCMINFO *pqcminfo = (QCMINFO *)lParam;
+        HMENU hpopup = CreatePopupMenu();
+        _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
+        pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
+        DestroyMenu(hpopup);
+        return S_OK;
     }
     return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
 }
index f2b1de4..c1ceb96 100644 (file)
@@ -113,6 +113,7 @@ void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags);
 BOOL SHELL_FS_HideExtension(LPCWSTR pwszPath);
 
 LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys);
+LSTATUS AddClsidKeyToArray(REFCLSID clsid, HKEY* array, UINT* cKeys);
 
 #ifdef __cplusplus
 
index a99f822..a6ea823 100644 (file)
@@ -289,6 +289,13 @@ LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys)
     return result;
 }
 
+LSTATUS AddClsidKeyToArray(REFCLSID clsid, HKEY* array, UINT* cKeys)
+{
+    WCHAR path[6 + 38 + 1] = L"CLSID\\";
+    StringFromGUID2(clsid, path + 6, 38 + 1);
+    return AddClassKeyToArray(path, array, cKeys);
+}
+
 void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys)
 {
     // This function opens the association array keys in canonical order for filesystem items.