Skip to content

Commit bd66ddc

Browse files
committed
Show "file in use" dialog for all operations
1 parent c813b0b commit bd66ddc

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

src/Files.Launcher/MessageHandlers/FileOperationsHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ await Win32API.StartSTATask(() =>
621621
Name = x.ProcessName,
622622
Pid = x.Id,
623623
FileName = x.MainModule?.FileName
624-
});
624+
}).ToList();
625625
processes.ForEach(x => x.Dispose());
626626
await Win32API.SendMessageAsync(connection, new ValueSet() {
627627
{ "Processes", JsonConvert.SerializeObject(win32proc) }

src/Files.Uwp/Filesystem/FilesystemOperations/ShellFilesystemOperations.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Files.Backend.ViewModels.Dialogs;
1818
using CommunityToolkit.Mvvm.DependencyInjection;
1919
using Files.Backend.Services;
20+
using Microsoft.Toolkit.Uwp;
2021

2122
namespace Files.Filesystem
2223
{
@@ -158,6 +159,13 @@ public async Task<IStorageHistory> CopyItemsAsync(IList<IStorageItemWithPath> so
158159
return await CopyItemsAsync(source, destination, collisions, progress, errorCode, cancellationToken);
159160
}
160161
}
162+
else if (copyResult.Items.Any(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse))
163+
{
164+
// TODO: proper dialog, retry
165+
var failedSources = copyResult.Items.Where(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse);
166+
var lockingProcess = await WhoIsLockingAsync(failedSources.Select(x => x.HResult == HResult.COPYENGINE_E_SHARING_VIOLATION_SRC ? x.Source : x.Destination));
167+
await DialogDisplayHelper.ShowDialogAsync("FileInUseDeleteDialog/Title".GetLocalized(), lockingProcess != null ? string.Join(Environment.NewLine, lockingProcess.Select(x => $"Name: {x.Name}, PID: {x.Pid}")) : "");
168+
}
161169
errorCode?.Report(HResult.Convert(copyResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
162170
return null;
163171
}
@@ -382,6 +390,13 @@ public async Task<IStorageHistory> DeleteItemsAsync(IList<IStorageItemWithPath>
382390
return await DeleteItemsAsync(source, progress, errorCode, permanently, cancellationToken);
383391
}
384392
}
393+
else if (deleteResult.Items.Any(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse))
394+
{
395+
// TODO: proper dialog, retry
396+
var failedSources = deleteResult.Items.Where(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse);
397+
var lockingProcess = await WhoIsLockingAsync(failedSources.Select(x => x.HResult == HResult.COPYENGINE_E_SHARING_VIOLATION_SRC ? x.Source : x.Destination));
398+
await DialogDisplayHelper.ShowDialogAsync("FileInUseDeleteDialog/Title".GetLocalized(), lockingProcess != null ? string.Join(Environment.NewLine, lockingProcess.Select(x => $"Name: {x.Name}, PID: {x.Pid}")) : "");
399+
}
385400
errorCode?.Report(HResult.Convert(deleteResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
386401
return null;
387402
}
@@ -500,6 +515,13 @@ public async Task<IStorageHistory> MoveItemsAsync(IList<IStorageItemWithPath> so
500515
return await MoveItemsAsync(source, destination, collisions, progress, errorCode, cancellationToken);
501516
}
502517
}
518+
else if (moveResult.Items.Any(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse))
519+
{
520+
// TODO: proper dialog, retry
521+
var failedSources = moveResult.Items.Where(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse);
522+
var lockingProcess = await WhoIsLockingAsync(failedSources.Select(x => x.HResult == HResult.COPYENGINE_E_SHARING_VIOLATION_SRC ? x.Source : x.Destination));
523+
await DialogDisplayHelper.ShowDialogAsync("FileInUseDeleteDialog/Title".GetLocalized(), lockingProcess != null ? string.Join(Environment.NewLine, lockingProcess.Select(x => $"Name: {x.Name}, PID: {x.Pid}")) : "");
524+
}
503525
errorCode?.Report(HResult.Convert(moveResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
504526
return null;
505527
}
@@ -557,6 +579,13 @@ public async Task<IStorageHistory> RenameAsync(IStorageItemWithPath source, stri
557579
return await RenameAsync(source, newName, collision, errorCode, cancellationToken);
558580
}
559581
}
582+
else if (renameResult.Items.Any(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse))
583+
{
584+
// TODO: proper dialog, retry
585+
var failedSources = renameResult.Items.Where(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse);
586+
var lockingProcess = await WhoIsLockingAsync(failedSources.Select(x => x.HResult == HResult.COPYENGINE_E_SHARING_VIOLATION_SRC ? x.Source : x.Destination));
587+
await DialogDisplayHelper.ShowDialogAsync("FileInUseDeleteDialog/Title".GetLocalized(), lockingProcess != null ? string.Join(Environment.NewLine, lockingProcess.Select(x => $"Name: {x.Name}, PID: {x.Pid}")) : "");
588+
}
560589
errorCode?.Report(HResult.Convert(renameResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
561590
return null;
562591
}
@@ -645,6 +674,13 @@ public async Task<IStorageHistory> RestoreItemsFromTrashAsync(IList<IStorageItem
645674
return await RestoreItemsFromTrashAsync(source, destination, progress, errorCode, cancellationToken);
646675
}
647676
}
677+
else if (moveResult.Items.Any(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse))
678+
{
679+
// TODO: proper dialog, retry
680+
var failedSources = moveResult.Items.Where(x => HResult.Convert(x.HResult) == FileSystemStatusCode.InUse);
681+
var lockingProcess = await WhoIsLockingAsync(failedSources.Select(x => x.HResult == HResult.COPYENGINE_E_SHARING_VIOLATION_SRC ? x.Source : x.Destination));
682+
await DialogDisplayHelper.ShowDialogAsync("FileInUseDeleteDialog/Title".GetLocalized(), lockingProcess != null ? string.Join(Environment.NewLine, lockingProcess.Select(x => $"Name: {x.Name}, PID: {x.Pid}")) : "");
683+
}
648684
errorCode?.Report(HResult.Convert(moveResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
649685
return null;
650686
}
@@ -694,6 +730,25 @@ private async Task<bool> RequestAdminOperation()
694730
return false;
695731
}
696732

733+
private async Task<List<Win32Process>> WhoIsLockingAsync(IEnumerable<string> filesToCheck)
734+
{
735+
var connection = await AppServiceConnectionHelper.Instance;
736+
if (connection != null)
737+
{
738+
var (status, response) = await connection.SendMessageForResponseAsync(new ValueSet()
739+
{
740+
{ "Arguments", "FileOperation" },
741+
{ "fileop", "CheckFileInUse" },
742+
{ "filepath", string.Join('|', filesToCheck) }
743+
});
744+
if (status == AppServiceResponseStatus.Success && response.ContainsKey("Processes"))
745+
{
746+
return JsonConvert.DeserializeObject<List<Win32Process>>((string)response["Processes"]);
747+
}
748+
}
749+
return null;
750+
}
751+
697752
private struct HResult
698753
{
699754
// https://github.com/RickStrahl/DeleteFiles/blob/master/DeleteFiles/ZetaLongPaths/Native/FileOperations/Interop/CopyEngineResult.cs

0 commit comments

Comments
 (0)