Make drag-and-drop follow Windows Explorer and refresh the tree after folder operations.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -23,6 +23,9 @@ public sealed class WindowsShellFileOperations : IShellFileOperations
|
||||
}
|
||||
|
||||
public bool DeleteToRecycleBin(IReadOnlyList<string> paths, out string? error)
|
||||
=> Delete(paths, recycle: true, out error);
|
||||
|
||||
public bool Delete(IReadOnlyList<string> paths, bool recycle, out string? error)
|
||||
{
|
||||
error = null;
|
||||
if (paths.Count == 0)
|
||||
@@ -30,28 +33,48 @@ public sealed class WindowsShellFileOperations : IShellFileOperations
|
||||
return true;
|
||||
}
|
||||
|
||||
var joined = string.Join("\0", paths.Select(PathRules.FromExtended)) + "\0\0";
|
||||
var op = new NativeMethods.ShFileOpStruct
|
||||
var packed = string.Join("\0", paths.Select(PathRules.FromExtended)) + "\0\0";
|
||||
var pFrom = Marshal.StringToHGlobalUni(packed);
|
||||
try
|
||||
{
|
||||
hwnd = 0,
|
||||
wFunc = NativeMethods.FoDelete,
|
||||
pFrom = joined,
|
||||
pTo = null!,
|
||||
fFlags = (ushort)(NativeMethods.FofAllowUndo | NativeMethods.FofNoConfirmation | NativeMethods.FofNoErrorUi | NativeMethods.FofSilent),
|
||||
fAnyOperationsAborted = 0,
|
||||
hNameMappings = 0,
|
||||
lpszProgressTitle = null
|
||||
};
|
||||
var flags = NativeMethods.FofNoConfirmation | NativeMethods.FofNoErrorUi;
|
||||
if (recycle)
|
||||
{
|
||||
flags |= NativeMethods.FofAllowUndo;
|
||||
}
|
||||
|
||||
var rc = NativeMethods.SHFileOperation(ref op);
|
||||
if (rc != 0)
|
||||
{
|
||||
error = $"Recycle failed ({rc})";
|
||||
_logger.LogWarning("SHFileOperation delete returned {Code}", rc);
|
||||
return false;
|
||||
var op = new NativeMethods.ShFileOpStruct
|
||||
{
|
||||
hwnd = NativeMethods.GetForegroundWindow(),
|
||||
wFunc = (uint)NativeMethods.FoDelete,
|
||||
pFrom = pFrom,
|
||||
pTo = 0,
|
||||
fFlags = (ushort)flags,
|
||||
fAnyOperationsAborted = 0,
|
||||
hNameMappings = 0,
|
||||
lpszProgressTitle = null
|
||||
};
|
||||
|
||||
var rc = NativeMethods.SHFileOperation(ref op);
|
||||
if (op.fAnyOperationsAborted != 0)
|
||||
{
|
||||
error = "Cancelled";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
error = recycle ? $"Recycle failed ({rc})" : $"Delete failed ({rc})";
|
||||
_logger.LogWarning("SHFileOperation delete returned {Code} recycle={Recycle}", rc, recycle);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(pFrom);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CopyFileWithProgress(string source, string destination, bool overwrite, IProgress<long>? progress, CancellationToken cancellationToken, out string? error)
|
||||
@@ -126,6 +149,34 @@ public sealed class WindowsShellFileOperations : IShellFileOperations
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CreateShortcut(string targetPath, string shortcutPath, out string? error)
|
||||
{
|
||||
error = null;
|
||||
try
|
||||
{
|
||||
var type = Type.GetTypeFromProgID("WScript.Shell");
|
||||
if (type is null)
|
||||
{
|
||||
error = "Shortcut service is unavailable.";
|
||||
return false;
|
||||
}
|
||||
|
||||
dynamic shell = Activator.CreateInstance(type)!;
|
||||
dynamic shortcut = shell.CreateShortcut(PathRules.FromExtended(shortcutPath));
|
||||
var target = PathRules.FromExtended(targetPath);
|
||||
shortcut.TargetPath = target;
|
||||
shortcut.WorkingDirectory = Directory.Exists(target) ? target : PathRules.Parent(target);
|
||||
shortcut.Save();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
_logger.LogDebug(ex, "CreateShortcut failed for {Target}", targetPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class BackgroundIo
|
||||
|
||||
Reference in New Issue
Block a user