From 28399a216b744e6263c772754644743e7a6682f1 Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Thu, 19 Dec 2024 14:38:27 +0100 Subject: [PATCH] [SHELL32] Use FS compatible PIDL format for Recycle Bin items (#7532) * [SHELL32] Use FS compatible PIDL format for Recycle Bin items This allows SHChangeNotify to handle these items and DefView will correctly update the recycle folder. CORE-18005 CORE-19239 CORE-13950 CORE-18435 CORE-18436 CORE-18437 --- dll/win32/shell32/CCopyAsPathMenu.cpp | 14 +- dll/win32/shell32/CDefView.cpp | 33 +- dll/win32/shell32/CDefaultContextMenu.cpp | 37 +- dll/win32/shell32/CFolderItemVerbs.cpp | 8 +- dll/win32/shell32/COpenWithMenu.cpp | 20 +- dll/win32/shell32/CSendToMenu.cpp | 9 +- dll/win32/shell32/CShellLink.cpp | 25 +- dll/win32/shell32/dialogs/recycler_prop.cpp | 77 +- dll/win32/shell32/folders/CDesktopFolder.cpp | 2 +- dll/win32/shell32/folders/CFSFolder.cpp | 150 +-- dll/win32/shell32/folders/CFSFolder.h | 7 + dll/win32/shell32/folders/CRecycleBin.cpp | 1008 +++++++++-------- dll/win32/shell32/folders/CRecycleBin.h | 7 +- dll/win32/shell32/precomp.h | 6 + .../shell32/shellrecyclebin/recyclebin.c | 161 +-- .../shell32/shellrecyclebin/recyclebin.h | 133 ++- .../shellrecyclebin/recyclebin_generic.cpp | 12 + .../shellrecyclebin/recyclebin_private.h | 12 + .../shell32/shellrecyclebin/recyclebin_v5.cpp | 40 +- .../shell32/shellrecyclebin/recyclebin_v5.h | 3 +- .../recyclebin_v5_enumerator.cpp | 73 +- dll/win32/shell32/shlexec.cpp | 10 +- dll/win32/shell32/utils.cpp | 94 ++ dll/win32/shell32/utils.h | 40 +- dll/win32/shell32/wine/pidl.c | 86 +- dll/win32/shell32/wine/pidl.h | 13 +- dll/win32/shell32/wine/shell32_main.h | 2 +- 27 files changed, 1141 insertions(+), 941 deletions(-) diff --git a/dll/win32/shell32/CCopyAsPathMenu.cpp b/dll/win32/shell32/CCopyAsPathMenu.cpp index f031874c5d405..3f8f43aeb2dba 100644 --- a/dll/win32/shell32/CCopyAsPathMenu.cpp +++ b/dll/win32/shell32/CCopyAsPathMenu.cpp @@ -103,6 +103,12 @@ CCopyAsPathMenu::DoCopyAsPath(IDataObject *pdto) return hr; } +static const CMVERBMAP g_VerbMap[] = +{ + { "copyaspath", IDC_COPYASPATH }, + { NULL } +}; + STDMETHODIMP CCopyAsPathMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { @@ -133,7 +139,8 @@ CCopyAsPathMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { TRACE("CCopyAsPathMenu::InvokeCommand(%p %p)\n", this, lpcmi); - if (IS_INTRESOURCE(lpcmi->lpVerb) && LOWORD(lpcmi->lpVerb) == IDC_COPYASPATH) + int CmdId = SHELL_MapContextMenuVerbToCmdId(lpcmi, g_VerbMap); + if (CmdId == IDC_COPYASPATH) return DoCopyAsPath(m_pDataObject); return E_FAIL; @@ -142,10 +149,9 @@ CCopyAsPathMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) STDMETHODIMP CCopyAsPathMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) { - FIXME("CCopyAsPathMenu::GetCommandString(%p %lu %u %p %p %u)\n", this, + TRACE("CCopyAsPathMenu::GetCommandString(%p %lu %u %p %p %u)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); - - return E_NOTIMPL; + return SHELL_GetCommandStringImpl(idCommand, uFlags, lpszName, uMaxNameLen, g_VerbMap); } STDMETHODIMP diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp index d4c8ed82cab9e..0505c8e7b6911 100644 --- a/dll/win32/shell32/CDefView.cpp +++ b/dll/win32/shell32/CDefView.cpp @@ -1318,6 +1318,7 @@ int CDefView::LV_FindItemByPidl(PCUITEMID_CHILD pidl) { for (i = 0; i < cItems; i++) { + //FIXME: ILIsEqual needs absolute pidls! currentpidl = _PidlByItem(i); if (ILIsEqual(pidl, currentpidl)) return i; @@ -2848,29 +2849,35 @@ LRESULT CDefView::OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & TRACE("(%p)(%p,%p,%p)\n", this, Pidls[0], Pidls[1], lParam); if (_DoFolderViewCB(SFVM_FSNOTIFY, (WPARAM)Pidls, lEvent) == S_FALSE) + { + SHChangeNotification_Unlock(hLock); return FALSE; + } // Translate child IDLs. // SHSimpleIDListFromPathW creates fake PIDLs (lacking some attributes) + lEvent &= ~SHCNE_INTERRUPT; HRESULT hr; PITEMID_CHILD child0 = NULL, child1 = NULL; CComHeapPtr pidl0Temp, pidl1Temp; - if (_ILIsSpecialFolder(Pidls[0]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[0])) + if (lEvent != SHCNE_UPDATEIMAGE && lEvent < SHCNE_EXTENDED_EVENT) { - child0 = ILFindLastID(Pidls[0]); - hr = SHGetRealIDL(m_pSFParent, child0, &pidl0Temp); - if (SUCCEEDED(hr)) - child0 = pidl0Temp; - } - if (_ILIsSpecialFolder(Pidls[1]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[1])) - { - child1 = ILFindLastID(Pidls[1]); - hr = SHGetRealIDL(m_pSFParent, child1, &pidl1Temp); - if (SUCCEEDED(hr)) - child1 = pidl1Temp; + if (_ILIsSpecialFolder(Pidls[0]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[0])) + { + child0 = ILFindLastID(Pidls[0]); + hr = SHGetRealIDL(m_pSFParent, child0, &pidl0Temp); + if (SUCCEEDED(hr)) + child0 = pidl0Temp; + } + if (_ILIsSpecialFolder(Pidls[1]) || ILIsParentOrSpecialParent(m_pidlParent, Pidls[1])) + { + child1 = ILFindLastID(Pidls[1]); + hr = SHGetRealIDL(m_pSFParent, child1, &pidl1Temp); + if (SUCCEEDED(hr)) + child1 = pidl1Temp; + } } - lEvent &= ~SHCNE_INTERRUPT; switch (lEvent) { case SHCNE_MKDIR: diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp b/dll/win32/shell32/CDefaultContextMenu.cpp index 6b83bd8a069b6..90954dd87c98c 100644 --- a/dll/win32/shell32/CDefaultContextMenu.cpp +++ b/dll/win32/shell32/CDefaultContextMenu.cpp @@ -84,6 +84,25 @@ UINT MapVerbToDfmCmd(_In_ LPCSTR verba) return 0; } +static HRESULT +MapVerbToCmdId(PVOID Verb, BOOL IsUnicode, IContextMenu *pCM, UINT idFirst, UINT idLast) +{ + const UINT gcs = IsUnicode ? GCS_VERBW : GCS_VERBA; + for (UINT id = idFirst; id <= idLast; ++id) + { + WCHAR buf[MAX_PATH]; + *buf = UNICODE_NULL; + HRESULT hr = pCM->GetCommandString(id, gcs, NULL, (LPSTR)buf, _countof(buf)); + if (FAILED(hr) || !*buf) + continue; + else if (IsUnicode && !_wcsicmp((LPWSTR)Verb, buf)) + return id; + else if (!IsUnicode && !lstrcmpiA((LPSTR)Verb, (LPSTR)buf)) + return id; + } + return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); +} + static inline bool IsVerbListSeparator(WCHAR Ch) { return Ch == L' ' || Ch == L','; // learn.microsoft.com/en-us/windows/win32/shell/context-menu-handlers @@ -738,7 +757,7 @@ CDefaultContextMenu::AddStaticContextMenusToMenu( return cIds; } -void WINAPI _InsertMenuItemW( +BOOL WINAPI _InsertMenuItemW( HMENU hMenu, UINT indexMenu, BOOL fByPosition, @@ -764,7 +783,7 @@ void WINAPI _InsertMenuItemW( else { ERR("failed to load string %p\n", dwTypeData); - return; + return FALSE; } } else @@ -774,7 +793,7 @@ void WINAPI _InsertMenuItemW( mii.wID = wID; mii.fType = fType; - InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii); + return InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii); } void @@ -1212,6 +1231,18 @@ CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode) } } + for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;) + { + DynamicShellEntry& entry = m_DynamicEntries.GetNext(it); + if (!entry.NumIds) + continue; + HRESULT hr = ::MapVerbToCmdId(Verb, IsUnicode, entry.pCM, 0, entry.NumIds - 1); + if (SUCCEEDED(hr)) + { + *idCmd = m_iIdSHEFirst + entry.iIdCmdFirst + hr; + return TRUE; + } + } return FALSE; } diff --git a/dll/win32/shell32/CFolderItemVerbs.cpp b/dll/win32/shell32/CFolderItemVerbs.cpp index 775f342f7375b..ce7f2266ba6e5 100644 --- a/dll/win32/shell32/CFolderItemVerbs.cpp +++ b/dll/win32/shell32/CFolderItemVerbs.cpp @@ -91,13 +91,7 @@ CFolderItemVerbs::~CFolderItemVerbs() HRESULT CFolderItemVerbs::Init(LPITEMIDLIST idlist) { - CComPtr folder; - LPCITEMIDLIST child; - HRESULT hr = SHBindToParent(idlist, IID_PPV_ARG(IShellFolder, &folder), &child); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - hr = folder->GetUIObjectOf(NULL, 1, &child, IID_IContextMenu, NULL, (PVOID*)&m_contextmenu); + HRESULT hr = SHELL_GetUIObjectOfAbsoluteItem(NULL, idlist, IID_PPV_ARG(IContextMenu, &m_contextmenu)); if (FAILED_UNEXPECTEDLY(hr)) return hr; diff --git a/dll/win32/shell32/COpenWithMenu.cpp b/dll/win32/shell32/COpenWithMenu.cpp index 42f538a7eeb58..96a73dd822d14 100644 --- a/dll/win32/shell32/COpenWithMenu.cpp +++ b/dll/win32/shell32/COpenWithMenu.cpp @@ -1250,6 +1250,11 @@ VOID COpenWithMenu::AddApp(PVOID pApp) m_idCmdLast++; } +static const CMVERBMAP g_VerbMap[] = { + { "openas", 0 }, + { NULL } +}; + HRESULT WINAPI COpenWithMenu::QueryContextMenu( HMENU hMenu, UINT indexMenu, @@ -1328,14 +1333,19 @@ HRESULT WINAPI COpenWithMenu::QueryContextMenu( HRESULT WINAPI COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) { + const SIZE_T idChooseApp = m_idCmdLast; HRESULT hr = E_FAIL; TRACE("This %p idFirst %u idLast %u idCmd %u\n", this, m_idCmdFirst, m_idCmdLast, m_idCmdFirst + LOWORD(lpici->lpVerb)); - if (HIWORD(lpici->lpVerb) == 0 && m_idCmdFirst + LOWORD(lpici->lpVerb) <= m_idCmdLast) + if (!IS_INTRESOURCE(lpici->lpVerb) && SHELL_MapContextMenuVerbToCmdId(lpici, g_VerbMap) == 0) + goto DoChooseApp; + + if (IS_INTRESOURCE(lpici->lpVerb) && m_idCmdFirst + LOWORD(lpici->lpVerb) <= m_idCmdLast) { - if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdLast) + if (m_idCmdFirst + LOWORD(lpici->lpVerb) == idChooseApp) { +DoChooseApp: OPENASINFO info; LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); @@ -1371,9 +1381,13 @@ HRESULT WINAPI COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax ) { - FIXME("%p %lu %u %p %p %u\n", this, + TRACE("%p %lu %u %p %p %u\n", this, idCmd, uType, pwReserved, pszName, cchMax ); + const SIZE_T idChooseApp = m_idCmdLast; + if (m_idCmdFirst + idCmd == idChooseApp) + return SHELL_GetCommandStringImpl(0, uType, pszName, cchMax, g_VerbMap); + return E_NOTIMPL; } diff --git a/dll/win32/shell32/CSendToMenu.cpp b/dll/win32/shell32/CSendToMenu.cpp index 131f21e2a0a2f..8cce580ba082b 100644 --- a/dll/win32/shell32/CSendToMenu.cpp +++ b/dll/win32/shell32/CSendToMenu.cpp @@ -130,14 +130,7 @@ HRESULT CSendToMenu::GetUIObjectFromPidl(HWND hwnd, PIDLIST_ABSOLUTE pidl, REFIID riid, LPVOID *ppvOut) { *ppvOut = NULL; - - PCITEMID_CHILD pidlLast; - CComPtr pFolder; - HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &pFolder), &pidlLast); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - hr = pFolder->GetUIObjectOf(hwnd, 1, &pidlLast, riid, NULL, ppvOut); + HRESULT hr = SHELL_GetUIObjectOfAbsoluteItem(hwnd, pidl, riid, ppvOut); if (FAILED_UNEXPECTEDLY(hr)) return hr; diff --git a/dll/win32/shell32/CShellLink.cpp b/dll/win32/shell32/CShellLink.cpp index d99db2f1470e5..9619efcf483f4 100644 --- a/dll/win32/shell32/CShellLink.cpp +++ b/dll/win32/shell32/CShellLink.cpp @@ -1729,18 +1729,10 @@ HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPWSTR pszIconPath, INT cc static HRESULT SHELL_PidlGetIconLocationW(PCIDLIST_ABSOLUTE pidl, UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) { - LPCITEMIDLIST pidlLast; - CComPtr psf; - - HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - CComPtr pei; - hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IExtractIconW, &pei)); + HRESULT hr = SHELL_GetUIObjectOfAbsoluteItem(NULL, pidl, IID_PPV_ARG(IExtractIconW, &pei)); if (FAILED_UNEXPECTEDLY(hr)) return hr; - hr = pei->GetIconLocation(uFlags, pszIconFile, cchMax, piIndex, pwFlags); if (FAILED_UNEXPECTEDLY(hr)) return hr; @@ -3142,20 +3134,9 @@ HRESULT STDMETHODCALLTYPE CShellLink::DragEnter(IDataObject *pDataObject, if (*pdwEffect == DROPEFFECT_NONE) return S_OK; - LPCITEMIDLIST pidlLast; - CComPtr psf; - - HRESULT hr = SHBindToParent(m_pPidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); - + HRESULT hr = SHELL_GetUIObjectOfAbsoluteItem(NULL, m_pPidl, IID_PPV_ARG(IDropTarget, &m_DropTarget)); if (SUCCEEDED(hr)) - { - hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IDropTarget, &m_DropTarget)); - - if (SUCCEEDED(hr)) - hr = m_DropTarget->DragEnter(pDataObject, dwKeyState, pt, pdwEffect); - else - *pdwEffect = DROPEFFECT_NONE; - } + hr = m_DropTarget->DragEnter(pDataObject, dwKeyState, pt, pdwEffect); else *pdwEffect = DROPEFFECT_NONE; diff --git a/dll/win32/shell32/dialogs/recycler_prop.cpp b/dll/win32/shell32/dialogs/recycler_prop.cpp index d7f79251a3e74..72387de388cf8 100644 --- a/dll/win32/shell32/dialogs/recycler_prop.cpp +++ b/dll/win32/shell32/dialogs/recycler_prop.cpp @@ -45,6 +45,9 @@ static void toggleNukeOnDeleteOption(HWND hwndDlg, BOOL bEnable) EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE); SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_UNCHECKED, 0); } + + // FIXME: Max capacity not implemented yet, disable for now (CORE-13743) + EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE); } static VOID @@ -129,7 +132,8 @@ InitializeRecycleBinDlg(HWND hwndDlg, WCHAR DefaultDrive) swprintf(szName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\%04X-%04X", LOWORD(dwSerial), HIWORD(dwSerial)); dwSize = sizeof(DWORD); - RegGetValueW(HKEY_CURRENT_USER, szName, L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize); + if (RegGetValueW(HKEY_CURRENT_USER, szName, L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize)) + pItem->dwMaxCapacity = ~0; /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */ FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024)); @@ -240,7 +244,7 @@ static VOID FreeDriveItemContext(HWND hwndDlg) } static INT -GetDefaultItem(HWND hwndDlg, LVITEMW* li) +GetSelectedDriveItem(HWND hwndDlg, LVITEMW* li) { HWND hDlgCtrl; UINT iItemCount, iIndex; @@ -275,6 +279,7 @@ RecycleBinDlg( WPARAM wParam, LPARAM lParam) { + enum { WM_NEWDRIVESELECTED = WM_APP, WM_UPDATEDRIVESETTINGS }; LPPSHNOTIFY lppsn; LPNMLISTVIEW lppl; LVITEMW li; @@ -329,25 +334,9 @@ RecycleBinDlg( ss.fNoConfirmRecycle = SendDlgItemMessage(hwndDlg, 14004, BM_GETCHECK, 0, 0) == BST_UNCHECKED; SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, TRUE); - if (GetDefaultItem(hwndDlg, &li) > -1) + if (GetSelectedDriveItem(hwndDlg, &li) > -1) { - pItem = (PDRIVE_ITEM_CONTEXT)li.lParam; - if (pItem) - { - uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE); - if (bSuccess) - { - /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */ - FreeBytesAvailable = pItem->FreeBytesAvailable; - FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024)); - pItem->dwMaxCapacity = min(uResult, FreeBytesAvailable.LowPart); - SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE); - } - if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED) - pItem->dwNukeOnDelete = TRUE; - else - pItem->dwNukeOnDelete = FALSE; - } + SendMessage(hwndDlg, WM_UPDATEDRIVESETTINGS, 0, li.lParam); } if (StoreDriveSettings(hwndDlg)) { @@ -369,31 +358,45 @@ RecycleBinDlg( if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED)) { - /* new focused item */ - toggleNukeOnDeleteOption(lppl->hdr.hwndFrom, pItem->dwNukeOnDelete); - SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE); + // New focused item, delay handling until after kill focus has been processed + PostMessage(hwndDlg, WM_NEWDRIVESELECTED, 0, (LPARAM)pItem); } else if ((lppl->uOldState & LVIS_FOCUSED) && !(lppl->uNewState & LVIS_FOCUSED)) { - /* kill focus */ - uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE); - if (bSuccess) - { - /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */ - FreeBytesAvailable = pItem->FreeBytesAvailable; - FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024)); - pItem->dwMaxCapacity = min(uResult, FreeBytesAvailable.LowPart); - SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE); - } - if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED) - pItem->dwNukeOnDelete = TRUE; - else - pItem->dwNukeOnDelete = FALSE; + // Kill focus + SendMessage(hwndDlg, WM_UPDATEDRIVESETTINGS, 0, (LPARAM)pItem); } return TRUE; } break; + case WM_NEWDRIVESELECTED: + if (lParam) + { + pItem = (PDRIVE_ITEM_CONTEXT)lParam; + toggleNukeOnDeleteOption(hwndDlg, pItem->dwNukeOnDelete); + SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE); + } + break; + case WM_UPDATEDRIVESETTINGS: + if (lParam) + { + pItem = (PDRIVE_ITEM_CONTEXT)lParam; + uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE); + if (bSuccess) + { + /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */ + FreeBytesAvailable = pItem->FreeBytesAvailable; + FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024)); + pItem->dwMaxCapacity = min(uResult, FreeBytesAvailable.LowPart); + SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE); + } + if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED) + pItem->dwNukeOnDelete = TRUE; + else + pItem->dwNukeOnDelete = FALSE; + } + break; case WM_DESTROY: FreeDriveItemContext(hwndDlg); break; diff --git a/dll/win32/shell32/folders/CDesktopFolder.cpp b/dll/win32/shell32/folders/CDesktopFolder.cpp index 945f3450edede..b785a28a9599f 100644 --- a/dll/win32/shell32/folders/CDesktopFolder.cpp +++ b/dll/win32/shell32/folders/CDesktopFolder.cpp @@ -695,7 +695,7 @@ HRESULT WINAPI CDesktopFolder::GetAttributesOf( *rgfInOut &= dwMyComputerAttributes; else if (_ILIsNetHood(apidl[i])) *rgfInOut &= dwMyNetPlacesAttributes; - else if (_ILIsFolder(apidl[i]) || _ILIsValue(apidl[i]) || _ILIsSpecialFolder(apidl[i])) + else if (_ILIsFolderOrFile(apidl[i]) || _ILIsSpecialFolder(apidl[i])) { CComPtr psf; HRESULT hr = _GetSFFromPidl(apidl[i], &psf); diff --git a/dll/win32/shell32/folders/CFSFolder.cpp b/dll/win32/shell32/folders/CFSFolder.cpp index 9ae1f53da1ff9..dd4b3678771ad 100644 --- a/dll/win32/shell32/folders/CFSFolder.cpp +++ b/dll/win32/shell32/folders/CFSFolder.cpp @@ -620,7 +620,7 @@ HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDW { DWORD dwFileAttributes, dwShellAttributes; - if (!_ILIsFolder(pidl) && !_ILIsValue(pidl)) + if (!_ILIsFolderOrFile(pidl)) { ERR("Got wrong type of pidl!\n"); *pdwAttributes &= SFGAO_CANLINK; @@ -1007,9 +1007,9 @@ HRESULT WINAPI CFSFolder::BindToObject( /* Get the pidl data */ FileStruct* pData = &_ILGetDataPointer(pidl)->u.file; - FileStructW* pDataW = _ILGetFileStructW(pidl); - - if (!pDataW) + WCHAR szNameBuf[MAX_PATH]; + LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf)); + if (!pszName) { ERR("CFSFolder::BindToObject: Invalid pidl!\n"); return E_INVALIDARG; @@ -1021,7 +1021,7 @@ HRESULT WINAPI CFSFolder::BindToObject( PERSIST_FOLDER_TARGET_INFO pfti = {0}; pfti.dwAttributes = -1; pfti.csidl = -1; - PathCombineW(pfti.szTargetParsingName, m_sPathTarget, pDataW->wszName); + PathCombineW(pfti.szTargetParsingName, m_sPathTarget, pszName); /* Get the CLSID to bind to */ CLSID clsidFolder; @@ -1088,6 +1088,20 @@ HRESULT WINAPI CFSFolder::BindToStorage( return E_NOTIMPL; } +HRESULT CFSFolder::CompareSortFoldersFirst(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) +{ + BOOL bIsFolder1 = _ILIsFolder(pidl1), bIsFolder2 = _ILIsFolder(pidl2); + // When sorting between a File and a Folder, the Folder gets sorted first + if (bIsFolder1 != bIsFolder2) + { + // ...but only if neither of them were generated by SHSimpleIDListFromPath + // because in that case we cannot tell if it's a file or a folder. + if (pidl1 && IsRealItem(*pidl1) && pidl2 && IsRealItem(*pidl2)) + return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1); + } + return MAKE_SCODE(SEVERITY_ERROR, FACILITY_SHELL, S_EQUAL); +} + /************************************************************************** * CFSFolder::CompareIDs */ @@ -1096,31 +1110,24 @@ HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) { + WCHAR szNameBuf1[MAX_PATH], szNameBuf2[_countof(szNameBuf1)]; + LPCWSTR pszName1 = GetItemFileName(pidl1, szNameBuf1, _countof(szNameBuf1)); + LPCWSTR pszName2 = GetItemFileName(pidl2, szNameBuf2, _countof(szNameBuf2)); + if (!pszName1 || !pszName2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS) + return E_INVALIDARG; + LPPIDLDATA pData1 = _ILGetDataPointer(pidl1); LPPIDLDATA pData2 = _ILGetDataPointer(pidl2); - FileStructW* pDataW1 = _ILGetFileStructW(pidl1); - FileStructW* pDataW2 = _ILGetFileStructW(pidl2); - BOOL bIsFolder1 = _ILIsFolder(pidl1); - BOOL bIsFolder2 = _ILIsFolder(pidl2); LPWSTR pExtension1, pExtension2; - if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS) - return E_INVALIDARG; - - /* When sorting between a File and a Folder, the Folder gets sorted first */ - if (bIsFolder1 != bIsFolder2) - { - // ...but only if neither of them were generated by SHSimpleIDListFromPath - // because in that case we cannot tell if it's a file or a folder. - if (IsRealItem(*pidl1) && IsRealItem(*pidl2)) - return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1); - } - + HRESULT hr = CompareSortFoldersFirst(pidl1, pidl2); + if (SUCCEEDED(hr)) + return hr; int result = 0; switch (LOWORD(lParam)) { case SHFSF_COL_NAME: - result = _wcsicmp(pDataW1->wszName, pDataW2->wszName); + result = _wcsicmp(pszName1, pszName2); break; case SHFSF_COL_SIZE: if (pData1->u.file.dwFileSize > pData2->u.file.dwFileSize) @@ -1131,8 +1138,9 @@ HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam, result = 0; break; case SHFSF_COL_TYPE: - pExtension1 = PathFindExtensionW(pDataW1->wszName); - pExtension2 = PathFindExtensionW(pDataW2->wszName); + // FIXME: Compare the type strings from SHGetFileInfo + pExtension1 = PathFindExtensionW(pszName1); + pExtension2 = PathFindExtensionW(pszName2); result = _wcsicmp(pExtension1, pExtension2); break; case SHFSF_COL_MDATE: @@ -1273,7 +1281,7 @@ HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl, { LPCITEMIDLIST rpidl = ILFindLastID(m_pidlRoot); - if (_ILIsFolder(rpidl) || _ILIsValue(rpidl)) + if (_ILIsFolderOrFile(rpidl)) { SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut); } @@ -1297,7 +1305,7 @@ HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl, while (cidl > 0 && *apidl) { pdump(*apidl); - if(_ILIsFolder(*apidl) || _ILIsValue(*apidl)) + if (_ILIsFolderOrFile(*apidl)) SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut); else ERR("Got an unknown type of pidl!!!\n"); @@ -1549,14 +1557,14 @@ HRESULT WINAPI CFSFolder::SetNameOf( DWORD dwFlags, PITEMID_CHILD *pPidlOut) { - WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1]; + WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1], szNameBuf[MAX_PATH]; BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl)); TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut); - FileStructW* pDataW = _ILGetFileStructW(pidl); - if (!pDataW) + LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf)); + if (!pszName) { ERR("Got garbage pidl:\n"); pdump_always(pidl); @@ -1564,7 +1572,7 @@ HRESULT WINAPI CFSFolder::SetNameOf( } /* build source path */ - PathCombineW(szSrc, m_sPathTarget, pDataW->wszName); // FIXME: PIDLs without wide string + PathCombineW(szSrc, m_sPathTarget, pszName); /* build destination path */ if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) @@ -1662,6 +1670,7 @@ HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl, } else { + FILETIME ft; hr = S_OK; psd->str.uType = STRRET_WSTR; if (iColumn != SHFSF_COL_NAME) @@ -1683,7 +1692,11 @@ HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl, GetItemDescription(pidl, psd->str.pOleStr, MAX_PATH); break; case SHFSF_COL_MDATE: - _ILGetFileDate(pidl, psd->str.pOleStr, MAX_PATH); + if (!_ILGetFileDateTime(pidl, &ft) || FAILED(FormatDateTime(ft, psd->str.pOleStr, MAX_PATH))) + { + *psd->str.pOleStr = UNICODE_NULL; + hr = S_FALSE; + } break; case SHFSF_COL_FATTS: _ILGetFileAttributes(pidl, psd->str.pOleStr, MAX_PATH); @@ -1744,17 +1757,12 @@ HRESULT WINAPI CFSFolder::Initialize(PCIDLIST_ABSOLUTE pidl) m_sPathTarget = NULL; /* set my path */ + HRESULT hr = E_FAIL; if (SHGetPathFromIDListW (pidl, wszTemp)) - { - int len = wcslen(wszTemp); - m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); - if (!m_sPathTarget) - return E_OUTOFMEMORY; - memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); - } + hr = SHStrDupW(wszTemp, &m_sPathTarget); TRACE ("--(%p)->(%s)\n", this, debugstr_w(m_sPathTarget)); - return S_OK; + return hr; } /************************************************************************** @@ -1806,44 +1814,28 @@ HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx, * the target folder is spezified in csidl OR pidlTargetFolder OR * szTargetParsingName */ + HRESULT hr = E_FAIL; if (ppfti) { if (ppfti->csidl != -1) { - if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl, - ppfti->csidl & CSIDL_FLAG_CREATE)) { - int len = wcslen(wszTemp); - m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); - if (!m_sPathTarget) - return E_OUTOFMEMORY; - memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); - } + BOOL create = ppfti->csidl & CSIDL_FLAG_CREATE; + if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl, create)) + hr = SHStrDupW(wszTemp, &m_sPathTarget); } else if (ppfti->szTargetParsingName[0]) { - int len = wcslen(ppfti->szTargetParsingName); - m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); - if (!m_sPathTarget) - return E_OUTOFMEMORY; - memcpy(m_sPathTarget, ppfti->szTargetParsingName, - (len + 1) * sizeof(WCHAR)); + hr = SHStrDupW(ppfti->szTargetParsingName, &m_sPathTarget); } else if (ppfti->pidlTargetFolder) { if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp)) - { - int len = wcslen(wszTemp); - m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR)); - if (!m_sPathTarget) - return E_OUTOFMEMORY; - memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR)); - } + hr = SHStrDupW(wszTemp, &m_sPathTarget); } } - TRACE("--(%p)->(target=%s)\n", this, debugstr_w(m_sPathTarget)); pdump(m_pidlRoot); - return (m_sPathTarget) ? S_OK : E_FAIL; + return hr; } HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti) @@ -1923,17 +1915,16 @@ HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvO HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) { HRESULT hr; - WCHAR wszPath[MAX_PATH]; + WCHAR wszPath[MAX_PATH], szNameBuf[MAX_PATH]; - FileStructW* pDataW = _ILGetFileStructW(pidl); - if (!pDataW) + LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf)); + if (!pszName) { ERR("Got garbage pidl\n"); pdump_always(pidl); return E_INVALIDARG; } - - PathCombineW(wszPath, m_sPathTarget, pDataW->wszName); + PathCombineW(wszPath, m_sPathTarget, pszName); CComPtr pp; hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp)); @@ -2104,3 +2095,28 @@ HRESULT WINAPI CFSFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) } return hr; } + +HRESULT CFSFolder::FormatDateTime(const FILETIME &ft, LPWSTR Buf, UINT cchBuf) +{ + FILETIME lft; + SYSTEMTIME time; + FileTimeToLocalFileTime(&ft, &lft); + FileTimeToSystemTime(&lft, &time); + UINT ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, Buf, cchBuf); + if (ret > 0 && ret < cchBuf) + { + /* Append space + time without seconds */ + Buf[ret-1] = ' '; + GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &Buf[ret], cchBuf - ret); + } + return ret ? S_OK : E_FAIL; +} + +HRESULT CFSFolder::FormatSize(UINT64 size, LPWSTR Buf, UINT cchBuf) +{ + if (StrFormatKBSizeW(size, Buf, cchBuf)) + return S_OK; + if (cchBuf) + *Buf = UNICODE_NULL; + return E_FAIL; +} diff --git a/dll/win32/shell32/folders/CFSFolder.h b/dll/win32/shell32/folders/CFSFolder.h index e3db017578d6d..79f9c2c17e5a1 100644 --- a/dll/win32/shell32/folders/CFSFolder.h +++ b/dll/win32/shell32/folders/CFSFolder.h @@ -130,6 +130,13 @@ class CFSFolder : // Helper functions shared with CDesktopFolder static HRESULT GetFSColumnDetails(UINT iColumn, SHELLDETAILS &sd); static HRESULT GetDefaultFSColumnState(UINT iColumn, SHCOLSTATEF &csFlags); + static HRESULT FormatDateTime(const FILETIME &ft, LPWSTR Buf, UINT cchBuf); + static HRESULT FormatSize(UINT64 size, LPWSTR Buf, UINT cchBuf); + static HRESULT CompareSortFoldersFirst(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); + static inline int CompareUiStrings(LPCWSTR a, LPCWSTR b) + { + return StrCmpLogicalW(a, b); + } }; #endif /* _CFSFOLDER_H_ */ diff --git a/dll/win32/shell32/folders/CRecycleBin.cpp b/dll/win32/shell32/folders/CRecycleBin.cpp index a9660e216a8c7..bd09268623bb2 100644 --- a/dll/win32/shell32/folders/CRecycleBin.cpp +++ b/dll/win32/shell32/folders/CRecycleBin.cpp @@ -58,78 +58,259 @@ static const columninfo RecycleBinColumns[] = #define COLUMNS_COUNT 6 +// The ROS Recycle Bin PIDL format starts with a NT4/2000 Unicode FS PIDL followed by +// BBITEMDATA and BBITEMFOOTER. This makes it compatible with SHChangeNotify listeners. +#include "pshpack1.h" +#define BBITEMFILETYPE (PT_FS | PT_FS_UNICODE_FLAG | PT_FS_FILE_FLAG) +#define BBITEMFOLDERTYPE (PT_FS | PT_FS_UNICODE_FLAG | PT_FS_FOLDER_FLAG) +struct BBITEMDATA +{ + FILETIME DeletionTime; +#ifdef COLUMN_FATTS + WORD AttribsHi; // Nobody needs this yet +#endif + WORD RecycledPathOffset; + WCHAR OriginalLocation[ANYSIZE_ARRAY]; + // ... @RecycledPathOffset WCHAR RecycledPath[ANYSIZE_ARRAY]; +}; +struct BBITEMFOOTER +{ + enum { ENDSIG = MAKEWORD('K', 'I') }; // "Killed item". MUST have the low bit set so _ILGetFileStructW returns NULL. + WORD DataSize; + WORD EndSignature; +}; +#include "poppack.h" + +static inline BOOL IsFolder(LPCITEMIDLIST pidl) +{ + return _ILGetFSType(pidl) & PT_FS_FOLDER_FLAG; +} + +static BBITEMDATA* ValidateItem(LPCITEMIDLIST pidl) +{ + const UINT minstringsize = sizeof(L"X") + sizeof(""); // PT_FS strings + const UINT minfs = sizeof(WORD) + FIELD_OFFSET(PIDLDATA, u.file.szNames) + minstringsize; + const UINT mindatasize = FIELD_OFFSET(BBITEMDATA, OriginalLocation) + (sizeof(L"C:\\X") * 2); + const UINT minsize = minfs + mindatasize + sizeof(BBITEMFOOTER); + const BYTE type = _ILGetType(pidl); + if ((type == BBITEMFILETYPE || type == BBITEMFOLDERTYPE) && pidl->mkid.cb >= minsize) + { + BBITEMFOOTER *pEnd = (BBITEMFOOTER*)((BYTE*)pidl + pidl->mkid.cb - sizeof(BBITEMFOOTER)); + if (pEnd->EndSignature == BBITEMFOOTER::ENDSIG && pEnd->DataSize >= mindatasize) + return (BBITEMDATA*)((BYTE*)pEnd - pEnd->DataSize); + } + return NULL; +} + +static LPITEMIDLIST CreateItem(LPCWSTR pszTrash, LPCWSTR pszOrig, const DELETED_FILE_INFO &Details) +{ + const BOOL folder = Details.Attributes & FILE_ATTRIBUTE_DIRECTORY; + LPCWSTR pszName = PathFindFileNameW(pszTrash); + SIZE_T ofsName = (SIZE_T)(pszName - pszTrash); + SIZE_T cchName = wcslen(pszName) + 1, cbName = cchName * sizeof(WCHAR); + SIZE_T cbFSNames = cbName + sizeof("") + 1; // Empty short name + 1 for WORD alignment + SIZE_T cbFS = sizeof(WORD) + FIELD_OFFSET(PIDLDATA, u.file.szNames) + cbFSNames; + SIZE_T cchTrash = ofsName + cchName, cbTrash = cchTrash * sizeof(WCHAR); + SIZE_T cchOrig = wcslen(pszOrig) + 1, cbOrig = cchOrig * sizeof(WCHAR); + SIZE_T cbData = FIELD_OFFSET(BBITEMDATA, OriginalLocation) + cbOrig + cbTrash; + SIZE_T cb = cbFS + cbData + sizeof(BBITEMFOOTER); + if (cb > 0xffff) + return NULL; + LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cb + sizeof(WORD)); + if (!pidl) + return pidl; + + pidl->mkid.cb = cb; + pidl->mkid.abID[0] = folder ? BBITEMFOLDERTYPE : BBITEMFILETYPE; + ILGetNext(pidl)->mkid.cb = 0; // Terminator + FileStruct &fsitem = ((PIDLDATA*)pidl->mkid.abID)->u.file; + fsitem.dummy = 0; + C_ASSERT(sizeof(RECYCLEBINFILESIZETYPE) <= sizeof(fsitem.dwFileSize)); + fsitem.dwFileSize = Details.FileSize; + fsitem.uFileAttribs = LOWORD(Details.Attributes); + FileTimeToDosDateTime(&Details.LastModification, &fsitem.uFileDate, &fsitem.uFileTime); + CopyMemory(fsitem.szNames, pszName, cbName); + LPSTR pszShort = const_cast(&fsitem.szNames[cbName]); + pszShort[0] = '\0'; + pszShort[1] = '\0'; // Fill alignment padding (for ILIsEqual memcmp) + + BBITEMFOOTER *footer = (BBITEMFOOTER*)((BYTE*)pidl + cb - sizeof(BBITEMFOOTER)); + footer->DataSize = cbData; + footer->EndSignature = BBITEMFOOTER::ENDSIG; + + BBITEMDATA *data = (BBITEMDATA*)((BYTE*)footer - footer->DataSize); + data->DeletionTime = Details.DeletionTime; +#ifdef COLUMN_FATTS + data->AttribsHi = HIWORD(Details.Attributes); +#endif + data->RecycledPathOffset = FIELD_OFFSET(BBITEMDATA, OriginalLocation) + cbOrig; + CopyMemory(data->OriginalLocation, pszOrig, cbOrig); + CopyMemory((BYTE*)data + data->RecycledPathOffset, pszTrash, cbTrash); + + assert(!(((SIZE_T)&fsitem.szNames) & 1)); // WORD aligned please + C_ASSERT(!(FIELD_OFFSET(BBITEMDATA, OriginalLocation) & 1)); // WORD aligned please + assert(!(((SIZE_T)data) & 1)); // WORD aligned please + assert(_ILGetFSType(pidl)); + assert(_ILIsPidlSimple(pidl)); + assert(*(WORD*)((BYTE*)pidl + pidl->mkid.cb - sizeof(WORD)) & 1); // ENDSIG bit + assert(_ILGetFileStructW(pidl) == NULL); // Our custom footer is incompatible with WinXP pidl data + assert(ValidateItem(pidl) == data); + return pidl; +} + +static inline UINT GetItemFileSize(LPCITEMIDLIST pidl) +{ + return _ILGetFSType(pidl) ? ((PIDLDATA*)pidl->mkid.abID)->u.file.dwFileSize : 0; +} + +static inline LPCWSTR GetItemOriginalFullPath(const BBITEMDATA &Data) +{ + return Data.OriginalLocation; +} + +static HRESULT GetItemOriginalFolder(const BBITEMDATA &Data, LPWSTR &Out) +{ + HRESULT hr = SHStrDupW(GetItemOriginalFullPath(Data), &Out); + if (SUCCEEDED(hr)) + PathRemoveFileSpecW(Out); + return hr; +} + +static LPCWSTR GetItemOriginalFileName(const BBITEMDATA &Data) +{ + return PathFindFileNameW(GetItemOriginalFullPath(Data)); +} + +static inline LPCWSTR GetItemRecycledFullPath(const BBITEMDATA &Data) +{ + return (LPCWSTR)((BYTE*)&Data + Data.RecycledPathOffset); +} + +static inline LPCWSTR GetItemRecycledFileName(LPCITEMIDLIST pidl, const BBITEMDATA &Data) +{ + C_ASSERT(BBITEMFILETYPE & PT_FS_UNICODE_FLAG); + return (LPCWSTR)((LPPIDLDATA)pidl->mkid.abID)->u.file.szNames; +} + +static int GetItemDriveNumber(LPCITEMIDLIST pidl) +{ + if (BBITEMDATA *pData = ValidateItem(pidl)) + return PathGetDriveNumberW(GetItemRecycledFullPath(*pData)); + WCHAR buf[MAX_PATH]; + return _ILSimpleGetTextW(pidl, buf, _countof(buf)) ? PathGetDriveNumberW(buf) : -1; +} + +static HRESULT GetItemTypeName(PCUITEMID_CHILD pidl, const BBITEMDATA &Data, SHFILEINFOW &shfi) +{ + LPCWSTR path = GetItemRecycledFullPath(Data); + UINT attribs = ((PIDLDATA*)pidl->mkid.abID)->u.file.uFileAttribs; + if (SHGetFileInfoW(path, attribs, &shfi, sizeof(shfi), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES)) + return S_OK; + shfi.szTypeName[0] = UNICODE_NULL; + return E_FAIL; +} + +static HDELFILE GetRecycleBinFileHandleFromItem(const BBITEMDATA &Data) +{ + RECYCLEBINFILEIDENTITY identity = { Data.DeletionTime, GetItemRecycledFullPath(Data) }; + return GetRecycleBinFileHandle(NULL, &identity); +} + /* * Recycle Bin folder */ -BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile); - -static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl); +static UINT GetDefaultRecycleDriveNumber() +{ + int drive = 0; + WCHAR buf[MAX_PATH]; + if (GetWindowsDirectoryW(buf, _countof(buf))) + drive = PathGetDriveNumberW(buf); + return max(0, drive); +} -typedef struct _SEARCH_CONTEXT +static inline LPCWSTR GetGlobalRecycleBinPath() { - PIDLRecycleStruct *pFileDetails; - HDELFILE hDeletedFile; - BOOL bFound; -} SEARCH_CONTEXT, *PSEARCH_CONTEXT; + return NULL; +} -HRESULT CRecyclerExtractIcon_CreateInstance( - LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut) +static BOOL IsRecycleBinEmpty(IShellFolder *pSF) { - PIDLRecycleStruct *pFileDetails = _ILGetRecycleStruct(pidl); - if (pFileDetails == NULL) - goto fallback; + CComPtr spEnumFiles; + HRESULT hr = pSF->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spEnumFiles); + CComHeapPtr spPidl; + ULONG itemcount; + return FAILED(hr) || spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK; +} - // Try to obtain the file - SEARCH_CONTEXT Context; - Context.pFileDetails = pFileDetails; - Context.bFound = FALSE; +static void CRecycleBin_ChangeNotifyBBItem(_In_ LONG Event, _In_opt_ LPCITEMIDLIST BBItem) +{ + LPITEMIDLIST pidlFolder = SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET, FALSE); + if (!pidlFolder) + return; + if (BBItem) + { + assert(ValidateItem(BBItem)); + if (LPITEMIDLIST pidlFull = ILCombine(pidlFolder, BBItem)) + { + // Send notification for [Desktop][RecycleBin][BBItem] + // FIXME: Windows monitors each RecycleBin FS folder on every drive + // instead of manually sending these? + SHChangeNotify(Event, SHCNF_IDLIST, pidlFull, NULL); + ILFree(pidlFull); + } + } + else + { + SHChangeNotify(Event, SHCNF_IDLIST, pidlFolder, NULL); + } + ILFree(pidlFolder); +} - EnumerateRecycleBinW(NULL, CBSearchRecycleBin, &Context); - if (Context.bFound) +EXTERN_C void CRecycleBin_NotifyRecycled(LPCWSTR OrigPath, const WIN32_FIND_DATAW *pFind, + const RECYCLEBINFILEIDENTITY *pFI) +{ + DELETED_FILE_INFO info; + info.LastModification = pFind->ftLastWriteTime; + info.DeletionTime = pFI->DeletionTime; + info.FileSize = pFind->nFileSizeLow; + info.Attributes = pFind->dwFileAttributes; + if (LPITEMIDLIST pidl = CreateItem(pFI->RecycledFullPath, OrigPath, info)) { - // This should be executed any time, if not, there are some errors in the implementation - IRecycleBinFile* pRecycleFile = (IRecycleBinFile*)Context.hDeletedFile; + CRecycleBin_ChangeNotifyBBItem(IsFolder(pidl) ? SHCNE_MKDIR : SHCNE_CREATE, pidl); + ILFree(pidl); + } +} - // Query the interface from the private interface - HRESULT hr = pRecycleFile->QueryInterface(riid, ppvOut); +static void CRecycleBin_NotifyRemovedFromRecycleBin(LPCITEMIDLIST BBItem) +{ + CRecycleBin_ChangeNotifyBBItem(IsFolder(BBItem) ? SHCNE_RMDIR : SHCNE_DELETE, BBItem); - // Close the file handle as we don't need it anymore - CloseRecycleBinHandle(Context.hDeletedFile); + CComHeapPtr pidlBB(SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET, FALSE)); + CComPtr pSF; + if (pidlBB && SUCCEEDED(SHBindToObject(NULL, pidlBB, IID_PPV_ARG(IShellFolder, &pSF)))) + { + if (IsRecycleBinEmpty(pSF)) + SHUpdateRecycleBinIcon(); + } +} +static HRESULT CRecyclerExtractIcon_CreateInstance( + IShellFolder &FSFolder, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) +{ + HRESULT hr = FSFolder.GetUIObjectOf(NULL, 1, &pidl, riid, NULL, ppvOut); + if (SUCCEEDED(hr)) return hr; - } -fallback: // In case the search fails we use a default icon ERR("Recycler could not retrieve the icon, this shouldn't happen\n"); - CComPtr initIcon; - HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon)); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - initIcon->SetNormalIcon(swShell32Name, 0); - - return initIcon->QueryInterface(riid, ppvOut); + if (IsFolder(pidl)) + return SHELL_CreateFallbackExtractIconForFolder(riid, ppvOut); + else + return SHELL_CreateFallbackExtractIconForNoAssocFile(riid, ppvOut); } -class CRecycleBinEnum : - public CEnumIDListBase -{ - private: - public: - CRecycleBinEnum(); - ~CRecycleBinEnum(); - HRESULT WINAPI Initialize(DWORD dwFlags); - static BOOL WINAPI CBEnumRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile); - BOOL WINAPI CBEnumRecycleBin(IN HDELFILE hDeletedFile); - - BEGIN_COM_MAP(CRecycleBinEnum) - COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) - END_COM_MAP() -}; - class CRecycleBinItemContextMenu : public CComObjectRootEx, public IContextMenu2 @@ -155,63 +336,23 @@ class CRecycleBinItemContextMenu : END_COM_MAP() }; -BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile) -{ - PSEARCH_CONTEXT pContext = (PSEARCH_CONTEXT)Context; - - PDELETED_FILE_DETAILS_W pFileDetails; - DWORD dwSize; - BOOL ret; - - if (!GetDeletedFileDetailsW(hDeletedFile, - 0, - NULL, - &dwSize) && - GetLastError() != ERROR_INSUFFICIENT_BUFFER) - { - ERR("GetDeletedFileDetailsW failed\n"); - return FALSE; - } - - pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize); - if (!pFileDetails) - { - ERR("No memory\n"); - return FALSE; - } - - if (!GetDeletedFileDetailsW(hDeletedFile, - dwSize, - pFileDetails, - NULL)) - { - ERR("GetDeletedFileDetailsW failed\n"); - SHFree(pFileDetails); - return FALSE; - } - - ret = memcmp(pFileDetails, pContext->pFileDetails, dwSize); - if (!ret) - { - pContext->hDeletedFile = hDeletedFile; - pContext->bFound = TRUE; - } - else - CloseRecycleBinHandle(hDeletedFile); - - SHFree(pFileDetails); - return ret; -} - -static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl) +class CRecycleBinEnum : + public CEnumIDListBase { - LPPIDLDATA pdata = _ILGetDataPointer(pidl); - - if (pdata && pdata->type == 0x00) - return (PIDLRecycleStruct*) & (pdata->u.crecycle); + public: + CRecycleBinEnum(); + ~CRecycleBinEnum(); + HRESULT WINAPI Initialize(DWORD dwFlags); + BOOL CBEnumRecycleBin(IN HDELFILE hDeletedFile); + static BOOL CALLBACK CBEnumRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile) + { + return static_cast(Context)->CBEnumRecycleBin(hDeletedFile); + } - return NULL; -} + BEGIN_COM_MAP(CRecycleBinEnum) + COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList) + END_COM_MAP() +}; CRecycleBinEnum::CRecycleBinEnum() { @@ -223,14 +364,7 @@ CRecycleBinEnum::~CRecycleBinEnum() HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags) { - WCHAR szDrive[8]; - if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) - 1)) - { - ERR("GetEnvironmentVariableW failed\n"); - return E_FAIL; - } - PathAddBackslashW(szDrive); - + LPCWSTR szDrive = GetGlobalRecycleBinPath(); if (dwFlags & SHCONTF_NONFOLDERS) { TRACE("Starting Enumeration\n"); @@ -248,83 +382,25 @@ HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags) return S_OK; } -static LPITEMIDLIST _ILCreateRecycleItem(PDELETED_FILE_DETAILS_W pFileDetails) -{ - PIDLDATA tmp; - LPITEMIDLIST pidl; - PIDLRecycleStruct * p; - int size0 = (char*)&tmp.u.crecycle.szName - (char*)&tmp.u.crecycle; - int size = size0; - - tmp.type = 0x00; - size += (wcslen(pFileDetails->FileName) + 1) * sizeof(WCHAR); - - pidl = (LPITEMIDLIST)SHAlloc(size + 4); - if (!pidl) - return pidl; - - pidl->mkid.cb = size + 2; - memcpy(pidl->mkid.abID, &tmp, 2 + size0); - - p = &((PIDLDATA*)pidl->mkid.abID)->u.crecycle; - RtlCopyMemory(p, pFileDetails, sizeof(DELETED_FILE_DETAILS_W)); - wcscpy(p->szName, pFileDetails->FileName); - *(WORD*)((char*)pidl + (size + 2)) = 0; - return pidl; -} - -BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile) +BOOL CRecycleBinEnum::CBEnumRecycleBin(IN HDELFILE hDeletedFile) { - return static_cast(Context)->CBEnumRecycleBin(hDeletedFile); -} - -BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HDELFILE hDeletedFile) -{ - PDELETED_FILE_DETAILS_W pFileDetails; - DWORD dwSize; LPITEMIDLIST pidl = NULL; - BOOL ret; - - if (!GetDeletedFileDetailsW(hDeletedFile, - 0, - NULL, - &dwSize) && - GetLastError() != ERROR_INSUFFICIENT_BUFFER) + DELETED_FILE_INFO info; + IRecycleBinFile *pRBF = IRecycleBinFileFromHDELFILE(hDeletedFile); + BOOL ret = SUCCEEDED(pRBF->GetInfo(&info)); + if (ret) { - ERR("GetDeletedFileDetailsW failed\n"); - return FALSE; - } - - pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize); - if (!pFileDetails) - { - ERR("No memory\n"); - return FALSE; + pidl = CreateItem(info.RecycledFullPath.String, info.OriginalFullPath.String, info); + ret = pidl != NULL; + FreeRecycleBinString(&info.OriginalFullPath); + FreeRecycleBinString(&info.RecycledFullPath); } - - if (!GetDeletedFileDetailsW(hDeletedFile, - dwSize, - pFileDetails, - NULL)) + if (pidl) { - ERR("GetDeletedFileDetailsW failed\n"); - SHFree(pFileDetails); - return FALSE; + ret = AddToEnumList(pidl); + if (!ret) + ILFree(pidl); } - - pidl = _ILCreateRecycleItem(pFileDetails); - if (!pidl) - { - SHFree(pFileDetails); - return FALSE; - } - - ret = AddToEnumList(pidl); - - if (!ret) - SHFree(pidl); - SHFree(pFileDetails); - TRACE("Returning %d\n", ret); CloseRecycleBinHandle(hDeletedFile); return ret; } @@ -351,99 +427,122 @@ HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl) return S_OK; } +enum { IDC_BB_RESTORE = 1, IDC_BB_CUT, IDC_BB_DELETE, IDC_BB_PROPERTIES }; +static const CMVERBMAP g_BBItemVerbMap[] = +{ + { "undelete", IDC_BB_RESTORE }, + { "cut", IDC_BB_CUT }, + { "delete", IDC_BB_DELETE }, + { "properties", IDC_BB_PROPERTIES }, + { NULL } +}; + HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { - WCHAR szBuffer[30] = {0}; - ULONG Count = 1; + UINT idHigh = 0, id; TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); - if (LoadStringW(shell32_hInstance, IDS_RESTORE, szBuffer, _countof(szBuffer))) + id = idCmdFirst + IDC_BB_RESTORE; + if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING, MAKEINTRESOURCEW(IDS_RESTORE), 0)) { - szBuffer[_countof(szBuffer)-1] = L'\0'; - _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_ENABLED); - Count++; + idHigh = max(idHigh, id); + indexMenu++; } - - if (LoadStringW(shell32_hInstance, IDS_CUT, szBuffer, _countof(szBuffer))) + id = idCmdFirst + IDC_BB_CUT; + if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING, MAKEINTRESOURCEW(IDS_CUT), MFS_DISABLED)) { - _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED); - szBuffer[_countof(szBuffer)-1] = L'\0'; - _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED); + idHigh = max(idHigh, id); + if (_InsertMenuItemW(hMenu, indexMenu++, TRUE, -1, MFT_SEPARATOR, NULL, 0)) + indexMenu++; } - - if (LoadStringW(shell32_hInstance, IDS_DELETE, szBuffer, _countof(szBuffer))) + id = idCmdFirst + IDC_BB_DELETE; + if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING, MAKEINTRESOURCEW(IDS_DELETE), 0)) { - szBuffer[_countof(szBuffer)-1] = L'\0'; - _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED); - _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED); + idHigh = max(idHigh, id); + if (_InsertMenuItemW(hMenu, indexMenu++, TRUE, -1, MFT_SEPARATOR, NULL, 0)) + indexMenu++; } - - if (LoadStringW(shell32_hInstance, IDS_PROPERTIES, szBuffer, _countof(szBuffer))) + id = idCmdFirst + IDC_BB_PROPERTIES; + if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), 0)) { - szBuffer[_countof(szBuffer)-1] = L'\0'; - _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED); - _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_DEFAULT); + idHigh = max(idHigh, id); + if (_InsertMenuItemW(hMenu, indexMenu++, TRUE, -1, MFT_SEPARATOR, NULL, 0)) + indexMenu++; } + return idHigh ? MAKE_HRESULT(SEVERITY_SUCCESS, 0, idHigh - idCmdFirst + 1) : S_OK; +} - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count); +static BOOL ConfirmDelete(LPCMINVOKECOMMANDINFO lpcmi, UINT cidl, LPCITEMIDLIST pidl, const BBITEMDATA &Data) +{ + if (lpcmi->fMask & CMIC_MASK_FLAG_NO_UI) + { + return TRUE; + } + else if (cidl == 1) + { + const UINT ask = IsFolder(pidl) ? ASK_DELETE_FOLDER : ASK_DELETE_FILE; + return SHELL_ConfirmYesNoW(lpcmi->hwnd, ask, GetItemOriginalFileName(Data)); + } + WCHAR buf[MAX_PATH]; + wsprintfW(buf, L"%d", cidl); + return SHELL_ConfirmYesNoW(lpcmi->hwnd, ASK_DELETE_MULTIPLE_ITEM, buf); } HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { - SEARCH_CONTEXT Context; - WCHAR szDrive[8]; - TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd); - if (lpcmi->lpVerb == MAKEINTRESOURCEA(1) || lpcmi->lpVerb == MAKEINTRESOURCEA(5)) - { - Context.pFileDetails = _ILGetRecycleStruct(apidl); - Context.bFound = FALSE; + int CmdId = SHELL_MapContextMenuVerbToCmdId(lpcmi, g_BBItemVerbMap); - if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) - 1)) - { - ERR("GetEnvironmentVariableW failed\n"); - return E_FAIL; - } - PathAddBackslashW(szDrive); + // Handle DefView accelerators + if ((SIZE_T)lpcmi->lpVerb == FCIDM_SHVIEW_CUT) + CmdId = IDC_BB_CUT; + if ((SIZE_T)lpcmi->lpVerb == FCIDM_SHVIEW_DELETE) + CmdId = IDC_BB_DELETE; + if ((SIZE_T)lpcmi->lpVerb == FCIDM_SHVIEW_PROPERTIES) + CmdId = IDC_BB_PROPERTIES; - EnumerateRecycleBinW(szDrive, CBSearchRecycleBin, (PVOID)&Context); - if (!Context.bFound) + if (CmdId == IDC_BB_RESTORE || CmdId == IDC_BB_DELETE) + { + BBITEMDATA *pData = ValidateItem(apidl); + if (!pData && FAILED_UNEXPECTEDLY(E_FAIL)) + return E_FAIL; + HDELFILE hDelFile = GetRecycleBinFileHandleFromItem(*pData); + if (!hDelFile && FAILED_UNEXPECTEDLY(E_FAIL)) return E_FAIL; - BOOL ret = TRUE; - - /* restore file */ - if (lpcmi->lpVerb == MAKEINTRESOURCEA(1)) - ret = RestoreFile(Context.hDeletedFile); - /* delete file */ - else - DeleteFileHandleToRecycleBin(Context.hDeletedFile); + HRESULT hr = S_FALSE; + if (CmdId == IDC_BB_RESTORE) + hr = RestoreFileFromRecycleBin(hDelFile) ? S_OK : E_FAIL; + else if (ConfirmDelete(lpcmi, 1, apidl, *pData)) + hr = DeleteFileInRecycleBin(hDelFile) ? S_OK : E_FAIL; - CloseRecycleBinHandle(Context.hDeletedFile); + if (hr == S_OK) + CRecycleBin_NotifyRemovedFromRecycleBin(apidl); - return (ret ? S_OK : E_FAIL); + CloseRecycleBinHandle(hDelFile); + return hr; } - else if (lpcmi->lpVerb == MAKEINTRESOURCEA(3)) + else if (CmdId == IDC_BB_CUT) { FIXME("implement cut\n"); + SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED); return E_NOTIMPL; } - else if (lpcmi->lpVerb == MAKEINTRESOURCEA(7)) + else if (CmdId == IDC_BB_PROPERTIES) { FIXME("implement properties\n"); + SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED); return E_NOTIMPL; } - - return S_OK; + return E_UNEXPECTED; } HRESULT WINAPI CRecycleBinItemContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) { TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); - - return E_FAIL; + return SHELL_GetCommandStringImpl(idCommand, uFlags, lpszName, uMaxNameLen, g_BBItemVerbMap); } HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -456,11 +555,47 @@ HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wPara CRecycleBin::CRecycleBin() { pidl = NULL; + ZeroMemory(m_pFSFolders, sizeof(m_pFSFolders)); } CRecycleBin::~CRecycleBin() { SHFree(pidl); + for (SIZE_T i = 0; i < _countof(m_pFSFolders); ++i) + { + if (m_pFSFolders[i]) + m_pFSFolders[i]->Release(); + } +} + +IShellFolder* CRecycleBin::GetFSFolderForItem(LPCITEMIDLIST pidl) +{ + int drive = GetItemDriveNumber(pidl); + if (drive < 0) + drive = GetDefaultRecycleDriveNumber(); + if ((UINT)drive >= _countof(m_pFSFolders) && FAILED_UNEXPECTEDLY(E_FAIL)) + return NULL; + + if (!m_pFSFolders[drive]) + { + HRESULT hr; + PERSIST_FOLDER_TARGET_INFO pfti = {}; + if (FAILED_UNEXPECTEDLY(hr = GetRecycleBinPathFromDriveNumber(drive, pfti.szTargetParsingName))) + return NULL; + pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; + pfti.csidl = -1; + CComHeapPtr pidlRoot; + pidlRoot.Attach(SHELL32_CreateSimpleIDListFromPath(pfti.szTargetParsingName, pfti.dwAttributes)); + if (!pidlRoot && FAILED_UNEXPECTEDLY(E_FAIL)) + return NULL; + IShellFolder *psf; + hr = SHELL32_CoCreateInitSF(pidlRoot, &pfti, NULL, &CLSID_ShellFSFolder, IID_PPV_ARG(IShellFolder, &psf)); + if (FAILED(hr)) + return NULL; + m_pFSFolders[drive] = psf; // Reference count is 1 (for the m_pFSFolders cache) + } + m_pFSFolders[drive]->AddRef(); // AddRef for the caller + return m_pFSFolders[drive]; } /************************************************************************* @@ -472,7 +607,7 @@ HRESULT WINAPI CRecycleBin::GetClassID(CLSID *pClassID) TRACE("(%p, %p)\n", this, pClassID); if (pClassID == NULL) return E_INVALIDARG; - memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID)); + *pClassID = GetClassID(); return S_OK; } @@ -490,8 +625,7 @@ HRESULT WINAPI CRecycleBin::Initialize(PCIDLIST_ABSOLUTE pidl) HRESULT WINAPI CRecycleBin::GetCurFolder(PIDLIST_ABSOLUTE *ppidl) { TRACE("\n"); - *ppidl = ILClone(pidl); - return S_OK; + return SHILClone((LPCITEMIDLIST)pidl, ppidl); } /************************************************************************* @@ -503,14 +637,7 @@ HRESULT WINAPI CRecycleBin::ParseDisplayName(HWND hwnd, LPBC pbc, ULONG *pdwAttributes) { FIXME("stub\n"); - return E_NOTIMPL; -} - - -PDELETED_FILE_DETAILS_W -UnpackDetailsFromPidl(LPCITEMIDLIST pidl) -{ - return (PDELETED_FILE_DETAILS_W)&pidl->mkid.abID; + return E_NOTIMPL; // FIXME: Parse "D.ext" } HRESULT WINAPI CRecycleBin::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) @@ -530,40 +657,82 @@ HRESULT WINAPI CRecycleBin::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REF return E_NOTIMPL; } -HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) +static HRESULT CompareCanonical(const BBITEMDATA &Data1, const BBITEMDATA &Data2) { - PIDLRecycleStruct* pData1 = _ILGetRecycleStruct(pidl1); - PIDLRecycleStruct* pData2 = _ILGetRecycleStruct(pidl2); - LPWSTR pName1, pName2; + // This assumes two files with the same original path cannot be deleted at + // the same time (within the FAT/NTFS FILETIME resolution). + int result = CompareFileTime(&Data1.DeletionTime, &Data2.DeletionTime); + if (result == 0) + result = _wcsicmp(GetItemOriginalFullPath(Data1), GetItemOriginalFullPath(Data2)); + return MAKE_COMPARE_HRESULT(result); +} - if(!pData1 || !pData2 || LOWORD(lParam) >= COLUMNS_COUNT) +HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) +{ + UINT column = UINT(lParam & SHCIDS_COLUMNMASK); + if (column >= COLUMNS_COUNT || !_ILGetFSType(pidl1) || !_ILGetFSType(pidl2)) + return E_INVALIDARG; + BBITEMDATA *pData1 = ValidateItem(pidl1), *pData2 = ValidateItem(pidl2); + if ((!pData1 || !pData2) && column != COLUMN_NAME) return E_INVALIDARG; - SHORT result; - LONGLONG diff; - switch (LOWORD(lParam)) + LPCWSTR pName1, pName2; + FILETIME ft1, ft2; + SHFILEINFOW shfi1, shfi2; + int result; + HRESULT hr = CFSFolder::CompareSortFoldersFirst(pidl1, pidl2); + if (SUCCEEDED(hr)) + return hr; + switch (column) { - case 0: /* Name */ - pName1 = PathFindFileNameW(pData1->szName); - pName2 = PathFindFileNameW(pData2->szName); - result = _wcsicmp(pName1, pName2); - break; - case 1: /* Orig. Location */ - result = _wcsicmp(pData1->szName, pData2->szName); + case COLUMN_NAME: + if (pData1 && pData2) + { + if (lParam & SHCIDS_CANONICALONLY) + return CompareCanonical(*pData1, *pData2); + pName1 = GetItemOriginalFileName(*pData1); + pName2 = GetItemOriginalFileName(*pData2); + result = CFSFolder::CompareUiStrings(pName1, pName2); + } + else + { + // We support comparing names even for non-Recycle items because + // SHChangeNotify can broadcast regular FS items. + if (IShellFolder *pSF = GetFSFolderForItem(pidl1)) + { + hr = pSF->CompareIDs(lParam, pidl1, pidl2); + pSF->Release(); + return hr; + } + return E_INVALIDARG; + } break; - case 2: /* Date Deleted */ + case COLUMN_DELFROM: + if (SUCCEEDED(hr = GetItemOriginalFolder(*pData1, const_cast(pName1)))) + { + if (SUCCEEDED(hr = GetItemOriginalFolder(*pData2, const_cast(pName2)))) + { + result = CFSFolder::CompareUiStrings(pName1, pName2); + SHFree(const_cast(pName2)); + } + SHFree(const_cast(pName1)); + } + return SUCCEEDED(hr) ? MAKE_COMPARE_HRESULT(result) : hr; + case COLUMN_DATEDEL: result = CompareFileTime(&pData1->DeletionTime, &pData2->DeletionTime); break; - case 3: /* Size */ - diff = pData1->FileSize.QuadPart - pData2->FileSize.QuadPart; - return MAKE_COMPARE_HRESULT(diff); - case 4: /* Type */ - pName1 = PathFindExtensionW(pData1->szName); - pName2 = PathFindExtensionW(pData2->szName); - result = _wcsicmp(pName1, pName2); + case COLUMN_SIZE: + result = GetItemFileSize(pidl1) - GetItemFileSize(pidl2); + break; + case COLUMN_TYPE: + GetItemTypeName(pidl1, *pData1, shfi1); + GetItemTypeName(pidl2, *pData2, shfi2); + result = CFSFolder::CompareUiStrings(shfi1.szTypeName, shfi2.szTypeName); break; - case 5: /* Modified */ - result = CompareFileTime(&pData1->LastModification, &pData2->LastModification); + case COLUMN_MTIME: + _ILGetFileDateTime(pidl1, &ft1); + _ILGetFileDateTime(pidl2, &ft2); + result = CompareFileTime(&ft1, &ft2); break; } return MAKE_COMPARE_HRESULT(result); @@ -578,7 +747,6 @@ HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void * if (!ppv) return hr; - *ppv = NULL; if (IsEqualIID (riid, IID_IDropTarget)) @@ -587,11 +755,12 @@ HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void * } else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid, IID_IContextMenu2)) { + m_IsBackgroundMenu = true; hr = this->QueryInterface(riid, ppv); } else if (IsEqualIID (riid, IID_IShellView)) { - SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this}; + SFV_CREATE sfvparams = { sizeof(SFV_CREATE), this }; hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppv); } else @@ -606,8 +775,26 @@ HRESULT WINAPI CRecycleBin::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY api SFGAOF *rgfInOut) { TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl, apidl ? apidl[0] : NULL, (unsigned int)*rgfInOut); - *rgfInOut &= SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK; - return S_OK; + HRESULT hr = S_OK; + const SFGAOF ThisFolder = SFGAO_FOLDER | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_CANRENAME | SFGAO_CANLINK; + if (!cidl) + { + *rgfInOut &= ThisFolder; + if (SHRestricted(REST_BITBUCKNOPROP)) + *rgfInOut &= ~SFGAO_HASPROPSHEET; + return hr; + } + SFGAOF remain = SFGAO_LINK & *rgfInOut; + *rgfInOut &= remain | SFGAO_HASPROPSHEET | SFGAO_CANDELETE | SFGAO_FILESYSTEM; // TODO: SFGAO_CANMOVE + for (UINT i = 0; (*rgfInOut & remain) && i < cidl && SUCCEEDED(hr); ++i) + { + if (IShellFolder* pSF = GetFSFolderForItem(apidl[i])) + { + hr = pSF->GetAttributesOf(1, &apidl[i], rgfInOut); + pSF->Release(); + } + } + return hr; } HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, @@ -623,14 +810,20 @@ HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_C return hr; *ppv = NULL; + assert(!cidl || (apidl && apidl[0])); if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1)) { + // FIXME: Handle multiple items hr = ShellObjectCreatorInit(apidl[0], riid, &pObj); } else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1)) { - hr = CRecyclerExtractIcon_CreateInstance(apidl[0], riid, &pObj); + if (IShellFolder *pSF = GetFSFolderForItem(apidl[0])) + { + hr = CRecyclerExtractIcon_CreateInstance(*pSF, apidl[0], riid, &pObj); + pSF->Release(); + } } else hr = E_NOINTERFACE; @@ -645,33 +838,36 @@ HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_C HRESULT WINAPI CRecycleBin::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF uFlags, STRRET *pName) { - PIDLRecycleStruct *pFileDetails; - LPWSTR pFileName; - TRACE("(%p, %p, %x, %p)\n", this, pidl, (unsigned int)uFlags, pName); + const BBITEMDATA *pData = ValidateItem(pidl); + if (!pData) + return E_INVALIDARG; - pFileDetails = _ILGetRecycleStruct(pidl); - if (!pFileDetails) + if (IS_SHGDN_FOR_PARSING(uFlags)) { - pName->cStr[0] = 0; - pName->uType = STRRET_CSTR; - return E_INVALIDARG; + LPCWSTR pszName = GetItemRecycledFullPath(*pData); + if (uFlags & SHGDN_INFOLDER) + pszName = PathFindFileNameW(pszName); + pName->pOleStr = SHStrDupW(pszName); } - - pFileName = wcsrchr(pFileDetails->szName, L'\\'); - if (!pFileName) + else { - pName->cStr[0] = 0; - pName->uType = STRRET_CSTR; - return E_UNEXPECTED; + if (uFlags & SHGDN_INFOLDER) + pName->pOleStr = SHStrDupW(GetItemOriginalFileName(*pData)); + else + pName->pOleStr = SHStrDupW(GetItemOriginalFullPath(*pData)); } - pName->pOleStr = StrDupW(pFileName + 1); - if (pName->pOleStr == NULL) - return E_OUTOFMEMORY; - - pName->uType = STRRET_WSTR; - return S_OK; + if (pName->pOleStr) + { + pName->uType = STRRET_WSTR; + if (!IsFolder(pidl)) + SHELL_FS_ProcessDisplayFilename(pName->pOleStr, uFlags); + return S_OK; + } + pName->uType = STRRET_CSTR; + pName->cStr[0] = '\0'; + return E_OUTOFMEMORY; } HRESULT WINAPI CRecycleBin::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pszName, @@ -719,33 +915,12 @@ HRESULT WINAPI CRecycleBin::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID return E_NOTIMPL; } -static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME * ft) -{ - FILETIME lft; - SYSTEMTIME time; - int ret; - - FileTimeToLocalFileTime(ft, &lft); - FileTimeToSystemTime(&lft, &time); - - ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size); - if (ret > 0 && ret < size) - { - /* Append space + time without seconds */ - buffer[ret-1] = ' '; - GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret); - } - - return (ret != 0 ? E_FAIL : S_OK); -} - HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPSHELLDETAILS pDetails) { - PIDLRecycleStruct * pFileDetails; + HRESULT hr; + FILETIME ft; + SHFILEINFOW shfi; WCHAR buffer[MAX_PATH]; - WCHAR szTypeName[100]; - LPWSTR pszBackslash; - UINT Length; TRACE("(%p, %p, %d, %p)\n", this, pidl, iColumn, pDetails); if (iColumn >= COLUMNS_COUNT) @@ -759,66 +934,36 @@ HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPS } if (iColumn == COLUMN_NAME) - return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str); + return GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &pDetails->str); - pFileDetails = _ILGetRecycleStruct(pidl); - if (!pFileDetails && FAILED_UNEXPECTEDLY(E_INVALIDARG)) + const BBITEMDATA *pData = ValidateItem(pidl); + if (!pData && FAILED_UNEXPECTEDLY(E_INVALIDARG)) return E_INVALIDARG; + switch (iColumn) { case COLUMN_DATEDEL: - FormatDateTime(buffer, MAX_PATH, &pFileDetails->DeletionTime); + CFSFolder::FormatDateTime(pData->DeletionTime, buffer, _countof(buffer)); break; case COLUMN_DELFROM: - pszBackslash = wcsrchr(pFileDetails->szName, L'\\'); - if (!pszBackslash) - { - ERR("Filename '%ls' not a valid path?\n", pFileDetails->szName); - Length = 0; - } - else - { - Length = (pszBackslash - pFileDetails->szName); - memcpy((LPVOID)buffer, pFileDetails->szName, Length * sizeof(WCHAR)); - } - buffer[Length] = UNICODE_NULL; - if (buffer[0] && buffer[1] == L':' && !buffer[2]) - { - buffer[2] = L'\\'; - buffer[3] = UNICODE_NULL; - } - break; + if (SUCCEEDED(hr = GetItemOriginalFolder(*pData, pDetails->str.pOleStr))) + pDetails->str.uType = STRRET_WSTR; + return hr; case COLUMN_SIZE: - StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH); + *buffer = UNICODE_NULL; + if (!IsFolder(pidl)) + CFSFolder::FormatSize(GetItemFileSize(pidl), buffer, _countof(buffer)); break; case COLUMN_MTIME: - FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification); + _ILGetFileDateTime(pidl, &ft); + CFSFolder::FormatDateTime(ft, buffer, _countof(buffer)); break; case COLUMN_TYPE: - { - SEARCH_CONTEXT Context; - Context.pFileDetails = pFileDetails; - Context.bFound = FALSE; - EnumerateRecycleBinW(NULL, CBSearchRecycleBin, (PVOID)&Context); - - if (Context.bFound) - { - GetDeletedFileTypeNameW(Context.hDeletedFile, buffer, _countof(buffer), NULL); - - CloseRecycleBinHandle(Context.hDeletedFile); - } - /* load localized file string */ - else if (LoadStringW(shell32_hInstance, IDS_ANY_FILE, szTypeName, _countof(szTypeName))) - { - StringCchPrintfW(buffer, _countof(buffer), szTypeName, PathFindExtensionW(pFileDetails->szName)); - } - - return SHSetStrRet(&pDetails->str, buffer); - } + GetItemTypeName(pidl, *pData, shfi); + return SHSetStrRet(&pDetails->str, shfi.szTypeName); default: return E_FAIL; } - return SHSetStrRet(&pDetails->str, buffer); } @@ -832,85 +977,75 @@ HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) return S_OK; } -BOOL CRecycleBin::RecycleBinIsEmpty() -{ - CComPtr spEnumFiles; - HRESULT hr = EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spEnumFiles); - if (FAILED(hr)) - return TRUE; - CComHeapPtr spPidl; - ULONG itemcount; - return spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK; - } - /************************************************************************* * RecycleBin IContextMenu interface */ -HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) +enum { IDC_EMPTYRECYCLEBIN = 1, IDC_PROPERTIES }; +static const CMVERBMAP g_BBFolderVerbMap[] = { - WCHAR szBuffer[100]; - MENUITEMINFOW mii; - int id = 1; + { "empty", IDC_EMPTYRECYCLEBIN }, + { "properties", IDC_PROPERTIES }, + { NULL } +}; +HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) +{ TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags ); if (!hMenu) return E_INVALIDARG; - ZeroMemory(&mii, sizeof(mii)); - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE; - mii.fState = RecycleBinIsEmpty() ? MFS_DISABLED : MFS_ENABLED; - szBuffer[0] = L'\0'; - LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, _countof(szBuffer)); - mii.dwTypeData = szBuffer; - mii.cch = wcslen(mii.dwTypeData); - mii.wID = idCmdFirst + id++; - mii.fType = MFT_STRING; - iIdEmpty = 1; - - if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) - return E_FAIL; + UINT idHigh = 0, id; - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id); + WORD state = IsRecycleBinEmpty(this) ? MFS_DISABLED : MFS_ENABLED; + id = idCmdFirst + IDC_EMPTYRECYCLEBIN; + if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING, MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET), state)) + { + idHigh = max(idHigh, id); + if (m_IsBackgroundMenu && !SHRestricted(REST_BITBUCKNOPROP)) + { + id = idCmdFirst + IDC_PROPERTIES; + if (_InsertMenuItemW(hMenu, ++indexMenu, TRUE, id, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), 0)) + { + idHigh = max(idHigh, id); + _InsertMenuItemW(hMenu, indexMenu++, TRUE, -1, MFT_SEPARATOR, NULL, 0); + } + } + } + return idHigh ? MAKE_HRESULT(SEVERITY_SUCCESS, 0, idHigh - idCmdFirst + 1) : S_OK; } HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { - HRESULT hr; - LPSHELLBROWSER lpSB; - IShellView * lpSV = NULL; - WCHAR szDrive[8]; - - TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb); - - if (LOWORD(lpcmi->lpVerb) == iIdEmpty) + TRACE("%p %p verb %p\n", this, lpcmi, lpcmi ? lpcmi->lpVerb : NULL); + int CmdId = SHELL_MapContextMenuVerbToCmdId(lpcmi, g_BBFolderVerbMap); + if (CmdId == IDC_EMPTYRECYCLEBIN) { - if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) - 1)) - { - ERR("GetEnvironmentVariableW failed\n"); - return E_FAIL; - } - PathAddBackslashW(szDrive); - - hr = SHEmptyRecycleBinW(lpcmi->hwnd, szDrive, 0); + HRESULT hr = SHEmptyRecycleBinW(lpcmi->hwnd, NULL, 0); TRACE("result %x\n", hr); if (hr != S_OK) return hr; - - lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0); - if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV))) - lpSV->Refresh(); +#if 0 // This is a nasty hack because lpcmi->hwnd might not be a shell browser. + // Not required with working SHChangeNotify. + CComPtr pSV; + LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessage(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0); + if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&pSV))) + pSV->Refresh(); +#endif + return hr; } - return S_OK; + else if (CmdId == IDC_PROPERTIES) + { + return SHELL_ShowItemIDListProperties((LPITEMIDLIST)CSIDL_BITBUCKET); + } + return E_INVALIDARG; } HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) { - FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); - - return E_NOTIMPL; + TRACE("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); + return SHELL_GetCommandStringImpl(idCommand, uFlags, lpszName, uMaxNameLen, g_BBFolderVerbMap); } /************************************************************************* @@ -937,6 +1072,7 @@ HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pf HRESULT WINAPI CRecycleBin::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) { TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID ); + m_IsBackgroundMenu = false; return S_OK; } @@ -982,7 +1118,7 @@ TRASH_CanTrashFile(LPCWSTR wszPath) swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber)); wcscat(szKey, szBuffer); - if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS) + if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS) { ERR("RegCreateKeyExW failed\n"); return FALSE; @@ -996,8 +1132,6 @@ TRASH_CanTrashFile(LPCWSTR wszPath) /* per default unlimited size */ dwSize = -1; RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD)); - RegCloseKey(hKey); - return TRUE; } else { @@ -1005,27 +1139,18 @@ TRASH_CanTrashFile(LPCWSTR wszPath) ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize); if (ret != ERROR_SUCCESS) { - if (ret == ERROR_FILE_NOT_FOUND) + dwNukeOnDelete = 0; + if (ret == ERROR_FILE_NOT_FOUND) { /* restore key and enable bitbucket */ - dwNukeOnDelete = 0; RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD)); } - RegCloseKey(hKey); - return TRUE; } - else if (dwNukeOnDelete) - { - /* do not delete to bitbucket */ - RegCloseKey(hKey); - return FALSE; - } - /* FIXME - * check if bitbucket is full - */ - RegCloseKey(hKey); - return TRUE; } + BOOL bCanTrash = !dwNukeOnDelete; + // FIXME: Check if bitbucket is full (CORE-13743) + RegCloseKey(hKey); + return bCanTrash; } BOOL @@ -1178,7 +1303,7 @@ HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags) while (penumFiles->Next(1, &pidl, NULL) == S_OK) { count++; - pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &StrRet); + pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &StrRet); StrRetToBuf(&StrRet, pidl, szBuffer, _countof(szBuffer)); CoTaskMemFree(pidl); } @@ -1228,6 +1353,7 @@ HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags) if (!ret) return HRESULT_FROM_WIN32(GetLastError()); + CRecycleBin_ChangeNotifyBBItem(SHCNE_UPDATEDIR, NULL); if (!(dwFlags & SHERB_NOSOUND)) { TRASH_PlayEmptyRecycleBinSound(); diff --git a/dll/win32/shell32/folders/CRecycleBin.h b/dll/win32/shell32/folders/CRecycleBin.h index ecf56699447bd..7d676cd687551 100644 --- a/dll/win32/shell32/folders/CRecycleBin.h +++ b/dll/win32/shell32/folders/CRecycleBin.h @@ -37,12 +37,15 @@ class CRecycleBin : { private: LPITEMIDLIST pidl; - INT iIdEmpty; - BOOL RecycleBinIsEmpty(); + IShellFolder *m_pFSFolders[RECYCLEBINMAXDRIVECOUNT]; + bool m_IsBackgroundMenu; + + IShellFolder* GetFSFolderForItem(LPCITEMIDLIST pidl); public: CRecycleBin(); ~CRecycleBin(); + static inline REFCLSID GetClassID() { return CLSID_RecycleBin; } // IPersistFolder STDMETHOD(GetClassID)(CLSID *pClassID) override; diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h index a28c023d244e0..48c636fe9f1b3 100644 --- a/dll/win32/shell32/precomp.h +++ b/dll/win32/shell32/precomp.h @@ -262,6 +262,12 @@ SHBindToObjectEx( _In_ REFIID riid, _Out_ void **ppvObj); +EXTERN_C HRESULT +SHELL_GetUIObjectOfAbsoluteItem( + _In_opt_ HWND hWnd, + _In_ PCIDLIST_ABSOLUTE pidl, + _In_ REFIID riid, _Out_ void **ppvObj); + DWORD SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes); HRESULT SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl, _Out_ PIDLIST_ABSOLUTE *ppidl); diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin.c b/dll/win32/shell32/shellrecyclebin/recyclebin.c index 88221b0da99cd..5a0153bacf5db 100644 --- a/dll/win32/shell32/shellrecyclebin/recyclebin.c +++ b/dll/win32/shell32/shellrecyclebin/recyclebin.c @@ -12,7 +12,7 @@ BOOL WINAPI CloseRecycleBinHandle( IN HDELFILE hDeletedFile) { - IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile; + IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDeletedFile); HRESULT hr; TRACE("(%p)\n", hDeletedFile); @@ -90,7 +90,7 @@ DeleteFileToRecycleBinW( } BOOL WINAPI -DeleteFileHandleToRecycleBin( +DeleteFileInRecycleBin( IN HDELFILE hDeletedFile) { IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile; @@ -231,9 +231,10 @@ EnumerateRecycleBinW( } else if (!SUCCEEDED(hr)) goto cleanup; - if (!pFnCallback(Context, (HANDLE)prbf)) + if (!pFnCallback(Context, (HDELFILE)prbf)) { - hr = HRESULT_FROM_WIN32(GetLastError()); + UINT error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); goto cleanup; } } @@ -252,132 +253,38 @@ EnumerateRecycleBinW( return FALSE; } -BOOL WINAPI -GetDeletedFileTypeNameW( - IN HDELFILE hDeletedFile, - OUT LPWSTR pTypeName, - IN DWORD BufferSize, - OUT LPDWORD RequiredSize OPTIONAL) +typedef struct _BBENUMFILECONTEXT { - IRecycleBinFile *prbf = (IRecycleBinFile *)hDeletedFile; - SIZE_T FinalSize; - - HRESULT hr = IRecycleBinFile_GetTypeName(prbf, BufferSize, pTypeName, &FinalSize); - - if (SUCCEEDED(hr)) - { - if (RequiredSize) - *RequiredSize = (DWORD)FinalSize; + const RECYCLEBINFILEIDENTITY *pFI; + HDELFILE hDelFile; +} BBENUMFILECONTEXT; - return TRUE; - } - if (HRESULT_FACILITY(hr) == FACILITY_WIN32) - SetLastError(HRESULT_CODE(hr)); - else - SetLastError(ERROR_GEN_FAILURE); - return FALSE; -} - -BOOL WINAPI -GetDeletedFileDetailsA( - IN HDELFILE hDeletedFile, - IN DWORD BufferSize, - IN OUT PDELETED_FILE_DETAILS_A FileDetails OPTIONAL, - OUT LPDWORD RequiredSize OPTIONAL) +static BOOL CALLBACK +GetRecycleBinFileHandleCallback(IN PVOID Context, IN HDELFILE hDeletedFile) { - PDELETED_FILE_DETAILS_W FileDetailsW = NULL; - DWORD BufferSizeW = 0; - BOOL ret = FALSE; - - TRACE("(%p, %lu, %p, %p)\n", hDeletedFile, BufferSize, FileDetails, RequiredSize); - - if (BufferSize >= FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)) + BBENUMFILECONTEXT *pCtx = (BBENUMFILECONTEXT*)Context; + IRecycleBinFile *pRBF = IRecycleBinFileFromHDELFILE(hDeletedFile); + if (IRecycleBinFile_IsEqualIdentity(pRBF, pCtx->pFI) == S_OK) { - BufferSizeW = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName) - + (BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)) * sizeof(WCHAR); + pCtx->hDelFile = hDeletedFile; + return FALSE; } - if (FileDetails && BufferSizeW) - { - FileDetailsW = HeapAlloc(GetProcessHeap(), 0, BufferSizeW); - if (!FileDetailsW) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto cleanup; - } - } - - ret = GetDeletedFileDetailsW(hDeletedFile, BufferSizeW, FileDetailsW, RequiredSize); - if (!ret) - goto cleanup; - - if (FileDetails) - { - CopyMemory(FileDetails, FileDetailsW, FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)); - if (0 == WideCharToMultiByte(CP_ACP, 0, FileDetailsW->FileName, -1, FileDetails->FileName, BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName), NULL, NULL)) - goto cleanup; - } - ret = TRUE; - -cleanup: - HeapFree(GetProcessHeap(), 0, FileDetailsW); - return ret; + CloseRecycleBinHandle(hDeletedFile); + return TRUE; } -BOOL WINAPI -GetDeletedFileDetailsW( - IN HDELFILE hDeletedFile, - IN DWORD BufferSize, - IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL, - OUT LPDWORD RequiredSize OPTIONAL) +EXTERN_C HDELFILE +GetRecycleBinFileHandle( + IN LPCWSTR pszRoot OPTIONAL, + IN const RECYCLEBINFILEIDENTITY *pFI) { - IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile; - HRESULT hr; - SIZE_T NameSize, Needed; - - TRACE("(%p, %lu, %p, %p)\n", hDeletedFile, BufferSize, FileDetails, RequiredSize); - - hr = IRecycleBinFile_GetFileName(rbf, 0, NULL, &NameSize); - if (!SUCCEEDED(hr)) - goto cleanup; - Needed = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName) + NameSize; - if (RequiredSize) - *RequiredSize = (DWORD)Needed; - if (Needed > BufferSize) - { - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - goto cleanup; - } - hr = IRecycleBinFile_GetFileName(rbf, NameSize, FileDetails->FileName, NULL); - if (!SUCCEEDED(hr)) - goto cleanup; - hr = IRecycleBinFile_GetLastModificationTime(rbf, &FileDetails->LastModification); - if (!SUCCEEDED(hr)) - goto cleanup; - hr = IRecycleBinFile_GetDeletionTime(rbf, &FileDetails->DeletionTime); - if (!SUCCEEDED(hr)) - goto cleanup; - hr = IRecycleBinFile_GetFileSize(rbf, &FileDetails->FileSize); - if (!SUCCEEDED(hr)) - goto cleanup; - hr = IRecycleBinFile_GetPhysicalFileSize(rbf, &FileDetails->PhysicalFileSize); - if (!SUCCEEDED(hr)) - goto cleanup; - hr = IRecycleBinFile_GetAttributes(rbf, &FileDetails->Attributes); - if (!SUCCEEDED(hr)) - goto cleanup; - -cleanup: - if (SUCCEEDED(hr)) - return TRUE; - if (HRESULT_FACILITY(hr) == FACILITY_WIN32) - SetLastError(HRESULT_CODE(hr)); - else - SetLastError(ERROR_GEN_FAILURE); - return FALSE; + BBENUMFILECONTEXT context = { pFI, NULL }; + EnumerateRecycleBinW(pszRoot, GetRecycleBinFileHandleCallback, &context); + return context.hDelFile; } BOOL WINAPI -RestoreFile( +RestoreFileFromRecycleBin( IN HDELFILE hDeletedFile) { IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile; @@ -395,7 +302,7 @@ RestoreFile( return FALSE; } -HRESULT WINAPI +EXTERN_C HRESULT GetDefaultRecycleBin( IN LPCWSTR pszVolume OPTIONAL, OUT IRecycleBin **pprb) @@ -425,3 +332,17 @@ GetDefaultRecycleBin( IUnknown_Release(pUnk); return hr; } + +EXTERN_C HRESULT +GetRecycleBinPathFromDriveNumber(UINT Drive, LPWSTR Path) +{ + const WCHAR volume[] = { LOWORD('A' + Drive), ':', '\\', '\0' }; + IRecycleBin *pRB; + HRESULT hr = GetDefaultRecycleBin(volume, &pRB); + if (SUCCEEDED(hr)) + { + hr = IRecycleBin_GetDirectory(pRB, Path); + IRecycleBin_Release(pRB); + } + return hr; +} diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin.h b/dll/win32/shell32/shellrecyclebin/recyclebin.h index a48f49264cf3a..b3795b04ebc6c 100644 --- a/dll/win32/shell32/shellrecyclebin/recyclebin.h +++ b/dll/win32/shell32/shellrecyclebin/recyclebin.h @@ -17,48 +17,64 @@ extern "C" { #include #include -#define ANY_SIZE 1 +#define RECYCLEBINMAXDRIVECOUNT 26 /* Structures used by the API Interface */ -typedef struct _DELETED_FILE_DETAILS_A +typedef UINT RECYCLEBINFILESIZETYPE; + +typedef struct _RECYCLEBINFILEIDENTITY { - FILETIME LastModification; - FILETIME DeletionTime; - ULARGE_INTEGER FileSize; - ULARGE_INTEGER PhysicalFileSize; - DWORD Attributes; - CHAR FileName[ANY_SIZE]; -} DELETED_FILE_DETAILS_A, *PDELETED_FILE_DETAILS_A; -typedef struct _DELETED_FILE_DETAILS_W + FILETIME DeletionTime; + LPCWSTR RecycledFullPath; /* "C:\Recycled\Dc1.ext" etc. */ +} RECYCLEBINFILEIDENTITY, *PRECYCLEBINFILEIDENTITY; + +typedef struct _RECYCLEBINSTRING { - FILETIME LastModification; - FILETIME DeletionTime; - ULARGE_INTEGER FileSize; - ULARGE_INTEGER PhysicalFileSize; - DWORD Attributes; - WCHAR FileName[ANY_SIZE]; -} DELETED_FILE_DETAILS_W, *PDELETED_FILE_DETAILS_W; -#ifdef UNICODE -#define DELETED_FILE_DETAILS DELETED_FILE_DETAILS_W -#define PDELETED_FILE_DETAILS PDELETED_FILE_DETAILS_W -#else -#define DELETED_FILE_DETAILS DELETED_FILE_DETAILS_A -#define PDELETED_FILE_DETAILS PDELETED_FILE_DETAILS_A -#endif + LPCWSTR String; + LPWSTR Alloc; +} RECYCLEBINSTRING, *PRECYCLEBINSTRING; + +typedef struct _DELETED_FILE_INFO +{ + FILETIME LastModification; + FILETIME DeletionTime; + RECYCLEBINFILESIZETYPE FileSize; + DWORD Attributes; + RECYCLEBINSTRING OriginalFullPath; + RECYCLEBINSTRING RecycledFullPath; +} DELETED_FILE_INFO, *PDELETED_FILE_INFO; /* Distinct handle type for deleted file/folder */ DECLARE_HANDLE(HDELFILE); +#define IRecycleBinFileFromHDELFILE(hDF) ( (IRecycleBinFile*)(hDF) ) /* API Interface */ +static inline void +FreeRecycleBinString(PRECYCLEBINSTRING pRBS) +{ + SHFree(pRBS->Alloc); + pRBS->String = pRBS->Alloc = NULL; +} + +static inline void +InitializeRecycleBinStringRef(PRECYCLEBINSTRING pRBS, LPCWSTR String) +{ + pRBS->String = String; + pRBS->Alloc = NULL; +} + +EXTERN_C HRESULT +GetRecycleBinPathFromDriveNumber(UINT Drive, LPWSTR Path); + /* Function called for each deleted file in the recycle bin * Context: value given by the caller of the EnumerateRecycleBin function * hDeletedFile: a handle to the deleted file * Returning FALSE stops the enumeration. * Remarks: the handle must be closed with the CloseRecycleBinHandle function */ -typedef BOOL (WINAPI *PENUMERATE_RECYCLEBIN_CALLBACK)(IN PVOID Context, IN HDELFILE hDeletedFile); +typedef BOOL (CALLBACK *PENUMERATE_RECYCLEBIN_CALLBACK)(IN PVOID Context, IN HDELFILE hDeletedFile); /* Closes a file deleted handle. * hDeletedFile: the handle to close @@ -85,13 +101,13 @@ DeleteFileToRecycleBinW( #define DeleteFileToRecycleBin DeleteFileToRecycleBinA #endif -/* Moves a file to the recycle bin. +/* Deletes a file in the recycle bin. * hDeletedFile: handle of the deleted file to delete * Returns TRUE if operation succeeded, FALSE otherwise. * Remark: The handle is obtained in the PENUMERATE_RECYCLEBIN_CALLBACK callback */ BOOL WINAPI -DeleteFileHandleToRecycleBin( +DeleteFileInRecycleBin( IN HDELFILE hDeletedFile); /* Removes all elements contained in a recycle bin @@ -134,38 +150,10 @@ EnumerateRecycleBinW( #define EnumerateRecycleBin EnumerateRecycleBinA #endif -BOOL WINAPI -GetDeletedFileTypeNameW( - IN HDELFILE hDeletedFile, - OUT LPWSTR pTypeName, - IN DWORD BufferSize, - OUT LPDWORD RequiredSize OPTIONAL); - -/* Gets details about a deleted file - * hDeletedFile: handle of the deleted file to get details about - * BufferSize: size of the 'FileDetails' buffer, in bytes - * FileDetails: if the function succeeded, contains details about the deleted file - * RequiredSize: contains the minimal buffer size required to get file information details - * Returns TRUE if operation succeeded, FALSE otherwise. - * Remark: The handle is obtained in the PENUMERATE_RECYCLEBIN_CALLBACK callback - */ -BOOL WINAPI -GetDeletedFileDetailsA( - IN HDELFILE hDeletedFile, - IN DWORD BufferSize, - IN OUT PDELETED_FILE_DETAILS_A FileDetails OPTIONAL, - OUT LPDWORD RequiredSize OPTIONAL); -BOOL WINAPI -GetDeletedFileDetailsW( - IN HDELFILE hDeletedFile, - IN DWORD BufferSize, - IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL, - OUT LPDWORD RequiredSize OPTIONAL); -#ifdef UNICODE -#define GetDeletedFileDetails GetDeletedFileDetailsW -#else -#define GetDeletedFileDetails GetDeletedFileDetailsA -#endif +EXTERN_C HDELFILE +GetRecycleBinFileHandle( + IN LPCWSTR pszRoot OPTIONAL, + IN const RECYCLEBINFILEIDENTITY *pFI); /* Restores a deleted file * hDeletedFile: handle of the deleted file to restore @@ -173,7 +161,7 @@ GetDeletedFileDetailsW( * Remarks: if the function succeeds, the handle is not valid anymore. */ BOOL WINAPI -RestoreFile( +RestoreFileFromRecycleBin( IN HDELFILE hDeletedFile); /* COM interface */ @@ -189,13 +177,14 @@ DECLARE_INTERFACE_(IRecycleBinFile, IUnknown) STDMETHOD_(ULONG, Release)(THIS) PURE; /* IRecycleBinFile methods */ + STDMETHOD(IsEqualIdentity)(THIS_ const RECYCLEBINFILEIDENTITY *pFI) PURE; + STDMETHOD(GetInfo)(THIS_ PDELETED_FILE_INFO pInfo) PURE; STDMETHOD(GetLastModificationTime)(THIS_ FILETIME *pLastModificationTime) PURE; STDMETHOD(GetDeletionTime)(THIS_ FILETIME *pDeletionTime) PURE; STDMETHOD(GetFileSize)(THIS_ ULARGE_INTEGER *pFileSize) PURE; STDMETHOD(GetPhysicalFileSize)(THIS_ ULARGE_INTEGER *pPhysicalFileSize) PURE; STDMETHOD(GetAttributes)(THIS_ DWORD *pAttributes) PURE; STDMETHOD(GetFileName)(THIS_ SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) PURE; - STDMETHOD(GetTypeName)(THIS_ SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) PURE; STDMETHOD(Delete)(THIS) PURE; STDMETHOD(Restore)(THIS) PURE; @@ -233,9 +222,10 @@ DECLARE_INTERFACE_(IRecycleBin, IUnknown) STDMETHOD_(ULONG, Release)(THIS) PURE; /* IRecycleBin methods */ - STDMETHOD(DeleteFile)(THIS_ LPCWSTR szFileName); - STDMETHOD(EmptyRecycleBin)(THIS); - STDMETHOD(EnumObjects)(THIS_ IRecycleBinEnumList **ppEnumList); + STDMETHOD(DeleteFile)(THIS_ LPCWSTR szFileName) PURE; + STDMETHOD(EmptyRecycleBin)(THIS) PURE; + STDMETHOD(EnumObjects)(THIS_ IRecycleBinEnumList **ppEnumList) PURE; + STDMETHOD(GetDirectory)(THIS_ LPWSTR szPath) PURE; END_INTERFACE }; @@ -252,6 +242,10 @@ EXTERN_C const IID IID_IRecycleBin; (This)->lpVtbl->AddRef(This) #define IRecycleBinFile_Release(This) \ (This)->lpVtbl->Release(This) +#define IRecycleBinFile_IsEqualIdentity(This, pFI) \ + (This)->lpVtbl->IsEqualIdentity(This, pFI) +#define IRecycleBinFile_GetInfo(This, pInfo) \ + (This)->lpVtbl->GetInfo(This, pInfo) #define IRecycleBinFile_GetLastModificationTime(This, pLastModificationTime) \ (This)->lpVtbl->GetLastModificationTime(This, pLastModificationTime) #define IRecycleBinFile_GetDeletionTime(This, pDeletionTime) \ @@ -264,8 +258,6 @@ EXTERN_C const IID IID_IRecycleBin; (This)->lpVtbl->GetAttributes(This, pAttributes) #define IRecycleBinFile_GetFileName(This, BufferSize, Buffer, RequiredSize) \ (This)->lpVtbl->GetFileName(This, BufferSize, Buffer, RequiredSize) -#define IRecycleBinFile_GetTypeName(This, BufferSize, Buffer, RequiredSize) \ - (This)->lpVtbl->GetTypeName(This, BufferSize, Buffer, RequiredSize) #define IRecycleBinFile_Delete(This) \ (This)->lpVtbl->Delete(This) #define IRecycleBinFile_Restore(This) \ @@ -296,13 +288,20 @@ EXTERN_C const IID IID_IRecycleBin; (This)->lpVtbl->EmptyRecycleBin(This) #define IRecycleBin_EnumObjects(This, ppEnumList) \ (This)->lpVtbl->EnumObjects(This, ppEnumList) +#define IRecycleBin_GetDirectory(This, szPath) \ + (This)->lpVtbl->GetDirectory(This, szPath) #endif -HRESULT WINAPI +EXTERN_C HRESULT GetDefaultRecycleBin( IN LPCWSTR pszVolume OPTIONAL, OUT IRecycleBin **pprb); +/* Recycle Bin shell folder internal API */ +void CRecycleBin_NotifyRecycled(LPCWSTR OrigPath, const WIN32_FIND_DATAW *pFind, + const RECYCLEBINFILEIDENTITY *pFI); + + #ifdef __cplusplus } #endif diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_generic.cpp b/dll/win32/shell32/shellrecyclebin/recyclebin_generic.cpp index a7ad1b00de017..09f561f40b674 100644 --- a/dll/win32/shell32/shellrecyclebin/recyclebin_generic.cpp +++ b/dll/win32/shell32/shellrecyclebin/recyclebin_generic.cpp @@ -23,6 +23,10 @@ class RecycleBinGeneric : public IRecycleBin STDMETHODIMP DeleteFile(LPCWSTR szFileName) override; STDMETHODIMP EmptyRecycleBin() override; STDMETHODIMP EnumObjects(IRecycleBinEnumList **ppEnumList) override; + STDMETHODIMP GetDirectory(LPWSTR szPath) override + { + return E_UNEXPECTED; + } protected: LONG m_ref; @@ -183,3 +187,11 @@ HRESULT RecycleBinGeneric_Constructor(OUT IUnknown **ppUnknown) *ppUnknown = static_cast(pThis); return S_OK; } + +EXTERN_C +BOOL RecycleBinGeneric_IsEqualFileIdentity(const RECYCLEBINFILEIDENTITY *p1, const RECYCLEBINFILEIDENTITY *p2) +{ + return p1->DeletionTime.dwLowDateTime == p2->DeletionTime.dwLowDateTime && + p1->DeletionTime.dwHighDateTime == p2->DeletionTime.dwHighDateTime && + _wcsicmp(p1->RecycledFullPath, p2->RecycledFullPath) == 0; +} diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_private.h b/dll/win32/shell32/shellrecyclebin/recyclebin_private.h index 42cffd2265665..57e440d389940 100644 --- a/dll/win32/shell32/shellrecyclebin/recyclebin_private.h +++ b/dll/win32/shell32/shellrecyclebin/recyclebin_private.h @@ -13,11 +13,20 @@ #include WINE_DEFAULT_DEBUG_CHANNEL(recyclebin); +#ifdef __cplusplus +static inline HRESULT HResultFromWin32(DWORD hr) +{ + // HRESULT_FROM_WIN32 will evaluate its parameter twice, this function will not. + return HRESULT_FROM_WIN32(hr); +} +#endif + /* Defines */ #define RECYCLE_BIN_DIRECTORY_WITH_ACL L"RECYCLER" #define RECYCLE_BIN_DIRECTORY_WITHOUT_ACL L"RECYCLED" #define RECYCLE_BIN_FILE_NAME L"INFO2" +#define RECYCLE_BIN_FILE_NAME_V1 L"INFO" #define ROUND_UP(N, S) ((( (N) + (S) - 1) / (S) ) * (S) ) @@ -43,6 +52,9 @@ typedef struct _INFO2_HEADER EXTERN_C HRESULT RecycleBinGeneric_Constructor(OUT IUnknown **ppUnknown); +EXTERN_C +BOOL RecycleBinGeneric_IsEqualFileIdentity(const RECYCLEBINFILEIDENTITY *p1, const RECYCLEBINFILEIDENTITY *p2); + /* recyclebin_generic_enumerator.c */ EXTERN_C diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp index c7009f94d9ddd..b040851d01144 100644 --- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp +++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp @@ -143,6 +143,13 @@ class RecycleBin5 : public IRecycleBin5 STDMETHODIMP DeleteFile(_In_ LPCWSTR szFileName) override; STDMETHODIMP EmptyRecycleBin() override; STDMETHODIMP EnumObjects(_Out_ IRecycleBinEnumList **ppEnumList) override; + STDMETHODIMP GetDirectory(LPWSTR szPath) override + { + if (!m_Folder[0]) + return E_UNEXPECTED; + lstrcpynW(szPath, m_Folder, MAX_PATH); + return S_OK; + } /* IRecycleBin5 interface */ STDMETHODIMP Delete( @@ -226,6 +233,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) SYSTEMTIME SystemTime; DWORD ClusterSize, BytesPerSector, SectorsPerCluster; HRESULT hr; + WIN32_FIND_DATAW wfd = {}; TRACE("(%p, %s)\n", this, debugstr_w(szFileName)); @@ -240,7 +248,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) { if (szFullName) CoTaskMemFree(szFullName); - return HRESULT_FROM_WIN32(GetLastError()); + return HResultFromWin32(GetLastError()); } else if (len < dwBufferLength) break; @@ -257,7 +265,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) if (dwAttributes == INVALID_FILE_ATTRIBUTES) { CoTaskMemFree(szFullName); - return HRESULT_FROM_WIN32(GetLastError()); + return HResultFromWin32(GetLastError()); } if (dwBufferLength < 2 || szFullName[1] != ':') @@ -270,7 +278,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) hFile = CreateFileW(szFullName, 0, 0, NULL, OPEN_EXISTING, (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_FLAG_BACKUP_SEMANTICS : 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); goto cleanup; } @@ -281,7 +289,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); if (!m_hInfoMapped) { - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); goto cleanup; } @@ -289,7 +297,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) pHeader = (PINFO2_HEADER)MapViewOfFile(m_hInfoMapped, FILE_MAP_WRITE, 0, 0, 0); if (!pHeader) { - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); goto cleanup; } @@ -297,7 +305,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart); if (FileSize.u.LowPart < sizeof(INFO2_HEADER)) { - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); goto cleanup; } dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)) - 1; @@ -307,14 +315,14 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) #if 0 if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&FileSize)) { - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); goto cleanup; } #else FileSize.u.LowPart = GetFileSize(hFile, &FileSize.u.HighPart); if (FileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) { - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); goto cleanup; } #endif @@ -350,7 +358,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) /* Get cluster size */ if (!GetDiskFreeSpaceW(m_VolumePath, &SectorsPerCluster, &BytesPerSector, NULL, NULL)) { - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); goto cleanup; } ClusterSize = BytesPerSector * SectorsPerCluster; @@ -359,7 +367,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) GetSystemTime(&SystemTime); if (!SystemTimeToFileTime(&SystemTime, &pDeletedFile->DeletionTime)) { - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); goto cleanup; } pDeletedFile->dwPhysicalFileSize = ROUND_UP(FileSize.u.LowPart, ClusterSize); @@ -373,11 +381,21 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) goto cleanup; } + wfd.dwFileAttributes = dwAttributes; + wfd.nFileSizeLow = FileSize.u.LowPart; + GetFileTime(hFile, &wfd.ftCreationTime, &wfd.ftLastAccessTime, &wfd.ftLastWriteTime); + /* Move file */ if (MoveFileW(szFullName, DeletedFileName)) hr = S_OK; else - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); + + if (SUCCEEDED(hr)) + { + RECYCLEBINFILEIDENTITY ident = { pDeletedFile->DeletionTime, DeletedFileName }; + CRecycleBin_NotifyRecycled(szFullName, &wfd, &ident); + } cleanup: if (pHeader) diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h index f33b3adbe540c..2461e0bdbca2a 100644 --- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h +++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h @@ -28,7 +28,7 @@ typedef interface IRecycleBin5 IRecycleBin5; EXTERN_C const IID IID_IRecycleBin5; #define INTERFACE IRecycleBin5 -DECLARE_INTERFACE_(IRecycleBin5, IUnknown) +DECLARE_INTERFACE_(IRecycleBin5, IRecycleBin) { BEGIN_INTERFACE @@ -41,6 +41,7 @@ DECLARE_INTERFACE_(IRecycleBin5, IUnknown) STDMETHOD(DeleteFile)(THIS_ IN LPCWSTR szFileName) PURE; STDMETHOD(EmptyRecycleBin)(THIS); STDMETHOD(EnumObjects)(THIS_ OUT IRecycleBinEnumList **ppEnumList) PURE; + STDMETHOD(GetDirectory)(THIS_ LPWSTR szPath) PURE; /* IRecycleBin5 interface */ STDMETHOD(Delete)( diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp b/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp index ff2593fe84d40..ae5c6d5e5e6af 100644 --- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp +++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp @@ -28,13 +28,18 @@ class RecycleBin5File : public IRecycleBinFile STDMETHODIMP_(ULONG) Release() override; /* IRecycleBinFile methods */ + STDMETHODIMP IsEqualIdentity(const RECYCLEBINFILEIDENTITY *pFI) override + { + RECYCLEBINFILEIDENTITY self = { m_deletedFile.DeletionTime, m_FullName }; + return RecycleBinGeneric_IsEqualFileIdentity(pFI, &self) ? S_OK : S_FALSE; + } + STDMETHODIMP GetInfo(PDELETED_FILE_INFO pInfo) override; STDMETHODIMP GetLastModificationTime(FILETIME *pLastModificationTime) override; STDMETHODIMP GetDeletionTime(FILETIME *pDeletionTime) override; STDMETHODIMP GetFileSize(ULARGE_INTEGER *pFileSize) override; STDMETHODIMP GetPhysicalFileSize(ULARGE_INTEGER *pPhysicalFileSize) override; STDMETHODIMP GetAttributes(DWORD *pAttributes) override; STDMETHODIMP GetFileName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) override; - STDMETHODIMP GetTypeName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) override; STDMETHODIMP Delete() override; STDMETHODIMP Restore() override; @@ -53,14 +58,8 @@ STDMETHODIMP RecycleBin5File::QueryInterface(REFIID riid, void **ppvObject) return E_POINTER; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBinFile)) - *ppvObject = static_cast(this); - else if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) { - DWORD dwAttributes; - if (GetAttributes(&dwAttributes) == S_OK) - return SHCreateFileExtractIconW(m_FullName, dwAttributes, riid, ppvObject); - else - return S_FALSE; + *ppvObject = static_cast(this); } else { @@ -94,13 +93,29 @@ STDMETHODIMP_(ULONG) RecycleBin5File::Release() return refCount; } +STDMETHODIMP RecycleBin5File::GetInfo(PDELETED_FILE_INFO pInfo) +{ + HRESULT hr = S_OK; + ULARGE_INTEGER uli; + if (FAILED(GetLastModificationTime(&pInfo->LastModification))) + ZeroMemory(&pInfo->LastModification, sizeof(pInfo->LastModification)); + pInfo->DeletionTime = m_deletedFile.DeletionTime; + C_ASSERT(sizeof(pInfo->FileSize) <= sizeof(UINT)); + pInfo->FileSize = FAILED(GetFileSize(&uli)) ? INVALID_FILE_SIZE : uli.LowPart; + if (FAILED(hr = GetAttributes(&pInfo->Attributes))) + pInfo->Attributes = 0; + InitializeRecycleBinStringRef(&pInfo->OriginalFullPath, m_deletedFile.FileNameW); + InitializeRecycleBinStringRef(&pInfo->RecycledFullPath, m_FullName); + return hr; +} + STDMETHODIMP RecycleBin5File::GetLastModificationTime(FILETIME *pLastModificationTime) { TRACE("(%p, %p)\n", this, pLastModificationTime); DWORD dwAttributes = ::GetFileAttributesW(m_FullName); if (dwAttributes == INVALID_FILE_ATTRIBUTES) - return HRESULT_FROM_WIN32(GetLastError()); + return HResultFromWin32(GetLastError()); HANDLE hFile; hFile = CreateFileW(m_FullName, @@ -112,13 +127,13 @@ STDMETHODIMP RecycleBin5File::GetLastModificationTime(FILETIME *pLastModificatio (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_FLAG_BACKUP_SEMANTICS : 0, NULL); if (hFile == INVALID_HANDLE_VALUE) - return HRESULT_FROM_WIN32(GetLastError()); + return HResultFromWin32(GetLastError()); HRESULT hr; if (GetFileTime(hFile, NULL, NULL, pLastModificationTime)) hr = S_OK; else - hr = HRESULT_FROM_WIN32(GetLastError()); + hr = HResultFromWin32(GetLastError()); CloseHandle(hFile); return hr; } @@ -174,7 +189,7 @@ STDMETHODIMP RecycleBin5File::GetAttributes(DWORD *pAttributes) dwAttributes = GetFileAttributesW(m_FullName); if (dwAttributes == INVALID_FILE_ATTRIBUTES) - return HRESULT_FROM_WIN32(GetLastError()); + return HResultFromWin32(GetLastError()); *pAttributes = dwAttributes; return S_OK; @@ -199,36 +214,6 @@ STDMETHODIMP RecycleBin5File::GetFileName(SIZE_T BufferSize, LPWSTR Buffer, SIZE return S_OK; } -STDMETHODIMP RecycleBin5File::GetTypeName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) -{ - HRESULT hr; - DWORD dwRequired; - DWORD dwAttributes; - SHFILEINFOW shFileInfo; - - TRACE("(%p, %u, %p, %p)\n", this, BufferSize, Buffer, RequiredSize); - - hr = GetAttributes(&dwAttributes); - if (!SUCCEEDED(hr)) - return hr; - - hr = SHGetFileInfoW(m_FullName, dwAttributes, &shFileInfo, sizeof(shFileInfo), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES); - if (!SUCCEEDED(hr)) - return hr; - - dwRequired = (DWORD)(wcslen(shFileInfo.szTypeName) + 1) * sizeof(WCHAR); - if (RequiredSize) - *RequiredSize = dwRequired; - - if (BufferSize == 0 && !Buffer) - return S_OK; - - if (BufferSize < dwRequired) - return E_OUTOFMEMORY; - CopyMemory(Buffer, shFileInfo.szTypeName, dwRequired); - return S_OK; -} - STDMETHODIMP RecycleBin5File::Delete() { TRACE("(%p)\n", this); @@ -390,7 +375,7 @@ STDMETHODIMP RecycleBin5Enum::Next(DWORD celt, IRecycleBinFile **rgelt, DWORD *p ULARGE_INTEGER FileSize; FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart); if (FileSize.u.LowPart == 0) - return HRESULT_FROM_WIN32(GetLastError()); + return HResultFromWin32(GetLastError()); DWORD dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)); @@ -455,7 +440,7 @@ RecycleBin5Enum::Init( m_hInfo = hInfo; m_pInfo = (PINFO2_HEADER)MapViewOfFile(hInfoMapped, FILE_MAP_READ, 0, 0, 0); if (!m_pInfo) - return HRESULT_FROM_WIN32(GetLastError()); + return HResultFromWin32(GetLastError()); if (m_pInfo->dwVersion != 5 || m_pInfo->dwRecordSize != sizeof(DELETED_FILE_RECORD)) return E_FAIL; diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index 3a665288535d9..68befd3292515 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -1390,14 +1390,7 @@ static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei, CComPtr shf; - LPCITEMIDLIST pidllast = NULL; - HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - return shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IDataObject, &dataObj)); + return SHELL_GetUIObjectOfAbsoluteItem(NULL, pidl, IID_PPV_ARG(IDataObject, &dataObj)); } static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, @@ -1582,6 +1575,7 @@ static HRESULT ShellExecute_ContextMenuVerb(LPSHELLEXECUTEINFOW sei) enum { idFirst = 1, idLast = 0x7fff }; HMENU hMenu = CreatePopupMenu(); + // Note: Windows does not pass CMF_EXTENDEDVERBS so "hidden" verbs cannot be executed hr = cm->QueryContextMenu(hMenu, 0, idFirst, idLast, fDefault ? CMF_DEFAULTONLY : 0); if (!FAILED_UNEXPECTEDLY(hr)) { diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index ce2f6f40834bd..a384c6c492526 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -259,6 +259,32 @@ HRESULT SHBindToObject( return SHBindToObjectEx(psf, pidl, NULL, riid, ppvObj); } +EXTERN_C HRESULT +SHELL_GetUIObjectOfAbsoluteItem( + _In_opt_ HWND hWnd, + _In_ PCIDLIST_ABSOLUTE pidl, + _In_ REFIID riid, _Out_ void **ppvObj) +{ + if (!ppvObj) + return E_INVALIDARG; + *ppvObj = NULL; + IShellFolder *psf; + PCUITEMID_CHILD pidlChild; + HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); + if (SUCCEEDED(hr)) + { + hr = psf->GetUIObjectOf(hWnd, 1, &pidlChild, riid, NULL, ppvObj); + psf->Release(); + if (SUCCEEDED(hr)) + { + if (*ppvObj) + return hr; + hr = E_FAIL; + } + } + return hr; +} + HRESULT Shell_DisplayNameOf( _In_ IShellFolder *psf, @@ -1429,3 +1455,71 @@ GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba) } return MapVerbToDfmCmd(verba); // Returns DFM_CMD_* or 0 } + +HRESULT +SHELL_MapContextMenuVerbToCmdId(LPCMINVOKECOMMANDINFO pICI, const CMVERBMAP *pMap) +{ + LPCSTR pVerbA = pICI->lpVerb; + CHAR buf[MAX_PATH]; + LPCMINVOKECOMMANDINFOEX pICIX = (LPCMINVOKECOMMANDINFOEX)pICI; + if (IsUnicode(*pICIX) && !IS_INTRESOURCE(pICIX->lpVerbW)) + { + if (SHUnicodeToAnsi(pICIX->lpVerbW, buf, _countof(buf))) + pVerbA = buf; + } + + if (IS_INTRESOURCE(pVerbA)) + return LOWORD(pVerbA); + for (SIZE_T i = 0; pMap[i].Verb; ++i) + { + assert(SUCCEEDED((int)(pMap[i].CmdId))); // The id must be >= 0 and ideally in the 0..0x7fff range + if (!lstrcmpiA(pMap[i].Verb, pVerbA) && pVerbA[0]) + return pMap[i].CmdId; + } + return E_FAIL; +} + +static const CMVERBMAP* +FindVerbMapEntry(UINT_PTR CmdId, const CMVERBMAP *pMap) +{ + for (SIZE_T i = 0; pMap[i].Verb; ++i) + { + if (pMap[i].CmdId == CmdId) + return &pMap[i]; + } + return NULL; +} + +HRESULT +SHELL_GetCommandStringImpl(SIZE_T CmdId, UINT uFlags, LPSTR Buf, UINT cchBuf, const CMVERBMAP *pMap) +{ + const CMVERBMAP* pEntry; + switch (uFlags | GCS_UNICODE) + { + case GCS_VALIDATEW: + case GCS_VERBW: + pEntry = FindVerbMapEntry(CmdId, pMap); + if ((uFlags | GCS_UNICODE) == GCS_VERBW) + { + if (!pEntry) + return E_INVALIDARG; + else if (uFlags & GCS_UNICODE) + return SHAnsiToUnicode(pEntry->Verb, (LPWSTR)Buf, cchBuf) ? S_OK : E_FAIL; + else + return StringCchCopyA(Buf, cchBuf, pEntry->Verb); + } + return pEntry ? S_OK : S_FALSE; // GCS_VALIDATE + } + return E_NOTIMPL; +} + +HRESULT +SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut) +{ + CComPtr initIcon; + HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + initIcon->SetNormalIcon(swShell32Name, IconIndex); + return initIcon->QueryInterface(riid, ppvOut); +} diff --git a/dll/win32/shell32/utils.h b/dll/win32/shell32/utils.h index 92a96888c5d13..5591eb6a46385 100644 --- a/dll/win32/shell32/utils.h +++ b/dll/win32/shell32/utils.h @@ -7,7 +7,16 @@ #pragma once -inline BOOL +#ifdef __cplusplus +static inline LPWSTR +SHStrDupW(LPCWSTR Src) +{ + LPWSTR Dup; + return SUCCEEDED(SHStrDupW(Src, &Dup)) ? Dup : NULL; +} +#endif + +static inline BOOL RegValueExists(HKEY hKey, LPCWSTR Name) { return RegQueryValueExW(hKey, Name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; @@ -31,12 +40,22 @@ RegSetOrDelete(HKEY hKey, LPCWSTR Name, DWORD Type, LPCVOID Data, DWORD Size) return RegDeleteValueW(hKey, Name); } -inline DWORD +static inline DWORD RegSetString(HKEY hKey, LPCWSTR Name, LPCWSTR Str, DWORD Type = REG_SZ) { return RegSetValueExW(hKey, Name, 0, Type, LPBYTE(Str), (lstrlenW(Str) + 1) * sizeof(WCHAR)); } +typedef struct { + LPCSTR Verb; + WORD CmdId; +} CMVERBMAP; + +HRESULT +SHELL_MapContextMenuVerbToCmdId(LPCMINVOKECOMMANDINFO pICI, const CMVERBMAP *pMap); +HRESULT +SHELL_GetCommandStringImpl(SIZE_T CmdId, UINT uFlags, LPSTR Buf, UINT cchBuf, const CMVERBMAP *pMap); + // SHExtractIconsW is a forward, use this function instead inside shell32 inline HICON SHELL32_SHExtractIcon(LPCWSTR File, int Index, int cx, int cy) @@ -45,3 +64,20 @@ SHELL32_SHExtractIcon(LPCWSTR File, int Index, int cx, int cy) int r = PrivateExtractIconsW(File, Index, cx, cy, &hIco, NULL, 1, 0); return r > 0 ? hIco : NULL; } + +HRESULT +SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut); + +static inline HRESULT +SHELL_CreateFallbackExtractIconForFolder(REFIID riid, LPVOID *ppvOut) +{ + const int id = IDI_SHELL_FOLDER; + return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut); +} + +static inline HRESULT +SHELL_CreateFallbackExtractIconForNoAssocFile(REFIID riid, LPVOID *ppvOut) +{ + const int id = IDI_SHELL_DOCUMENT; + return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut); +} diff --git a/dll/win32/shell32/wine/pidl.c b/dll/win32/shell32/wine/pidl.c index de484982b15d0..2bca7cdd4363f 100644 --- a/dll/win32/shell32/wine/pidl.c +++ b/dll/win32/shell32/wine/pidl.c @@ -2112,32 +2112,22 @@ BOOL _ILIsSpecialFolder (LPCITEMIDLIST pidl) BOOL _ILIsDrive(LPCITEMIDLIST pidl) { - LPPIDLDATA lpPData = _ILGetDataPointer(pidl); - - TRACE("(%p)\n",pidl); - - return (pidl && lpPData && (PT_DRIVE == lpPData->type || - PT_DRIVE1 == lpPData->type || - PT_DRIVE2 == lpPData->type || - PT_DRIVE3 == lpPData->type)); + const BYTE type = _ILGetType(pidl); + const BYTE fldrtype = (PT_DRIVE & PT_FOLDERTYPEMASK); + return (type & PT_FOLDERTYPEMASK) == fldrtype && type != PT_COMPUTER_REGITEM; } BOOL _ILIsFolder(LPCITEMIDLIST pidl) { - LPPIDLDATA lpPData = _ILGetDataPointer(pidl); - - TRACE("(%p)\n",pidl); - - return (pidl && lpPData && (PT_FOLDER == lpPData->type || PT_FOLDER1 == lpPData->type)); + /* A folder or a simple PT_FS with a child */ + const BYTE type = _ILGetFSType(pidl); + return (type & PT_FS_FOLDER_FLAG) != 0 || (type == PT_FS && ILGetNext(pidl)); } BOOL _ILIsValue(LPCITEMIDLIST pidl) { - LPPIDLDATA lpPData = _ILGetDataPointer(pidl); - - TRACE("(%p)\n",pidl); - - return (pidl && lpPData && PT_VALUE == lpPData->type); + const BYTE type = _ILGetFSType(pidl); + return type && !(type & PT_FS_FOLDER_FLAG); } BOOL _ILIsCPanelStruct(LPCITEMIDLIST pidl) @@ -2281,6 +2271,9 @@ static LPWSTR _ILGetTextPointerW(LPCITEMIDLIST pidl) if (!pdata) return NULL; + if (_ILGetFSType(pidl) & PT_FS_UNICODE_FLAG) + return (LPWSTR)pdata->u.file.szNames; + switch (pdata->type) { case PT_GUID: @@ -2311,9 +2304,6 @@ static LPWSTR _ILGetTextPointerW(LPCITEMIDLIST pidl) /*return (LPSTR)&(pdata->u.network.szNames);*/ return NULL; - case PT_VALUEW: - return (LPWSTR)pdata->u.file.szNames; - #ifdef __REACTOS__ /* r54423 */ case PT_CPLAPPLET: return pdata->u.cpanel.szName; @@ -2439,7 +2429,7 @@ FileStructW* _ILGetFileStructW(LPCITEMIDLIST pidl) { FileStructW *pFileStructW; WORD cbOffset; - if (!(_ILIsValue(pidl) || _ILIsFolder(pidl))) + if (!_ILIsFolderOrFile(pidl)) return NULL; cbOffset = *(const WORD *)((const BYTE *)pidl + pidl->mkid.cb - sizeof(WORD)); @@ -2479,48 +2469,12 @@ FileStructW* _ILGetFileStructW(LPCITEMIDLIST pidl) { */ BOOL _ILGetFileDateTime(LPCITEMIDLIST pidl, FILETIME *pFt) { - LPPIDLDATA pdata = _ILGetDataPointer(pidl); - - if (!pdata) - return FALSE; - - switch (pdata->type) + if (_ILGetFSType(pidl) > PT_FS) /* Only non-simple FS items have a date */ { - case PT_FOLDER: - case PT_VALUE: - DosDateTimeToFileTime(pdata->u.file.uFileDate, pdata->u.file.uFileTime, pFt); - break; - default: - return FALSE; - } - return TRUE; -} - -BOOL _ILGetFileDate(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize) -{ - FILETIME ft,lft; - SYSTEMTIME time; - BOOL ret; - - if (_ILGetFileDateTime( pidl, &ft )) - { - FileTimeToLocalFileTime(&ft, &lft); - FileTimeToSystemTime (&lft, &time); - - ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, pOut, uOutSize); - if (ret) - { - /* Append space + time without seconds */ - pOut[ret - 1] = L' '; - GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &pOut[ret], uOutSize - ret); - } - } - else - { - pOut[0] = UNICODE_NULL; - ret = FALSE; + LPPIDLDATA pdata = _ILGetDataPointer(pidl); + return DosDateTimeToFileTime(pdata->u.file.uFileDate, pdata->u.file.uFileTime, pFt); } - return ret; + return FALSE; } /************************************************************************* @@ -2543,15 +2497,13 @@ BOOL _ILGetFileDate(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize) DWORD _ILGetFileSize(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize) { LPPIDLDATA pdata = _ILGetDataPointer(pidl); - DWORD dwSize; - if (!pdata) return 0; - switch (pdata->type) + if (_ILGetFSType(pidl) & PT_FS_FILE_FLAG) { - case PT_VALUE: - dwSize = pdata->u.file.dwFileSize; + /* FIXME: Handle INVALID_FILE_SIZE (get size from disk) */ + DWORD dwSize = pdata->u.file.dwFileSize; if (pOut) StrFormatKBSizeW(dwSize, pOut, uOutSize); return dwSize; diff --git a/dll/win32/shell32/wine/pidl.h b/dll/win32/shell32/wine/pidl.h index 28fff42feb452..bd8e34ea4bdbe 100644 --- a/dll/win32/shell32/wine/pidl.h +++ b/dll/win32/shell32/wine/pidl.h @@ -157,16 +157,6 @@ typedef struct tagPIDLPrinterStruct WCHAR szName[1]; } PIDLPrinterStruct; -typedef struct tagPIDLRecycleStruct -{ - FILETIME LastModification; - FILETIME DeletionTime; - ULARGE_INTEGER FileSize; - ULARGE_INTEGER PhysicalFileSize; - DWORD Attributes; - WCHAR szName[1]; -} PIDLRecycleStruct; - #endif /* !__REACTOS__ */ typedef struct tagGUIDStruct @@ -233,7 +223,6 @@ typedef struct tagPIDLDATA #ifdef __REACTOS__ struct tagPIDLFontStruct cfont; struct tagPIDLPrinterStruct cprinter; - struct tagPIDLRecycleStruct crecycle; #endif }u; } PIDLDATA, *LPPIDLDATA; @@ -243,7 +232,6 @@ typedef struct tagPIDLDATA * getting special values from simple pidls */ DWORD _ILSimpleGetTextW (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize) DECLSPEC_HIDDEN; -BOOL _ILGetFileDate (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize) DECLSPEC_HIDDEN; DWORD _ILGetFileSize (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize) DECLSPEC_HIDDEN; BOOL _ILGetExtension (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize) DECLSPEC_HIDDEN; DWORD _ILGetFileAttributes(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize) DECLSPEC_HIDDEN; @@ -261,6 +249,7 @@ BOOL _ILIsMyDocuments (LPCITEMIDLIST pidl); BOOL _ILIsBitBucket (LPCITEMIDLIST pidl); BOOL _ILIsNetHood (LPCITEMIDLIST pidl); BOOL _ILIsControlPanel (LPCITEMIDLIST pidl); +#define _ILIsFolderOrFile _ILGetFSType #endif BOOL _ILIsDrive (LPCITEMIDLIST pidl) DECLSPEC_HIDDEN; BOOL _ILIsFolder (LPCITEMIDLIST pidl) DECLSPEC_HIDDEN; diff --git a/dll/win32/shell32/wine/shell32_main.h b/dll/win32/shell32/wine/shell32_main.h index d40e1a164b327..fa5bafd8880e8 100644 --- a/dll/win32/shell32/wine/shell32_main.h +++ b/dll/win32/shell32/wine/shell32_main.h @@ -139,7 +139,7 @@ void FreeChangeNotifications(void) DECLSPEC_HIDDEN; BOOL SHELL_DeleteDirectoryW(HWND hwnd, LPCWSTR pwszDir, BOOL bShowUI); BOOL SHELL_ConfirmYesNoW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir); -void WINAPI _InsertMenuItemW (HMENU hmenu, UINT indexMenu, BOOL fByPosition, +BOOL WINAPI _InsertMenuItemW (HMENU hmenu, UINT indexMenu, BOOL fByPosition, UINT wID, UINT fType, LPCWSTR dwTypeData, UINT fState); static __inline BOOL SHELL_OsIsUnicode(void)