From b32d1e948b32a8f7607ebc30f10dda903426f63c Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 2 Oct 2015 14:55:46 +0200 Subject: 7.1 --- zen/recycler.cpp | 195 ++++++++++++++++--------------------------------------- 1 file changed, 57 insertions(+), 138 deletions(-) (limited to 'zen/recycler.cpp') diff --git a/zen/recycler.cpp b/zen/recycler.cpp index ed6669ef..a4f6c128 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -9,10 +9,10 @@ #ifdef ZEN_WIN #include "thread.h" - #include "dll.h" - #include "win_ver.h" - #include "long_path_prefix.h" - #include "IFileOperation/file_op.h" + + #ifdef ZEN_WIN_VISTA_AND_LATER + #include "vista_file_op.h" + #endif #elif defined ZEN_LINUX #include @@ -27,122 +27,55 @@ using namespace zen; #ifdef ZEN_WIN -namespace -{ -/* -Performance test: delete 1000 files ------------------------------------- -SHFileOperation - single file 33s -SHFileOperation - multiple files 2,1s -IFileOperation - single file 33s -IFileOperation - multiple files 2,1s - -=> SHFileOperation and IFileOperation have nearly IDENTICAL performance characteristics! - -Nevertheless, let's use IFileOperation for better error reporting (including details on locked files)! -*/ - -struct CallbackData +void zen::recycleOrDelete(const std::vector& itempaths, const std::function& onRecycleItem) { - CallbackData(const std::function& notifyDeletionStatus) : - notifyDeletionStatus_(notifyDeletionStatus) {} - - const std::function& notifyDeletionStatus_; //in, optional - std::exception_ptr exception; //out -}; - - -bool onRecyclerCallback(const wchar_t* itempath, void* sink) -{ - CallbackData& cbd = *static_cast(sink); //sink is NOT optional here - - if (cbd.notifyDeletionStatus_) - try - { - cbd.notifyDeletionStatus_(itempath); //throw ? - } - catch (...) - { - cbd.exception = std::current_exception(); - return false; - } - return true; -} -} - - -void zen::recycleOrDelete(const std::vector& itempaths, const std::function& notifyDeletionStatus) -{ - if (itempaths.empty()) - return; + if (itempaths.empty()) return; //warning: moving long file paths to recycler does not work! //both ::SHFileOperation() and ::IFileOperation cannot delete a folder named "System Volume Information" with normal attributes but shamelessly report success //both ::SHFileOperation() and ::IFileOperation can't handle \\?\-prefix! - if (vistaOrLater()) //new recycle bin usage: available since Vista - { -#define DEF_DLL_FUN(name) const DllFun name(fileop::getDllName(), fileop::funName_##name); - DEF_DLL_FUN(moveToRecycleBin); - DEF_DLL_FUN(getLastErrorMessage); -#undef DEF_DLL_FUN - - if (!moveToRecycleBin || !getLastErrorMessage) - throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempaths[0])), - replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(fileop::getDllName()))); - - std::vector cNames; - for (auto it = itempaths.begin(); it != itempaths.end(); ++it) //CAUTION: do not create temporary strings here!! - cNames.push_back(it->c_str()); - - - - CallbackData cbd(notifyDeletionStatus); - if (!moveToRecycleBin(&cNames[0], cNames.size(), onRecyclerCallback, &cbd)) - { - if (cbd.exception) - std::rethrow_exception(cbd.exception); + /* + Performance test: delete 1000 files + ------------------------------------ + SHFileOperation - single file 33s + SHFileOperation - multiple files 2,1s + IFileOperation - single file 33s + IFileOperation - multiple files 2,1s - if (cNames.size() == 1) - throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempaths[0])), getLastErrorMessage()); + => SHFileOperation and IFileOperation have nearly IDENTICAL performance characteristics! - //batch recycling failed: retry one-by-one to get a better error message; see FileOperation.dll - for (size_t i = 0; i < cNames.size(); ++i) - { - if (notifyDeletionStatus) notifyDeletionStatus(itempaths[i]); + Nevertheless, let's use IFileOperation for better error reporting (including details on locked files)! + */ +#ifdef ZEN_WIN_VISTA_AND_LATER + vista::moveToRecycleBin(itempaths, onRecycleItem); //throw FileError - if (!moveToRecycleBin(&cNames[i], 1, nullptr, nullptr)) - throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtFileName(itempaths[i])), getLastErrorMessage()); //already includes details about locking errors! - } - } - } - else //regular recycle bin usage: available since XP: 1. bad error reporting 2. early failure +#else //regular recycle bin usage: available since XP: 1. bad error reporting 2. early failure + Zstring itempathsDoubleNull; + for (const Zstring& itempath : itempaths) { - Zstring itempathsDoubleNull; - for (const Zstring& itempath : itempaths) - { - itempathsDoubleNull += itempath; - itempathsDoubleNull += L'\0'; - } + itempathsDoubleNull += itempath; + itempathsDoubleNull += L'\0'; + } - SHFILEOPSTRUCT fileOp = {}; - fileOp.hwnd = nullptr; - fileOp.wFunc = FO_DELETE; - fileOp.pFrom = itempathsDoubleNull.c_str(); - fileOp.pTo = nullptr; - fileOp.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; - fileOp.fAnyOperationsAborted = false; - fileOp.hNameMappings = nullptr; - fileOp.lpszProgressTitle = nullptr; - - //"You should use fully-qualified path names with this function. Using it with relative path names is not thread safe." - if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) - { - std::wstring itempathFmt = fmtFileName(itempaths[0]); //probably not the correct file name for file lists larger than 1! - if (itempaths.size() > 1) - itempathFmt += L", ..."; //give at least some hint that there are multiple files, and the error need not be related to the first one - throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", itempathFmt)); - } + SHFILEOPSTRUCT fileOp = {}; + fileOp.hwnd = nullptr; + fileOp.wFunc = FO_DELETE; + fileOp.pFrom = itempathsDoubleNull.c_str(); + fileOp.pTo = nullptr; + fileOp.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; + fileOp.fAnyOperationsAborted = false; + fileOp.hNameMappings = nullptr; + fileOp.lpszProgressTitle = nullptr; + + //"You should use fully-qualified path names with this function. Using it with relative path names is not thread safe." + if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) + { + std::wstring itempathFmt = fmtFileName(itempaths[0]); //probably not the correct file name for file lists larger than 1! + if (itempaths.size() > 1) + itempathFmt += L", ..."; //give at least some hint that there are multiple files, and the error need not be related to the first one + throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", itempathFmt)); } +#endif } #endif @@ -241,39 +174,25 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError #ifdef ZEN_WIN bool zen::recycleBinExists(const Zstring& dirpath, const std::function& onUpdateGui) //throw FileError { - if (vistaOrLater()) - { - using namespace fileop; - const DllFun getRecycleBinStatus(getDllName(), funName_getRecycleBinStatus); - const DllFun getLastErrorMessage(getDllName(), funName_getLastErrorMessage); +#ifdef ZEN_WIN_VISTA_AND_LATER + return vista::supportsRecycleBin(dirpath); //throw FileError - if (!getRecycleBinStatus || !getLastErrorMessage) - throw FileError(replaceCpy(_("Checking recycle bin failed for folder %x."), L"%x", fmtFileName(dirpath)), - replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); - - bool hasRecycler = false; - if (!getRecycleBinStatus(dirpath.c_str(), hasRecycler)) - throw FileError(replaceCpy(_("Checking recycle bin failed for folder %x."), L"%x", fmtFileName(dirpath)), getLastErrorMessage()); - - return hasRecycler; - } - else +#else + //excessive runtime if recycle bin exists, is full and drive is slow: + auto ft = async([dirpath]() { - //excessive runtime if recycle bin exists, is full and drive is slow: - auto ft = async([dirpath]() - { - SHQUERYRBINFO recInfo = {}; - recInfo.cbSize = sizeof(recInfo); - return ::SHQueryRecycleBin(dirpath.c_str(), //__in_opt LPCTSTR pszRootPath, - &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo - }); + SHQUERYRBINFO recInfo = {}; + recInfo.cbSize = sizeof(recInfo); + return ::SHQueryRecycleBin(dirpath.c_str(), //__in_opt LPCTSTR pszRootPath, + &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo + }); - while (!ft.timed_wait(boost::posix_time::milliseconds(50))) - if (onUpdateGui) - onUpdateGui(); //may throw! + while (!ft.timed_wait(boost::posix_time::milliseconds(50))) + if (onUpdateGui) + onUpdateGui(); //may throw! - return ft.get() == S_OK; - } + return ft.get() == S_OK; +#endif //1. ::SHQueryRecycleBin() is excessive: traverses whole $Recycle.Bin directory tree each time!!!! But it's safe and correct. -- cgit