[FarGroup/FarManager] master: Continue 6695 (277edc3da)

0 views
Skip to first unread message

farg...@farmanager.com

unread,
Jun 10, 2026, 5:15:56 PM (7 days ago) Jun 10
to farco...@googlegroups.com
Repository : https://github.com/FarGroup/FarManager
On branch : master
Link : https://github.com/FarGroup/FarManager/commit/277edc3da609fe09badc3d82b44b88ec8c7fcb51

>---------------------------------------------------------------

commit 277edc3da609fe09badc3d82b44b88ec8c7fcb51
Author: Alex Alabuzhev <alab...@gmail.com>
Date: Wed Jun 10 22:12:44 2026 +0100

Continue 6695


>---------------------------------------------------------------

277edc3da609fe09badc3d82b44b88ec8c7fcb51
far/delete.cpp | 17 +++++++--
far/imports.hpp | 1 +
far/platform.com.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++
far/platform.com.hpp | 2 ++
4 files changed, 116 insertions(+), 3 deletions(-)

diff --git a/far/delete.cpp b/far/delete.cpp
index 98f5a9471..7cb6d7fed 100644
--- a/far/delete.cpp
+++ b/far/delete.cpp
@@ -74,6 +74,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// External:
#include "format.hpp"
+#include "platform.com.hpp"

//----------------------------------------------------------------------------

@@ -718,11 +719,21 @@ ShellDelete::ShellDelete(panel_ptr SrcPanel, delete_type const Type):

if (m_DeleteType == delete_type::recycle)
{
- if (SHQUERYRBINFO Info{ sizeof(Info) }; FAILED(SHQueryRecycleBin(GetPathRoot(SrcPanel->GetCurDir()).c_str(), &Info)))
+ const auto TestItem = ConvertNameToFull(SingleSelData.FileName);
+
+ const auto CanRecycle = [&]
{
- // If we can't query recycle bin for the given path, we likely won't be able to move into it, so update the action accordingly
+ // Clever method, should be more accurate (Vista+)
+ // You don't want to know how it works
+ if (const auto Result = os::com::can_recycle(TestItem))
+ return *Result;
+
+ // Legacy method
+ return os::fs::drive::get_type(SrcPanel->GetCurDir().c_str()) == DRIVE_FIXED;
+ }();
+
+ if (!CanRecycle)
m_DeleteType = delete_type::remove;
- }
}

show_confirmation(SrcPanel, m_DeleteType, SelCount, SingleSelData);
diff --git a/far/imports.hpp b/far/imports.hpp
index 8ffd732e8..75202ff95 100644
--- a/far/imports.hpp
+++ b/far/imports.hpp
@@ -171,6 +171,7 @@ public: \
DEFINE_IMPORT_FUNCTION(kernel32, nop, hr, WINAPI, HRESULT, GetThreadDescription, HANDLE Thread, PWSTR* ThreadDescription); // 10

DEFINE_IMPORT_FUNCTION(shell32, nop, hr, STDAPICALLTYPE, HRESULT, SHCreateAssociationRegistration, REFIID riid, void** ppv); // Vista
+ DEFINE_IMPORT_FUNCTION(shell32, nop, hr, STDAPICALLTYPE, HRESULT, SHCreateItemFromParsingName, PCWSTR Path, IBindCtx* bc, REFIID riid, void** ppv); // Vista

DEFINE_IMPORT_FUNCTION(user32, le, nullptr, WINAPI, HPOWERNOTIFY, RegisterPowerSettingNotification, HANDLE hRecipient, LPCGUID PowerSettingGuid, DWORD Flags); // Vista
DEFINE_IMPORT_FUNCTION(user32, le, false, WINAPI, BOOL, UnregisterPowerSettingNotification, HPOWERNOTIFY Handle); // Vista
diff --git a/far/platform.com.cpp b/far/platform.com.cpp
index 33ebce2da..2aa49e77e 100644
--- a/far/platform.com.cpp
+++ b/far/platform.com.cpp
@@ -258,4 +258,103 @@ namespace os::com
return {};
}
}
+
+ std::optional<bool> can_recycle(string_view const Object)
+ {
+WARNING_PUSH()
+WARNING_DISABLE_GCC("-Wnon-virtual-dtor")
+ class FileOperationProgressSink final: public IFileOperationProgressSink
+ {
+ public:
+ explicit FileOperationProgressSink(std::optional<bool>& CanRecycle):
+ m_CanRecycle(&CanRecycle)
+ {
+ }
+
+ // IUnknown
+ IFACEMETHODIMP QueryInterface(REFIID InterfaceId, PVOID* Interface) override
+ {
+ if (InterfaceId == IID_IUnknown)
+ *Interface = static_cast<IUnknown*>(static_cast<IFileOperationProgressSink*>(this));
+ else if (InterfaceId == IID_IFileOperationProgressSink)
+ *Interface = static_cast<IFileOperationProgressSink*>(this);
+ else
+ {
+ *Interface = {};
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+ }
+
+ IFACEMETHODIMP_(ULONG) AddRef() override { return 1; }
+ IFACEMETHODIMP_(ULONG) Release() override { return 1; }
+
+ // IFileOperationProgressSink
+ IFACEMETHODIMP StartOperations() override { return S_OK; }
+ IFACEMETHODIMP FinishOperations(HRESULT hrResult) override { return S_OK; }
+ IFACEMETHODIMP PreRenameItem(DWORD, IShellItem*, PCWSTR) override { return S_OK; }
+ IFACEMETHODIMP PostRenameItem(DWORD, IShellItem*, PCWSTR, HRESULT, IShellItem*) override { return S_OK; }
+ IFACEMETHODIMP PreMoveItem(DWORD, IShellItem*, IShellItem*, PCWSTR) override { return S_OK; }
+ IFACEMETHODIMP PostMoveItem(DWORD, IShellItem*, IShellItem*, PCWSTR, HRESULT, IShellItem*) override { return S_OK; }
+ IFACEMETHODIMP PreCopyItem(DWORD, IShellItem*, IShellItem*, PCWSTR) override { return S_OK; }
+ IFACEMETHODIMP PostCopyItem(DWORD, IShellItem*, IShellItem*, PCWSTR, HRESULT, IShellItem*) override { return S_OK; }
+
+ IFACEMETHODIMP PreDeleteItem(DWORD Flags, IShellItem*) override
+ {
+ *m_CanRecycle = flags::check_one(Flags, TSF_DELETE_RECYCLE_IF_POSSIBLE);
+ return E_ABORT;
+ }
+
+ IFACEMETHODIMP PostDeleteItem(DWORD, IShellItem*, HRESULT, IShellItem*) override { return S_OK; }
+ IFACEMETHODIMP PreNewItem(DWORD, IShellItem*, PCWSTR) override { return S_OK; }
+ IFACEMETHODIMP PostNewItem(DWORD, IShellItem*, PCWSTR, PCWSTR, DWORD, HRESULT, IShellItem*) override { return S_OK; }
+ IFACEMETHODIMP UpdateProgress(UINT, UINT) override { return S_OK; }
+ IFACEMETHODIMP ResetTimer() override { return S_OK; }
+ IFACEMETHODIMP PauseTimer() override { return S_OK; }
+ IFACEMETHODIMP ResumeTimer() override { return S_OK; }
+
+ private:
+ std::optional<bool>* m_CanRecycle;
+ };
+WARNING_POP()
+
+ // Unfortunately, there seems to be no way to just query if an item can be recycled (shame on you, Microsoft), so we have to get creative:
+ // IFileOperation has pre- and post-callbacks for all the actions it performs, including delete.
+ // The pre-delete callback receives a set of flags, one of which indicates whether the shell considers the item recyclable or not.
+ // So we call delete on the item in question, check the flag in the callback and then immediately abort the operation.
+ // This is ludicrous, but hey, as long as it works.
+
+ if (!imports.SHCreateItemFromParsingName)
+ return {};
+
+ SCOPED_ACTION(initialize)(mode::sta);
+
+ ptr<IFileOperation> FileOperation;
+ if (FAILED(CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOperation, IID_PPV_ARGS_Helper(&ptr_setter(FileOperation)))))
+ return {};
+
+ if (FAILED(FileOperation->SetOperationFlags(
+ FOF_ALLOWUNDO | // We want to know if the item can be recycled
+ FOF_NORECURSION | // Since we're not actually deleting anything, probing only the top level item is fine (and way faster)
+ FOF_NO_UI // Obviously, we don't want any UI
+ )))
+ return {};
+
+ ptr<IShellItem> Item;
+ if (FAILED(imports.SHCreateItemFromParsingName(null_terminated(Object).c_str(), nullptr, IID_IShellItem, IID_PPV_ARGS_Helper(&ptr_setter(Item)))))
+ return {};
+
+ std::optional<bool> CanRecycle;
+ FileOperationProgressSink Sink(CanRecycle);
+
+ if (FAILED(FileOperation->DeleteItem(Item.get(), &Sink)))
+ return {};
+
+ [[maybe_unused]] const auto Result = FileOperation->PerformOperations();
+ assert(Result == E_ABORT); // We should have aborted the operation in the callback, so it must fail
+
+ return CanRecycle;
+ }
}
diff --git a/far/platform.com.hpp b/far/platform.com.hpp
index d5bf1f575..178c442ef 100644
--- a/far/platform.com.hpp
+++ b/far/platform.com.hpp
@@ -108,6 +108,8 @@ namespace os::com
string get_shell_filetype_description(string_view FileName);

ptr<IFileIsInUse> create_file_is_in_use(const string& File);
+
+ std::optional<bool> can_recycle(string_view Object);
}

#endif // PLATFORM_COM_HPP_4E1C5B1E_3366_45BB_A55B_AD2B1357CA7D


Reply all
Reply to author
Forward
0 new messages