Reactos

[SHELL32][BROWSEUI] CopyMoveToMenu must initialize without a PIDL folder (#7047)

- IShellExtInit cannot rely on a folder PIDL being present
- Use common QueryContextMenu function for both menu items
- Allow copy to the same folder
- Disable toolbar buttons when the selection is not valid
- Minor DefView cache enhancement and bugfixes

authored by

Whindmar Saksit and committed by
GitHub
d24675b6 df5affed

+128 -145
+7
dll/win32/browseui/internettoolbar.cpp
··· 1362 1362 HRESULT STDMETHODCALLTYPE CInternetToolbar::SendToolbarMsg(const GUID *pguidCmdGroup, UINT uMsg, 1363 1363 WPARAM wParam, LPARAM lParam, LRESULT *result) 1364 1364 { 1365 + if (fToolbarWindow) 1366 + { 1367 + LRESULT res = ::SendMessageW(fToolbarWindow, uMsg, wParam, lParam); 1368 + if (result) 1369 + *result = res; 1370 + return S_OK; 1371 + } 1365 1372 return E_NOTIMPL; 1366 1373 } 1367 1374
+78 -131
dll/win32/shell32/CCopyMoveToMenu.cpp
··· 9 9 10 10 WINE_DEFAULT_DEBUG_CHANNEL(shell); 11 11 12 + enum { IDC_ACTION = 0 }; 13 + 12 14 CCopyMoveToMenu::CCopyMoveToMenu() : 13 - m_idCmdFirst(0), 14 - m_idCmdLast(0), 15 - m_idCmdAction(-1), 16 15 m_fnOldWndProc(NULL), 17 16 m_bIgnoreTextBoxChange(FALSE) 18 17 { ··· 106 105 SHGetPathFromIDListW(pidl, szPath); 107 106 108 107 if (ILIsEqual(pidl, this_->m_pidlFolder)) 109 - PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE); 108 + PostMessageW(hwnd, BFFM_ENABLEOK, 0, this_->GetFileOp() == FO_COPY); 110 109 else if (PathFileExistsW(szPath) || _ILIsDesktop(pidl)) 111 110 PostMessageW(hwnd, BFFM_ENABLEOK, 0, TRUE); 112 111 else ··· 122 121 } 123 122 124 123 HRESULT 125 - CCopyMoveToMenu::DoRealFileOp(LPCMINVOKECOMMANDINFO lpici, LPCITEMIDLIST pidl) 124 + CCopyMoveToMenu::DoRealFileOp(const CIDA *pCIDA, LPCMINVOKECOMMANDINFO lpici, PCUIDLIST_ABSOLUTE pidlDestination) 126 125 { 127 - CDataObjectHIDA pCIDA(m_pDataObject); 128 - if (FAILED_UNEXPECTEDLY(pCIDA.hr())) 129 - return pCIDA.hr(); 130 - 131 - PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(pCIDA); 132 - if (!pidlParent) 133 - { 134 - ERR("HIDA_GetPIDLFolder failed\n"); 135 - return E_FAIL; 136 - } 137 - 138 126 CStringW strFiles; 139 127 WCHAR szPath[MAX_PATH]; 140 128 for (UINT n = 0; n < pCIDA->cidl; ++n) 141 129 { 142 - PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(pCIDA, n); 143 - if (!pidlRelative) 144 - continue; 130 + CComHeapPtr<ITEMIDLIST> pidlCombine(SHELL_CIDA_ILCloneFull(pCIDA, n)); 131 + if (!pidlCombine) 132 + return E_FAIL; 145 133 146 - CComHeapPtr<ITEMIDLIST> pidlCombine(ILCombine(pidlParent, pidlRelative)); 147 - if (!pidl) 134 + if (!SHGetPathFromIDListW(pidlCombine, szPath)) 148 135 return E_FAIL; 149 - 150 - SHGetPathFromIDListW(pidlCombine, szPath); 151 136 152 137 if (n > 0) 153 138 strFiles += L'|'; ··· 157 142 strFiles += L'|'; // double null-terminated 158 143 strFiles.Replace(L'|', L'\0'); 159 144 160 - if (_ILIsDesktop(pidl)) 161 - SHGetSpecialFolderPathW(NULL, szPath, CSIDL_DESKTOPDIRECTORY, FALSE); 145 + if (_ILIsDesktop(pidlDestination)) 146 + SHGetSpecialFolderPathW(lpici->hwnd, szPath, CSIDL_DESKTOPDIRECTORY, TRUE); 162 147 else 163 - SHGetPathFromIDListW(pidl, szPath); 148 + SHGetPathFromIDListW(pidlDestination, szPath); 164 149 INT cchPath = lstrlenW(szPath); 165 150 if (cchPath + 1 < MAX_PATH) 166 151 { ··· 184 169 } 185 170 186 171 static HRESULT 187 - DoGetFileTitle(CStringW& strTitle, IDataObject *pDataObject) 172 + DoGetFileTitle(const CIDA *pCIDA, CStringW& strTitle) 188 173 { 189 - CDataObjectHIDA pCIDA(pDataObject); 190 - if (FAILED_UNEXPECTEDLY(pCIDA.hr())) 191 - return E_FAIL; 192 - 193 - PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(pCIDA); 194 - if (!pidlParent) 195 - { 196 - ERR("HIDA_GetPIDLFolder failed\n"); 197 - return E_FAIL; 198 - } 174 + CComHeapPtr<ITEMIDLIST> pidlCombine(SHELL_CIDA_ILCloneFull(pCIDA, 0)); 175 + if (!pidlCombine) 176 + return E_OUTOFMEMORY; 199 177 200 178 WCHAR szPath[MAX_PATH]; 201 - PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(pCIDA, 0); 202 - if (!pidlRelative) 203 - { 204 - ERR("HIDA_GetPIDLItem failed\n"); 205 - return E_FAIL; 206 - } 207 - 208 - CComHeapPtr<ITEMIDLIST> pidlCombine(ILCombine(pidlParent, pidlRelative)); 209 - 210 - if (!SHGetPathFromIDListW(pidlCombine, szPath)) 211 - { 212 - ERR("Cannot get path\n"); 213 - return E_FAIL; 214 - } 215 - 216 - strTitle = PathFindFileNameW(szPath); 179 + HRESULT hr = SHGetNameAndFlagsW(pidlCombine, SHGDN_INFOLDER, szPath, _countof(szPath), NULL); 180 + strTitle = SUCCEEDED(hr) ? szPath : L""; 217 181 if (strTitle.IsEmpty()) 218 182 return E_FAIL; 219 183 220 184 if (pCIDA->cidl > 1) 221 185 strTitle += L" ..."; 222 - 223 186 return S_OK; 224 187 } 225 188 226 189 HRESULT CCopyMoveToMenu::DoAction(LPCMINVOKECOMMANDINFO lpici) 227 190 { 228 191 WCHAR wszPath[MAX_PATH]; 229 - HRESULT hr = E_FAIL; 230 - 231 192 TRACE("(%p)\n", lpici); 232 193 194 + CDataObjectHIDA pCIDA(m_pDataObject); 195 + HRESULT hr = pCIDA.hr(); 196 + if (FAILED_UNEXPECTEDLY(hr)) 197 + { 198 + ERR("Failed to get CIDA, %#x\n", hr); 199 + return hr; 200 + } 201 + 233 202 if (!SHGetPathFromIDListW(m_pidlFolder, wszPath)) 234 203 { 235 204 ERR("SHGetPathFromIDListW failed\n"); 236 - return hr; 205 + return E_FAIL; 237 206 } 238 207 239 208 CStringW strFileTitle; 240 - hr = DoGetFileTitle(strFileTitle, m_pDataObject); 209 + hr = DoGetFileTitle(pCIDA, strFileTitle); 241 210 if (FAILED(hr)) 242 211 return hr; 243 212 ··· 252 221 info.lParam = reinterpret_cast<LPARAM>(this); 253 222 CComHeapPtr<ITEMIDLIST> pidl(SHBrowseForFolder(&info)); 254 223 if (pidl) 255 - hr = DoRealFileOp(lpici, pidl); 224 + hr = DoRealFileOp(pCIDA, lpici, pidl); 256 225 257 226 return hr; 258 227 } ··· 279 248 UINT idCmdLast, 280 249 UINT uFlags) 281 250 { 282 - MENUITEMINFOW mii; 283 - UINT Count = 0; 284 - 285 251 TRACE("CCopyToMenu::QueryContextMenu(%p, %u, %u, %u, %u)\n", 286 252 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 287 - 288 - if (uFlags & (CMF_NOVERBS | CMF_VERBSONLY)) 289 - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdFirst); 290 - 291 - m_idCmdFirst = m_idCmdLast = idCmdFirst; 292 - 293 - // insert separator if necessary 294 - ZeroMemory(&mii, sizeof(mii)); 295 - mii.cbSize = sizeof(mii); 296 - mii.fMask = MIIM_TYPE; 297 - if (GetPreviousMenuItemInfo(hMenu, indexMenu, &mii) && 298 - mii.fType != MFT_SEPARATOR) 299 - { 300 - ZeroMemory(&mii, sizeof(mii)); 301 - mii.cbSize = sizeof(mii); 302 - mii.fMask = MIIM_TYPE; 303 - mii.fType = MFT_SEPARATOR; 304 - if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 305 - { 306 - ++indexMenu; 307 - ++Count; 308 - } 309 - } 310 - 311 - // insert "Copy to folder..." 312 - CStringW strText(MAKEINTRESOURCEW(IDS_COPYTOMENU)); 313 - ZeroMemory(&mii, sizeof(mii)); 314 - mii.cbSize = sizeof(mii); 315 - mii.fMask = MIIM_ID | MIIM_TYPE; 316 - mii.fType = MFT_STRING; 317 - mii.dwTypeData = strText.GetBuffer(); 318 - mii.cch = wcslen(mii.dwTypeData); 319 - mii.wID = m_idCmdLast; 320 - if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 321 - { 322 - m_idCmdAction = m_idCmdLast++; 323 - ++indexMenu; 324 - ++Count; 325 - } 326 - 327 - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdFirst + Count); 253 + return QueryContextMenuImpl(TRUE, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 328 254 } 329 255 330 256 STDMETHODIMP ··· 334 260 UINT idCmdLast, 335 261 UINT uFlags) 336 262 { 337 - MENUITEMINFOW mii; 338 - UINT Count = 0; 339 - 340 263 TRACE("CMoveToMenu::QueryContextMenu(%p, %u, %u, %u, %u)\n", 341 264 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 265 + return QueryContextMenuImpl(FALSE, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 266 + } 342 267 268 + STDMETHODIMP 269 + CCopyMoveToMenu::QueryContextMenuImpl(BOOL IsCopyOp, HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 270 + { 343 271 if (uFlags & (CMF_NOVERBS | CMF_VERBSONLY)) 344 - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdFirst); 272 + return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); 345 273 346 - m_idCmdFirst = m_idCmdLast = idCmdFirst; 274 + UINT idHighest = 0; 275 + CStringW strCopyTo(MAKEINTRESOURCEW(IDS_COPYTOMENU)), strMoveTo; 276 + LPWSTR itemText = strCopyTo.GetBuffer(); 277 + if (!IsCopyOp) 278 + { 279 + strMoveTo.LoadString(IDS_MOVETOMENU); 280 + itemText = strMoveTo.GetBuffer(); 281 + } 347 282 348 - // insert separator if necessary 349 - CStringW strCopyTo(MAKEINTRESOURCEW(IDS_COPYTOMENU)); 283 + // Insert separator if necessary 350 284 WCHAR szBuff[128]; 351 - ZeroMemory(&mii, sizeof(mii)); 352 - mii.cbSize = sizeof(mii); 353 - mii.fMask = MIIM_TYPE; 285 + MENUITEMINFOW mii = { sizeof(mii), MIIM_TYPE }; 354 286 mii.dwTypeData = szBuff; 355 287 mii.cch = _countof(szBuff); 356 288 if (GetPreviousMenuItemInfo(hMenu, indexMenu, &mii) && 357 289 mii.fType != MFT_SEPARATOR && 358 - !(mii.fType == MFT_STRING && CStringW(szBuff) == strCopyTo)) 290 + (IsCopyOp || !(mii.fType == MFT_STRING && !wcscmp(szBuff, strCopyTo.GetBuffer())))) 359 291 { 360 - ZeroMemory(&mii, sizeof(mii)); 361 - mii.cbSize = sizeof(mii); 362 292 mii.fMask = MIIM_TYPE; 363 293 mii.fType = MFT_SEPARATOR; 294 + mii.dwTypeData = NULL; 295 + mii.cch = 0; 364 296 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 365 297 { 366 298 ++indexMenu; 367 - ++Count; 368 299 } 369 300 } 370 301 371 - // insert "Move to folder..." 372 - CStringW strText(MAKEINTRESOURCEW(IDS_MOVETOMENU)); 373 - ZeroMemory(&mii, sizeof(mii)); 374 - mii.cbSize = sizeof(mii); 302 + // Insert the menu item 375 303 mii.fMask = MIIM_ID | MIIM_TYPE; 376 304 mii.fType = MFT_STRING; 377 - mii.dwTypeData = strText.GetBuffer(); 378 - mii.cch = wcslen(mii.dwTypeData); 379 - mii.wID = m_idCmdLast; 305 + mii.dwTypeData = itemText; 306 + mii.wID = idCmdFirst + IDC_ACTION; 380 307 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 381 308 { 382 - m_idCmdAction = m_idCmdLast++; 309 + idHighest = max(idHighest, mii.wID); 383 310 ++indexMenu; 384 - ++Count; 385 311 } 386 - 387 - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdFirst + Count); 312 + return idHighest >= idCmdFirst ? MAKE_HRESULT(SEVERITY_SUCCESS, 0, idHighest - idCmdFirst + 1) : E_FAIL; 388 313 } 389 314 390 315 STDMETHODIMP ··· 395 320 396 321 if (IS_INTRESOURCE(lpici->lpVerb)) 397 322 { 398 - if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdAction) 323 + if (LOWORD(lpici->lpVerb) == IDC_ACTION) 399 324 hr = DoAction(lpici); 400 325 } 401 326 else ··· 415 340 LPSTR pszName, 416 341 UINT cchMax) 417 342 { 343 + if ((uType | GCS_UNICODE) == GCS_VALIDATEW) 344 + return idCmd == IDC_ACTION ? S_OK : S_FALSE; 345 + 346 + if (uType == GCS_VERBW && idCmd == IDC_ACTION) 347 + return SHAnsiToUnicode(GetVerb(), (LPWSTR)pszName, cchMax); 348 + 418 349 FIXME("%p %lu %u %p %p %u\n", this, 419 350 idCmd, uType, pwReserved, pszName, cchMax); 420 351 ··· 431 362 STDMETHODIMP 432 363 CCopyMoveToMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) 433 364 { 434 - m_pidlFolder.Attach(ILClone(pidlFolder)); 435 365 m_pDataObject = pdtobj; 436 - return S_OK; 366 + HRESULT hr = E_FAIL; 367 + if (pidlFolder) 368 + { 369 + hr = SHILClone(pidlFolder, &pidlFolder); 370 + } 371 + else 372 + { 373 + pidlFolder = SHELL_DataObject_ILCloneFullItem(pdtobj, 0); 374 + if (pidlFolder) 375 + { 376 + ILRemoveLastID((LPITEMIDLIST)pidlFolder); 377 + hr = S_OK; 378 + } 379 + } 380 + 381 + if (SUCCEEDED(hr)) 382 + m_pidlFolder.Attach(const_cast<PIDLIST_ABSOLUTE>(pidlFolder)); 383 + return hr; 437 384 } 438 385 439 386 STDMETHODIMP
+2 -2
dll/win32/shell32/CCopyMoveToMenu.h
··· 13 13 public IShellExtInit 14 14 { 15 15 protected: 16 - UINT m_idCmdFirst, m_idCmdLast, m_idCmdAction; 17 16 CComPtr<IDataObject> m_pDataObject; 18 17 CComPtr<IUnknown> m_pSite; 19 18 20 - HRESULT DoRealFileOp(LPCMINVOKECOMMANDINFO lpici, PCUIDLIST_ABSOLUTE pidl); 19 + HRESULT DoRealFileOp(const CIDA *pCIDA, LPCMINVOKECOMMANDINFO lpici, PCUIDLIST_ABSOLUTE pidlDestination); 21 20 HRESULT DoAction(LPCMINVOKECOMMANDINFO lpici); 22 21 23 22 public: ··· 32 31 virtual UINT GetActionTitleStringID() const = 0; 33 32 virtual UINT GetFileOp() const = 0; 34 33 virtual LPCSTR GetVerb() const = 0; 34 + STDMETHODIMP QueryContextMenuImpl(BOOL IsCopyOp, HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); 35 35 36 36 // IContextMenu 37 37 STDMETHODIMP GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) override;
+25 -12
dll/win32/shell32/CDefView.cpp
··· 723 723 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETICON, 2, pIcon, &lResult); 724 724 m_pShellBrowser->SendControlMsg(FCW_STATUS, SB_SETTEXT, 2, (LPARAM)szPartText, &lResult); 725 725 } 726 + 727 + SFGAOF att = 0; 728 + if (cSelectedItems > 0) 729 + { 730 + UINT maxquery = 42; // Checking the attributes can be slow, only check small selections (_DoCopyToMoveToFolder will verify the full array) 731 + att = SFGAO_CANCOPY | SFGAO_CANMOVE; 732 + if (cSelectedItems <= maxquery && (!GetSelections() || FAILED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &att)))) 733 + att = 0; 734 + } 735 + m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, FCIDM_SHVIEW_COPYTO, (att & SFGAO_CANCOPY) != 0, &lResult); 736 + m_pShellBrowser->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, FCIDM_SHVIEW_MOVETO, (att & SFGAO_CANMOVE) != 0, &lResult); 726 737 } 727 738 728 739 LRESULT CDefView::OnUpdateStatusbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) ··· 1947 1958 */ 1948 1959 UINT CDefView::GetSelections() 1949 1960 { 1950 - SHFree(m_apidl); 1951 - 1952 - m_cidl = m_ListView.GetSelectedCount(); 1953 - m_apidl = static_cast<PCUITEMID_CHILD*>(SHAlloc(m_cidl * sizeof(PCUITEMID_CHILD))); 1954 - if (!m_apidl) 1961 + UINT count = m_ListView.GetSelectedCount(); 1962 + if (count > m_cidl || !count || !m_apidl) // !count to free possibly large cache, !m_apidl to make sure m_apidl is a valid pointer 1955 1963 { 1956 - m_cidl = 0; 1957 - return 0; 1964 + SHFree(m_apidl); 1965 + m_apidl = static_cast<PCUITEMID_CHILD*>(SHAlloc(count * sizeof(PCUITEMID_CHILD))); 1966 + if (!m_apidl) 1967 + { 1968 + m_cidl = 0; 1969 + return 0; 1970 + } 1958 1971 } 1972 + m_cidl = count; 1959 1973 1960 1974 TRACE("-- Items selected =%u\n", m_cidl); 1961 1975 ··· 2025 2039 UINT uCommand; 2026 2040 HRESULT hResult; 2027 2041 2028 - m_cidl = m_ListView.GetSelectedCount(); 2029 - if (m_cidl == 0) 2042 + if (m_ListView.GetSelectedCount() == 0) 2030 2043 return S_OK; 2031 2044 2032 2045 hResult = OnDefaultCommand(); ··· 2100 2113 } 2101 2114 } 2102 2115 2103 - m_cidl = m_ListView.GetSelectedCount(); 2116 + UINT count = m_ListView.GetSelectedCount(); 2104 2117 // In case we still have this left over, clean it up 2105 - hResult = GetItemObject(m_cidl ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &m_pCM)); 2118 + hResult = GetItemObject(count ? SVGIO_SELECTION : SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &m_pCM)); 2106 2119 MenuCleanup _(m_pCM, m_hContextMenu); 2107 2120 if (FAILED_UNEXPECTEDLY(hResult)) 2108 2121 return 0; ··· 3382 3395 m_ListView.SetItemState(-1, 0, LVIS_SELECTED); 3383 3396 3384 3397 int lvIndex; 3385 - for (UINT i = 0 ; i < m_cidl; i++) 3398 + for (UINT i = 0 ; i < cidl; i++) 3386 3399 { 3387 3400 lvIndex = LV_FindItemByPidl(apidl[i]); 3388 3401 if (lvIndex != -1)
+3
dll/win32/shell32/precomp.h
··· 229 229 BOOL PathIsDosDevice(_In_ LPCWSTR pszName); 230 230 HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl); 231 231 232 + PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index); 233 + PIDLIST_ABSOLUTE SHELL_DataObject_ILCloneFullItem(_In_ IDataObject *pDO, _In_ UINT Index); 234 + 232 235 EXTERN_C HRESULT 233 236 IUnknown_InitializeCommand( 234 237 _In_ IUnknown *pUnk,
+13
dll/win32/shell32/shldataobject.cpp
··· 101 101 102 102 return hr; 103 103 } 104 + 105 + PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index) 106 + { 107 + if (Index < pCIDA->cidl) 108 + return ILCombine(HIDA_GetPIDLFolder(pCIDA), HIDA_GetPIDLItem(pCIDA, Index)); 109 + return NULL; 110 + } 111 + 112 + PIDLIST_ABSOLUTE SHELL_DataObject_ILCloneFullItem(_In_ IDataObject *pDO, _In_ UINT Index) 113 + { 114 + CDataObjectHIDA cida(pDO); 115 + return SUCCEEDED(cida.hr()) ? SHELL_CIDA_ILCloneFull(cida, Index) : NULL; 116 + }