diff --git a/Explorer++/Helper/FileActionHandler.cpp b/Explorer++/Helper/FileActionHandler.cpp index 42223b71f6..63844bba73 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 cb8310c39e..b90440a02f 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 c92fde2ca7..218f563d4e 100644 --- a/Explorer++/Helper/FileOperations.cpp +++ b/Explorer++/Helper/FileOperations.cpp @@ -10,7 +10,6 @@ #include "Macros.h" #include "ShellHelper.h" #include "StringHelper.h" -#include #include #include #include @@ -46,12 +45,25 @@ HRESULT FileOperations::RenameFile(IShellItem *item, const std::wstring &newName return hr; } -HRESULT FileOperations::DeleteFiles(HWND hwnd, const std::vector &pidls, +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 sink{}; + hr = CreateFileOperationProgressSink(pidls, &sink); + if (FAILED(hr) || !sink) + { + return hr; + } + DWORD cookie{}; + hr = fo->Advise(sink.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_PPV_ARGS(&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,79 @@ 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); + RETURN_IF_FAILED(SHGetDesktopFolder(&pDesktop)); + + PidlAbsolute pidlBin; + hr = SHGetKnownFolderIDList(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, nullptr, PidlOutParam(pidlBin)); + if (FAILED(hr) || !pidlBin.Raw()) + return hr; + + wil::com_ptr_nothrow pShellFolder{}; + hr = pDesktop->BindToObject(pidlBin.Raw(), nullptr, IID_PPV_ARGS(&pShellFolder)); + if (FAILED(hr) || !pShellFolder) + return hr; + + wil::com_ptr_nothrow enumerator{}; + hr = pShellFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &enumerator); + if (FAILED(hr) || !enumerator) + return hr; + + ULONG numFetched = 1; + unique_pidl_child pidlItem; + while (S_OK == enumerator->Next(1, wil::out_param(pidlItem), &numFetched) && (1 == numFetched)) + { + const auto pidlRelative = ILFindLastID(static_cast(pidl)); + hr = pShellFolder->CompareIDs(SHCIDS_CANONICALONLY, pidlRelative, pidlItem.get()); + if (0 == static_cast(HRESULT_CODE(hr))) + { + hr = PerformUndeleting(pShellFolder, pidlItem.get()); + 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 f25d77291b..e09f5874b8 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,13 @@ 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);