From 95a7f5efce99ebd0aa60942a51c9dc3563e3dee2 Mon Sep 17 00:00:00 2001 From: flaviu22 Date: Wed, 2 Oct 2024 14:09:57 +0300 Subject: [PATCH 1/2] Solved undo deleted item - clean code --- Explorer++/Helper/FileActionHandler.cpp | 10 +- Explorer++/Helper/FileActionHandler.h | 1 + Explorer++/Helper/FileOperations.cpp | 118 +++++++++++++++++++++--- Explorer++/Helper/FileOperations.h | 97 ++++++++++++++++++- 4 files changed, 208 insertions(+), 18 deletions(-) diff --git a/Explorer++/Helper/FileActionHandler.cpp b/Explorer++/Helper/FileActionHandler.cpp index 42223b71f..63844bba7 100644 --- a/Explorer++/Helper/FileActionHandler.cpp +++ b/Explorer++/Helper/FileActionHandler.cpp @@ -60,13 +60,14 @@ BOOL FileActionHandler::RenameFiles(const RenamedItems_t &itemList) HRESULT FileActionHandler::DeleteFiles(HWND hwnd, const DeletedItems_t &deletedItems, bool permanent, bool silent) { - HRESULT hr = FileOperations::DeleteFiles(hwnd, deletedItems, permanent, silent); + DeletedItems_t temp(deletedItems); + const HRESULT hr = FileOperations::DeleteFiles(hwnd, temp, permanent, silent); if (SUCCEEDED(hr)) { UndoItem_t undoItem; undoItem.type = UndoType::Deleted; - undoItem.deletedItems = deletedItems; + undoItem.deletedItems = temp; m_stackFileActions.push(undoItem); } @@ -127,6 +128,11 @@ void FileActionHandler::UndoDeleteOperation(const DeletedItems_t &deletedItemLis - Find the item in the recycle bin (probably need to read INFO2 file). - Restore it (context menu command). - Push delete action onto stack. */ + + for (const auto& item : deletedItemList) + { + FileOperations::Undelete(item); + } } BOOL FileActionHandler::CanUndo() const diff --git a/Explorer++/Helper/FileActionHandler.h b/Explorer++/Helper/FileActionHandler.h index cb8310c39..b90440a02 100644 --- a/Explorer++/Helper/FileActionHandler.h +++ b/Explorer++/Helper/FileActionHandler.h @@ -46,5 +46,6 @@ class FileActionHandler void UndoRenameOperation(const RenamedItems_t &renamedItemList); void UndoDeleteOperation(const DeletedItems_t &deletedItemList); +private: std::stack m_stackFileActions; }; diff --git a/Explorer++/Helper/FileOperations.cpp b/Explorer++/Helper/FileOperations.cpp index c92fde2ca..42cdabb50 100644 --- a/Explorer++/Helper/FileOperations.cpp +++ b/Explorer++/Helper/FileOperations.cpp @@ -10,10 +10,10 @@ #include "Macros.h" #include "ShellHelper.h" #include "StringHelper.h" -#include #include #include #include +#include BOOL GetFileClusterSize(const std::wstring &strFilename, PLARGE_INTEGER lpRealFileSize); @@ -46,12 +46,24 @@ HRESULT FileOperations::RenameFile(IShellItem *item, const std::wstring &newName return hr; } -HRESULT FileOperations::DeleteFiles(HWND hwnd, const std::vector &pidls, - bool permanent, bool silent) +HRESULT FileOperations::DeleteFiles(HWND hwnd, std::vector &pidls, bool permanent, bool silent) { - wil::com_ptr_nothrow fo; - HRESULT hr = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&fo)); + wil::com_ptr_nothrow fo{}; + auto hr = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&fo)); + if (FAILED(hr) || !fo) + { + return hr; + } + wil::com_ptr_nothrow pSink{}; + hr = CreateFileOperationProgressSink(pidls, &pSink); + if (FAILED(hr) || !pSink) + { + return hr; + } + + DWORD cookie{}; + hr = fo->Advise(pSink.get(), &cookie); if (FAILED(hr)) { return hr; @@ -90,9 +102,8 @@ HRESULT FileOperations::DeleteFiles(HWND hwnd, const std::vector shellItemArray; - hr = SHCreateShellItemArrayFromIDLists(static_cast(pidls.size()), &pidls[0], - &shellItemArray); + wil::com_ptr_nothrow shellItemArray{}; + hr = SHCreateShellItemArrayFromIDLists(static_cast(pidls.size()), &pidls[0], &shellItemArray); if (FAILED(hr)) { @@ -100,7 +111,7 @@ HRESULT FileOperations::DeleteFiles(HWND hwnd, const std::vector unknown; - hr = shellItemArray->QueryInterface(IID_IUnknown, reinterpret_cast(&unknown)); + hr = shellItemArray->QueryInterface(IID_IUnknown, reinterpret_cast(&unknown)); if (FAILED(hr)) { @@ -115,6 +126,7 @@ HRESULT FileOperations::DeleteFiles(HWND hwnd, const std::vectorPerformOperations(); + fo->Unadvise(cookie); return hr; } @@ -425,8 +437,7 @@ HRESULT FileOperations::CreateLinkToFile(const std::wstring &strTargetFilename, const std::wstring &strLinkFilename, const std::wstring &strLinkDescription) { IShellLink *pShellLink = nullptr; - HRESULT hr = - CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShellLink)); + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShellLink)); if (SUCCEEDED(hr)) { @@ -448,6 +459,17 @@ HRESULT FileOperations::CreateLinkToFile(const std::wstring &strTargetFilename, return hr; } +HRESULT CreateFileOperationProgressSink(std::vector &pidls, + IFileOperationProgressSink **ppSink) +{ + CFileOperationProgressSink* pfo = new (std::nothrow)CFileOperationProgressSink(pidls); + if (!pfo) + return E_OUTOFMEMORY; + const auto hr = pfo->QueryInterface(IID_IFileOperationProgressSink, reinterpret_cast(ppSink)); + pfo->Release(); + return hr; +} + HRESULT FileOperations::ResolveLink(HWND hwnd, DWORD fFlags, const TCHAR *szLinkFilename, TCHAR *szResolvedPath, int nBufferSize) { @@ -649,3 +671,77 @@ void FileOperations::DeleteFileSecurely(const std::wstring &strFilename, DeleteFile(strFilename.c_str()); } + +HRESULT FileOperations::Undelete(const PCIDLIST_ABSOLUTE &pidl) +{ + wil::com_ptr_nothrow pDesktop{}; + HRESULT hr = SHGetDesktopFolder(&pDesktop); + if (SUCCEEDED(hr) && pDesktop) + { + CComHeapPtr pidlbin{}; + hr = SHGetKnownFolderIDList(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, NULL, &pidlbin); + if (SUCCEEDED(hr) && pidlbin) + { + wil::com_ptr_nothrow pShellFolder{}; + hr = pDesktop->BindToObject(pidlbin, nullptr, IID_PPV_ARGS(&pShellFolder)); + if (SUCCEEDED(hr) && pShellFolder) + { + wil::com_ptr_nothrow pEnum{}; + hr = pShellFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnum); + if (SUCCEEDED(hr) && pEnum) + { + ULONG fetched{}; + for (PITEMID_CHILD pidChild{}; pEnum->Next(1, &pidChild, &fetched) == S_OK; pidChild = nullptr) + { + const auto pidlRelative = ILFindLastID(static_cast(pidl)); + hr = pShellFolder->CompareIDs(SHCIDS_CANONICALONLY, pidlRelative, pidChild); + if (0 == static_cast(HRESULT_CODE(hr))) + { + hr = PerformUndeleting(pShellFolder, pidChild); + break; + } + } + } + } + } + } + + return hr; +} + +HRESULT FileOperations::PerformUndeleting(wil::com_ptr_nothrow& shellFolder, const PITEMID_CHILD &pidChild) +{ + PITEMID_CHILD* item = static_cast(CoTaskMemAlloc(sizeof(PITEMID_CHILD))); + SecureZeroMemory(item, sizeof(PITEMID_CHILD)); + item[0] = pidChild; + + wil::com_ptr_nothrow pContextMenu{}; + HRESULT hr = shellFolder->GetUIObjectOf(nullptr, 1, reinterpret_cast(item), + __uuidof(IContextMenu), nullptr, reinterpret_cast(&pContextMenu)); + if (SUCCEEDED(hr) && pContextMenu) + hr = InvokeVerb(pContextMenu.get(), "undelete"); + + CoTaskMemFree(item); + + return hr; +} + +HRESULT FileOperations::InvokeVerb(IContextMenu* pContextMenu, PCSTR pszVerb) +{ + HRESULT hr{}; + const HMENU hmenu = CreatePopupMenu(); + if (pContextMenu && hmenu) + { + hr = pContextMenu->QueryContextMenu(hmenu, 0, 1, 0x7FFF, CMF_NORMAL); + if (SUCCEEDED(hr)) + { + CMINVOKECOMMANDINFO info = { 0 }; + info.cbSize = sizeof(info); + info.lpVerb = pszVerb; + hr = pContextMenu->InvokeCommand(&info); + } + DestroyMenu(hmenu); + } + return hr; +} + diff --git a/Explorer++/Helper/FileOperations.h b/Explorer++/Helper/FileOperations.h index f25d77291..fb7385441 100644 --- a/Explorer++/Helper/FileOperations.h +++ b/Explorer++/Helper/FileOperations.h @@ -8,6 +8,92 @@ #include #include +#include + +class CFileOperationProgressSink : public IFileOperationProgressSink +{ +public: + // IUnknown + STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv) + { + HRESULT hr{ E_NOINTERFACE }; + if (!ppv) + return E_POINTER; + + *ppv = nullptr; + if (iid == __uuidof(IUnknown)) + { + *ppv = static_cast(this); + AddRef(); + hr = S_OK; + } + else if (iid == __uuidof(IFileOperationProgressSink)) + { + *ppv = static_cast(this); + AddRef(); + hr = S_OK; + } + + return hr; + } + + STDMETHODIMP_(ULONG) AddRef() + { + ++m_cRef; + return m_cRef; + } + + STDMETHODIMP_(ULONG) Release() + { + ULONG cRef = --m_cRef; + if (0 == cRef) + delete this; + + return cRef; + } + + // IFileOperationProgressSink + STDMETHODIMP StartOperations() { return S_OK; } + STDMETHODIMP FinishOperations(HRESULT) { return S_OK; } + STDMETHODIMP PreRenameItem(DWORD, IShellItem *, LPCWSTR) { return S_OK; } + STDMETHODIMP PostRenameItem(DWORD, IShellItem *, LPCWSTR, HRESULT, IShellItem *) { return S_OK; } + STDMETHODIMP PreMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR) { return S_OK; } + STDMETHODIMP PostMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT, IShellItem *) { return S_OK; } + STDMETHODIMP PreCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR) { return S_OK; } + STDMETHODIMP PostCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT, IShellItem *) { return S_OK; } + STDMETHODIMP PreDeleteItem(DWORD, IShellItem *) { return S_OK; } + STDMETHODIMP PreNewItem(DWORD, IShellItem *, LPCWSTR) { return S_OK; } + STDMETHODIMP PostNewItem(DWORD, IShellItem *, LPCWSTR, LPCWSTR, DWORD, HRESULT, IShellItem *) { return S_OK; } + STDMETHODIMP UpdateProgress(UINT, UINT) { return S_OK; } + STDMETHODIMP ResetTimer() { return S_OK; } + STDMETHODIMP PauseTimer() { return S_OK; } + STDMETHODIMP ResumeTimer() { return S_OK; } + STDMETHODIMP PostDeleteItem(DWORD, IShellItem *, HRESULT, IShellItem *psiNewlyCreated) + { + HRESULT hr{ S_OK }; + if (psiNewlyCreated) + { + PIDLIST_ABSOLUTE pidlNewlyCreated{}; + hr = SHGetIDListFromObject(psiNewlyCreated, &pidlNewlyCreated); + if (SUCCEEDED(hr) && pidlNewlyCreated) + m_vPidls.emplace_back(pidlNewlyCreated); + } + return hr; + } + + CFileOperationProgressSink(std::vector &pidls) + :m_vPidls(pidls) + { + } + +private: + LONG m_cRef{ 1 }; + std::vector &m_vPidls; + ~CFileOperationProgressSink() + { + } +}; + namespace FileOperations { @@ -18,8 +104,7 @@ enum class OverwriteMethod }; HRESULT RenameFile(IShellItem *item, const std::wstring &newName); -HRESULT DeleteFiles(HWND hwnd, const std::vector &pidls, bool permanent, - bool silent); +HRESULT DeleteFiles(HWND hwnd, std::vector &pidls, bool permanent, bool silent); void DeleteFileSecurely(const std::wstring &strFilename, OverwriteMethod overwriteMethod); HRESULT CopyFilesToFolder(HWND hOwner, const std::wstring &strTitle, std::vector &pidls, bool move); @@ -39,10 +124,12 @@ HRESULT ResolveLink(HWND hwnd, DWORD fFlags, const TCHAR *szLinkFilename, TCHAR int nBufferSize); BOOL CreateBrowseDialog(HWND hOwner, const std::wstring &strTitle, PIDLIST_ABSOLUTE *ppidl); - +HRESULT Undelete(const PCIDLIST_ABSOLUTE &pidl); +HRESULT PerformUndeleting(wil::com_ptr_nothrow& shellFolder, const PITEMID_CHILD &pidChild); +HRESULT InvokeVerb(IContextMenu *pContextMenu, PCSTR pszVerb); }; HRESULT CopyFiles(const std::vector &items, IDataObject **dataObjectOut); HRESULT CutFiles(const std::vector &items, IDataObject **dataObjectOut); -HRESULT CopyFilesToClipboard(const std::vector &items, bool move, - IDataObject **dataObjectOut); +HRESULT CopyFilesToClipboard(const std::vector &items, bool move, IDataObject **dataObjectOut); +HRESULT CreateFileOperationProgressSink(std::vector &pidls, IFileOperationProgressSink **ppSink); From e318fada9500eebb69ead4cd0dd21aa9409bcb22 Mon Sep 17 00:00:00 2001 From: flaviu22 Date: Mon, 7 Oct 2024 13:55:09 +0300 Subject: [PATCH 2/2] Restructures the code for undo deleted item --- Explorer++/Helper/FileOperations.cpp | 50 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Explorer++/Helper/FileOperations.cpp b/Explorer++/Helper/FileOperations.cpp index 42cdabb50..e0b8e065e 100644 --- a/Explorer++/Helper/FileOperations.cpp +++ b/Explorer++/Helper/FileOperations.cpp @@ -676,33 +676,33 @@ HRESULT FileOperations::Undelete(const PCIDLIST_ABSOLUTE &pidl) { wil::com_ptr_nothrow pDesktop{}; HRESULT hr = SHGetDesktopFolder(&pDesktop); - if (SUCCEEDED(hr) && pDesktop) + if (FAILED(hr) || !pDesktop) + return hr; + + CComHeapPtr pidlbin{}; + hr = SHGetKnownFolderIDList(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, NULL, &pidlbin); + if (FAILED(hr) || !pidlbin) + return hr; + + wil::com_ptr_nothrow pShellFolder{}; + hr = pDesktop->BindToObject(pidlbin, nullptr, IID_PPV_ARGS(&pShellFolder)); + if (FAILED(hr) || !pShellFolder) + return hr; + + wil::com_ptr_nothrow pEnum{}; + hr = pShellFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnum); + if (FAILED(hr) || !pEnum) + return hr; + + ULONG fetched{}; + for (PITEMID_CHILD pidChild{}; pEnum->Next(1, &pidChild, &fetched) == S_OK; pidChild = nullptr) { - CComHeapPtr pidlbin{}; - hr = SHGetKnownFolderIDList(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, NULL, &pidlbin); - if (SUCCEEDED(hr) && pidlbin) + const auto pidlRelative = ILFindLastID(static_cast(pidl)); + hr = pShellFolder->CompareIDs(SHCIDS_CANONICALONLY, pidlRelative, pidChild); + if (0 == static_cast(HRESULT_CODE(hr))) { - wil::com_ptr_nothrow pShellFolder{}; - hr = pDesktop->BindToObject(pidlbin, nullptr, IID_PPV_ARGS(&pShellFolder)); - if (SUCCEEDED(hr) && pShellFolder) - { - wil::com_ptr_nothrow pEnum{}; - hr = pShellFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnum); - if (SUCCEEDED(hr) && pEnum) - { - ULONG fetched{}; - for (PITEMID_CHILD pidChild{}; pEnum->Next(1, &pidChild, &fetched) == S_OK; pidChild = nullptr) - { - const auto pidlRelative = ILFindLastID(static_cast(pidl)); - hr = pShellFolder->CompareIDs(SHCIDS_CANONICALONLY, pidlRelative, pidChild); - if (0 == static_cast(HRESULT_CODE(hr))) - { - hr = PerformUndeleting(pShellFolder, pidChild); - break; - } - } - } - } + hr = PerformUndeleting(pShellFolder, pidChild); + break; } }