Fix drag-handoff bugs: gate FX re-resolve on outside-panel, cache unresolvable OS-drag verdict, block double FX-add retry

This commit is contained in:
2026-07-30 00:09:00 -04:00
parent 0800760833
commit 875d5b4632
9 changed files with 110 additions and 75 deletions
+21 -4
View File
@@ -64,10 +64,11 @@ HGLOBAL buildHDrop(const std::vector<std::string>& paths) {
// COM reference counts MUST be interlocked. A CF_HDROP target is free to marshal the data
// object into another apartment and finish the copy on a background thread AFTER DoDragDrop
// has returned (Explorer's async file copy does exactly this). A plain ++/-- there races the
// source thread's post-DoDragDrop Release: one lost increment destroys the object — and with
// it the source HGLOBAL — before the target reads it, and the drop lands with no file. That
// race is intermittent and a retry usually wins it; do not "simplify" these back.
// has returned; Explorer's async file copy is suspected to do exactly this, though that
// specific behavior is not confirmed by experiment. A plain ++/-- there races the source
// thread's post-DoDragDrop Release: one lost increment destroys the object — and with it the
// source HGLOBAL — before the target reads it, and the drop lands with no file. That race is
// intermittent and a retry usually wins it; do not "simplify" these back.
inline ULONG comAddRef(volatile LONG& refs) {
return static_cast<ULONG>(InterlockedIncrement(&refs));
}
@@ -234,6 +235,15 @@ bool initiateDragOut(HWND__* /*panelHwnd*/, const std::vector<std::string>& abso
return hr == DRAGDROP_S_DROP && effect == DROPEFFECT_COPY;
}
bool canInitiateDragOut(const std::vector<std::string>& absolutePaths) {
if (absolutePaths.empty()) return false;
if (!ensureOleForThisThread()) return false;
HGLOBAL hdrop = buildHDrop(absolutePaths);
if (!hdrop) return false;
GlobalFree(hdrop); // probe only — initiateDragOut builds its own on the real attempt
return true;
}
} // namespace reasampler
#else // ---- macOS / Linux (SWELL) -----------------------------------------------------
@@ -260,6 +270,13 @@ bool initiateDragOut(HWND__* panelHwnd, const std::vector<std::string>& absolute
return true; // fire-and-forget; SWELL owns the drag from here (no accept/cancel return)
}
// SWELL exposes no readiness probe ahead of SWELL_InitiateDragDropOfFileList (which itself
// reports no accept/cancel outcome) — the non-empty check is the only thing knowable in
// advance on this platform.
bool canInitiateDragOut(const std::vector<std::string>& absolutePaths) {
return !absolutePaths.empty();
}
} // namespace reasampler
#endif