Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solved undo deleted item - clean code #488

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Explorer++/Helper/FileActionHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Explorer++/Helper/FileActionHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ class FileActionHandler
void UndoRenameOperation(const RenamedItems_t &renamedItemList);
void UndoDeleteOperation(const DeletedItems_t &deletedItemList);

private:
std::stack<UndoItem_t> m_stackFileActions;
};
118 changes: 107 additions & 11 deletions Explorer++/Helper/FileOperations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
#include "Macros.h"
#include "ShellHelper.h"
#include "StringHelper.h"
#include <wil/com.h>
#include <filesystem>
#include <list>
#include <sstream>
#include <atlbase.h>
flaviu22 marked this conversation as resolved.
Show resolved Hide resolved

BOOL GetFileClusterSize(const std::wstring &strFilename, PLARGE_INTEGER lpRealFileSize);

Expand Down Expand Up @@ -46,12 +46,24 @@ HRESULT FileOperations::RenameFile(IShellItem *item, const std::wstring &newName
return hr;
}

HRESULT FileOperations::DeleteFiles(HWND hwnd, const std::vector<PCIDLIST_ABSOLUTE> &pidls,
bool permanent, bool silent)
HRESULT FileOperations::DeleteFiles(HWND hwnd, std::vector<PCIDLIST_ABSOLUTE> &pidls, bool permanent, bool silent)
flaviu22 marked this conversation as resolved.
Show resolved Hide resolved
{
wil::com_ptr_nothrow<IFileOperation> fo;
HRESULT hr = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&fo));
wil::com_ptr_nothrow<IFileOperation> fo{};
auto hr = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&fo));
if (FAILED(hr) || !fo)
{
return hr;
}

wil::com_ptr_nothrow<IFileOperationProgressSink> pSink{};
flaviu22 marked this conversation as resolved.
Show resolved Hide resolved
hr = CreateFileOperationProgressSink(pidls, &pSink);
if (FAILED(hr) || !pSink)
{
return hr;
}

DWORD cookie{};
hr = fo->Advise(pSink.get(), &cookie);
if (FAILED(hr))
{
return hr;
Expand Down Expand Up @@ -90,17 +102,16 @@ HRESULT FileOperations::DeleteFiles(HWND hwnd, const std::vector<PCIDLIST_ABSOLU
}
}

wil::com_ptr_nothrow<IShellItemArray> shellItemArray;
hr = SHCreateShellItemArrayFromIDLists(static_cast<UINT>(pidls.size()), &pidls[0],
&shellItemArray);
wil::com_ptr_nothrow<IShellItemArray> shellItemArray{};
hr = SHCreateShellItemArrayFromIDLists(static_cast<UINT>(pidls.size()), &pidls[0], &shellItemArray);

if (FAILED(hr))
{
return hr;
}

wil::com_ptr_nothrow<IUnknown> unknown;
hr = shellItemArray->QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&unknown));
hr = shellItemArray->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&unknown));
flaviu22 marked this conversation as resolved.
Show resolved Hide resolved

if (FAILED(hr))
{
Expand All @@ -115,6 +126,7 @@ HRESULT FileOperations::DeleteFiles(HWND hwnd, const std::vector<PCIDLIST_ABSOLU
}

hr = fo->PerformOperations();
fo->Unadvise(cookie);

return hr;
}
Expand Down Expand Up @@ -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))
{
Expand All @@ -448,6 +459,17 @@ HRESULT FileOperations::CreateLinkToFile(const std::wstring &strTargetFilename,
return hr;
}

HRESULT CreateFileOperationProgressSink(std::vector<PCIDLIST_ABSOLUTE> &pidls,
IFileOperationProgressSink **ppSink)
{
CFileOperationProgressSink* pfo = new (std::nothrow)CFileOperationProgressSink(pidls);
if (!pfo)
return E_OUTOFMEMORY;
const auto hr = pfo->QueryInterface(IID_IFileOperationProgressSink, reinterpret_cast<LPVOID*>(ppSink));
pfo->Release();
return hr;
}

HRESULT FileOperations::ResolveLink(HWND hwnd, DWORD fFlags, const TCHAR *szLinkFilename,
TCHAR *szResolvedPath, int nBufferSize)
{
Expand Down Expand Up @@ -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<IShellFolder> pDesktop{};
flaviu22 marked this conversation as resolved.
Show resolved Hide resolved
HRESULT hr = SHGetDesktopFolder(&pDesktop);
if (FAILED(hr) || !pDesktop)
return hr;
flaviu22 marked this conversation as resolved.
Show resolved Hide resolved

CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlbin{};
flaviu22 marked this conversation as resolved.
Show resolved Hide resolved
hr = SHGetKnownFolderIDList(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, NULL, &pidlbin);
if (FAILED(hr) || !pidlbin)
return hr;

wil::com_ptr_nothrow<IShellFolder> pShellFolder{};
hr = pDesktop->BindToObject(pidlbin, nullptr, IID_PPV_ARGS(&pShellFolder));
if (FAILED(hr) || !pShellFolder)
return hr;

wil::com_ptr_nothrow<IEnumIDList> 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)
flaviu22 marked this conversation as resolved.
Show resolved Hide resolved
{
const auto pidlRelative = ILFindLastID(static_cast<PCUIDLIST_RELATIVE>(pidl));
hr = pShellFolder->CompareIDs(SHCIDS_CANONICALONLY, pidlRelative, pidChild);
if (0 == static_cast<short>(HRESULT_CODE(hr)))
{
hr = PerformUndeleting(pShellFolder, pidChild);
break;
}
}

return hr;
}

HRESULT FileOperations::PerformUndeleting(wil::com_ptr_nothrow<IShellFolder>& shellFolder, const PITEMID_CHILD &pidChild)
{
PITEMID_CHILD* item = static_cast<PITEMID_CHILD*>(CoTaskMemAlloc(sizeof(PITEMID_CHILD)));
SecureZeroMemory(item, sizeof(PITEMID_CHILD));
item[0] = pidChild;

wil::com_ptr_nothrow<IContextMenu> pContextMenu{};
HRESULT hr = shellFolder->GetUIObjectOf(nullptr, 1, reinterpret_cast<PCUITEMID_CHILD_ARRAY>(item),
__uuidof(IContextMenu), nullptr, reinterpret_cast<void **>(&pContextMenu));
if (SUCCEEDED(hr) && pContextMenu)
hr = InvokeVerb(pContextMenu.get(), "undelete");

CoTaskMemFree(item);

return hr;
}

HRESULT FileOperations::InvokeVerb(IContextMenu* pContextMenu, PCSTR pszVerb)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to use ExecuteActionFromContextMenu for this.

{
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;
}

97 changes: 92 additions & 5 deletions Explorer++/Helper/FileOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,92 @@
#include <list>
#include <vector>

#include <wil/com.h>

class CFileOperationProgressSink : public IFileOperationProgressSink
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an existing implementation of this - see FileProgressSink. That implementation could be updated to notify on deletes. That would simplify this code, since you wouldn't need to implement the entire class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am affraid this is not possible, because CFileOperationProgressSink needs a custom constructor:

	CFileOperationProgressSink(std::vector<PCIDLIST_ABSOLUTE> &pidls) 
		:m_vPidls(pidls)
	{
	}

in order to initialize

std::vector<PCIDLIST_ABSOLUTE> &m_vPidls;

which is a reference.

Because FileProgressSink is derived from winrt::implements<FileProgressSink, IFileOperationProgressSink, winrt::non_agile>, I cannot implement that custom constructor:

	FileProgressSink(std::vector<PCIDLIST_ABSOLUTE> &pidls)
		:m_vPidls(pidls)
	{
	}

Error:

31>C:\Project\explorerplusplus_undo_delete\vcpkg_installed\x64-windows-static\x64-windows-static\include\winrt\base.h(8059,52): error C2280: 'winrt::impl::heap_implements<FileProgressSink>::heap_implements(void)': attempting to reference a deleted function
31>(compiling source file '/MainMenuHandler.cpp')
31>    C:\Project\explorerplusplus_undo_delete\vcpkg_installed\x64-windows-static\x64-windows-static\include\winrt\base.h(8043,1):
31>    compiler has generated 'winrt::impl::heap_implements<FileProgressSink>::heap_implements' here
31>    C:\Project\explorerplusplus_undo_delete\vcpkg_installed\x64-windows-static\x64-windows-static\include\winrt\base.h(8043,1):
31>    'winrt::impl::heap_implements<FileProgressSink>::heap_implements(void)': function was implicitly deleted because a base class 'FileProgressSink' has either no appropriate default constructor or overload resolution was ambiguous
31>    C:\Project\explorerplusplus_undo_delete\Explorer++\Explorer++\FileProgressSink.h(10,7):
31>    see declaration of 'FileProgressSink'
31>    C:\Project\explorerplusplus_undo_delete\vcpkg_installed\x64-windows-static\x64-windows-static\include\winrt\base.h(8059,52):
31>    the template instantiation context (the oldest one first) is
31>        C:\Project\explorerplusplus_undo_delete\Explorer++\Explorer++\MainMenuHandler.cpp(216,21):
31>        see reference to function template instantiation 'winrt::com_ptr<D> winrt::make_self<FileProgressSink,>(void)' being compiled
31>        with
31>        [
31>            D=FileProgressSink
31>        ]
31>        C:\Project\explorerplusplus_undo_delete\vcpkg_installed\x64-windows-static\x64-windows-static\include\winrt\base.h(8176,28):
31>        see reference to function template instantiation 'T *winrt::impl::create_and_initialize<FileProgressSink,>(void)' being compiled
31>        with
31>        [
31>            T=FileProgressSink
31>        ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we derive FileProgressSink from IFileOperationProgressSink only?

{
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<IUnknown *>(this);
AddRef();
hr = S_OK;
}
else if (iid == __uuidof(IFileOperationProgressSink))
{
*ppv = static_cast<IFileOperationProgressSink *>(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<PCIDLIST_ABSOLUTE> &pidls)
:m_vPidls(pidls)
{
}

private:
LONG m_cRef{ 1 };
std::vector<PCIDLIST_ABSOLUTE> &m_vPidls;
~CFileOperationProgressSink()
{
}
};

namespace FileOperations
{

Expand All @@ -18,8 +104,7 @@ enum class OverwriteMethod
};

HRESULT RenameFile(IShellItem *item, const std::wstring &newName);
HRESULT DeleteFiles(HWND hwnd, const std::vector<PCIDLIST_ABSOLUTE> &pidls, bool permanent,
bool silent);
HRESULT DeleteFiles(HWND hwnd, std::vector<PCIDLIST_ABSOLUTE> &pidls, bool permanent, bool silent);
void DeleteFileSecurely(const std::wstring &strFilename, OverwriteMethod overwriteMethod);
HRESULT CopyFilesToFolder(HWND hOwner, const std::wstring &strTitle,
std::vector<PCIDLIST_ABSOLUTE> &pidls, bool move);
Expand All @@ -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<IShellFolder>& shellFolder, const PITEMID_CHILD &pidChild);
HRESULT InvokeVerb(IContextMenu *pContextMenu, PCSTR pszVerb);
};

HRESULT CopyFiles(const std::vector<PidlAbsolute> &items, IDataObject **dataObjectOut);
HRESULT CutFiles(const std::vector<PidlAbsolute> &items, IDataObject **dataObjectOut);
HRESULT CopyFilesToClipboard(const std::vector<PidlAbsolute> &items, bool move,
IDataObject **dataObjectOut);
HRESULT CopyFilesToClipboard(const std::vector<PidlAbsolute> &items, bool move, IDataObject **dataObjectOut);
HRESULT CreateFileOperationProgressSink(std::vector<PCIDLIST_ABSOLUTE> &pidls, IFileOperationProgressSink **ppSink);