Summary
When moving a file from an Android device to the PC via drag & drop (i.e. a drag with the Move effect, not Copy), the file is first pulled into a local temp folder and then handed off to Windows to write to the chosen destination. If the user clicks Cancel on the Windows progress dialog, the destination never receives the file, but the source file on the Android device is deleted anyway.
The result is data loss: the file is neither at the PC destination nor on the Android source. Recovering it requires technical skills (and may not be possible at all — see below).
Steps to reproduce
- Connect an Android device and browse to a file in ADB Explorer.
- Drag the file out to a PC location (e.g. Explorer window or Desktop) using the Move effect (hold Shift while dragging, or drop on a target that requests move).
- While the Windows progress dialog is showing, click Cancel.
Expected
The source file remains on the Android device (since the move did not complete). Nothing is lost.
Actual
- The destination does not receive the file.
- The source file on the Android device is deleted.
- A partial/leftover copy may be sitting in a temp folder, but it is not guaranteed to survive (see "Why recovery is unreliable").
Recommendations
- Do not delete the Android source until the transfer to the final destination is actually confirmed complete. Deletion should be tied to the destination succeeding, not to an intermediate temp copy finishing.
- Never automatically wipe files that are left in limbo. Given how sensitive file moves are, if something goes wrong the leftover temp copy is the user's last chance at recovery. Cleanup of leftover files should not run proactively (e.g. at the start of the next drag) in a way that destroys data from a previously failed transfer. Let the OS reclaim temp space instead, or only ever delete files the app itself just successfully transferred.
Technical root-cause analysis
Note: Everything below this line — the code-level analysis, file/line references, and any suggested code — was produced with AI assistance based on reading the source. It should be reviewed by a maintainer before acting on it. Code snippets are illustrative direction, not tested patches.
How the move-drag actually works
For an Android→PC drag, ADB Explorer is the drag source. It does not copy a temp file to the destination itself — it hands Windows Explorer a read-only stream and lets Explorer write the destination. The data is produced lazily per file in FileClass.PrepareDescriptors (ADB Explorer/Models/File/FileClass.cs):
- A
FileSyncOperation.PullFile operation is created to pull Android → TempDragPath.
fileOp.PropertyChanged += PullOperation_PropertyChanged; subscribes the source-deletion handler.
- The descriptor's
Stream lambda queues the pull, waits for it to finish, then returns a COM stream over the temp file via NativeMethods.GetComStreamFromFile(...).
Defect 1 — source is deleted when the pull-to-temp finishes, not when the destination write succeeds
FileClass.PullOperation_PropertyChanged (ADB Explorer/Models/File/FileClass.cs, approx. lines 456–490):
if (op.Status is FileOperation.OperationStatus.Completed)
{
if (op.VFDO.CurrentEffect.HasFlag(DragDropEffects.Move))
{
// Delete file from device
ShellFileOperation.SilentDelete(op.Device, op.FilePath.FullPath); // <-- source deleted here
...
}
op.VFDO = null;
}
Here op is the pull operation (Android → temp). The moment that pull completes, the Android source is deleted. Nothing checks whether Explorer ever finished — or even started — writing the temp data to the real destination (no inspection of PerformedDropEffect, PasteSucceeded, or the destination file). So cancelling the destination write after the pull has completed leaves the source already gone.
This only affects the Move effect. DropSource.GiveFeedback in VirtualFileDataObject.cs downgrades the default to Copy unless Shift is held, and Copy never deletes the source — so copy-drags are safe.
Defect 2 — proactive temp cleanup destroys limbo files
In the happy path the temp file cleans itself up: NativeMethods.GetComStreamFromFile (ADB Explorer/Services/AppInfra/NativeMethods/DragDropNative.cs, ~line 183) opens the temp file with STGM_DELETEONRELEASE, so Windows deletes it when Explorer releases the stream. No explicit clear is needed for a successful transfer.
The explicit CopyPasteService.ClearTempFolder() (Directory.Delete(TempDragPath, true)) therefore only ever deletes files that did not self-delete — i.e. the limbo files left by a cancelled/failed move. It runs at the start of every new transfer:
VirtualFileDataObject.PrepareTransfer(files, …)
VirtualFileDataObject.PrepareTransfer(packages, …)
CopyPasteService.AcceptDataObject (the IsVirtual branch)
So the next drag operation wipes whatever the previous failed move left behind.
Why recovery is unreliable
STGM_DELETEONRELEASE can also remove the limbo copy: if Explorer requested the stream (pull ran → source already deleted) and the user then cancels, Explorer may still release the stream, deleting the temp file too. In that case nothing is left to recover. So "the file survives in temp" is not guaranteed — which is why fixing the deletion ordering (Defect 1) matters more than the cleanup behavior (Defect 2).
Suggested direction (AI-generated, untested)
- Defect 1: Move the source deletion out of the pull-completion handler. Delete the Android source only after the drop is confirmed — e.g. when Explorer reports
PerformedDropEffect == Move at the end of the async operation (IAsyncOperation.EndOperation / dwEffects in VirtualFileDataObject.cs), and/or after verifying the destination file exists and is complete.
- Defect 2: Remove (or tightly scope) the proactive
ClearTempFolder() wipes so they cannot delete data from a previous, unfinished transfer. Rely on STGM_DELETEONRELEASE for happy-path cleanup and the OS for reclaiming abandoned temp space.
Relevant files
ADB Explorer/Models/File/FileClass.cs — PrepareDescriptors, PullOperation_PropertyChanged
ADB Explorer/Services/AppInfra/LowLevel/VirtualFileDataObject.cs — PrepareTransfer, DropSource.GiveFeedback, IAsyncOperation members
ADB Explorer/Services/AppInfra/NativeMethods/DragDropNative.cs — GetComStreamFromFile (STGM_DELETEONRELEASE)
ADB Explorer/Services/AppInfra/CopyPasteService.cs — ClearTempFolder and its call sites
Summary
When moving a file from an Android device to the PC via drag & drop (i.e. a drag with the Move effect, not Copy), the file is first pulled into a local temp folder and then handed off to Windows to write to the chosen destination. If the user clicks Cancel on the Windows progress dialog, the destination never receives the file, but the source file on the Android device is deleted anyway.
The result is data loss: the file is neither at the PC destination nor on the Android source. Recovering it requires technical skills (and may not be possible at all — see below).
Steps to reproduce
Expected
The source file remains on the Android device (since the move did not complete). Nothing is lost.
Actual
Recommendations
Technical root-cause analysis
How the move-drag actually works
For an Android→PC drag, ADB Explorer is the drag source. It does not copy a temp file to the destination itself — it hands Windows Explorer a read-only stream and lets Explorer write the destination. The data is produced lazily per file in
FileClass.PrepareDescriptors(ADB Explorer/Models/File/FileClass.cs):FileSyncOperation.PullFileoperation is created to pull Android →TempDragPath.fileOp.PropertyChanged += PullOperation_PropertyChanged;subscribes the source-deletion handler.Streamlambda queues the pull, waits for it to finish, then returns a COM stream over the temp file viaNativeMethods.GetComStreamFromFile(...).Defect 1 — source is deleted when the pull-to-temp finishes, not when the destination write succeeds
FileClass.PullOperation_PropertyChanged(ADB Explorer/Models/File/FileClass.cs, approx. lines 456–490):Here
opis the pull operation (Android → temp). The moment that pull completes, the Android source is deleted. Nothing checks whether Explorer ever finished — or even started — writing the temp data to the real destination (no inspection ofPerformedDropEffect,PasteSucceeded, or the destination file). So cancelling the destination write after the pull has completed leaves the source already gone.This only affects the Move effect.
DropSource.GiveFeedbackinVirtualFileDataObject.csdowngrades the default to Copy unless Shift is held, and Copy never deletes the source — so copy-drags are safe.Defect 2 — proactive temp cleanup destroys limbo files
In the happy path the temp file cleans itself up:
NativeMethods.GetComStreamFromFile(ADB Explorer/Services/AppInfra/NativeMethods/DragDropNative.cs, ~line 183) opens the temp file withSTGM_DELETEONRELEASE, so Windows deletes it when Explorer releases the stream. No explicit clear is needed for a successful transfer.The explicit
CopyPasteService.ClearTempFolder()(Directory.Delete(TempDragPath, true)) therefore only ever deletes files that did not self-delete — i.e. the limbo files left by a cancelled/failed move. It runs at the start of every new transfer:VirtualFileDataObject.PrepareTransfer(files, …)VirtualFileDataObject.PrepareTransfer(packages, …)CopyPasteService.AcceptDataObject(theIsVirtualbranch)So the next drag operation wipes whatever the previous failed move left behind.
Why recovery is unreliable
STGM_DELETEONRELEASEcan also remove the limbo copy: if Explorer requested the stream (pull ran → source already deleted) and the user then cancels, Explorer may still release the stream, deleting the temp file too. In that case nothing is left to recover. So "the file survives in temp" is not guaranteed — which is why fixing the deletion ordering (Defect 1) matters more than the cleanup behavior (Defect 2).Suggested direction (AI-generated, untested)
PerformedDropEffect == Moveat the end of the async operation (IAsyncOperation.EndOperation/dwEffectsinVirtualFileDataObject.cs), and/or after verifying the destination file exists and is complete.ClearTempFolder()wipes so they cannot delete data from a previous, unfinished transfer. Rely onSTGM_DELETEONRELEASEfor happy-path cleanup and the OS for reclaiming abandoned temp space.Relevant files
ADB Explorer/Models/File/FileClass.cs—PrepareDescriptors,PullOperation_PropertyChangedADB Explorer/Services/AppInfra/LowLevel/VirtualFileDataObject.cs—PrepareTransfer,DropSource.GiveFeedback,IAsyncOperationmembersADB Explorer/Services/AppInfra/NativeMethods/DragDropNative.cs—GetComStreamFromFile(STGM_DELETEONRELEASE)ADB Explorer/Services/AppInfra/CopyPasteService.cs—ClearTempFolderand its call sites