|
17 | 17 | using Files.Backend.ViewModels.Dialogs;
|
18 | 18 | using CommunityToolkit.Mvvm.DependencyInjection;
|
19 | 19 | using Files.Backend.Services;
|
| 20 | +using Microsoft.Toolkit.Uwp; |
20 | 21 |
|
21 | 22 | namespace Files.Filesystem
|
22 | 23 | {
|
@@ -158,6 +159,13 @@ public async Task<IStorageHistory> CopyItemsAsync(IList<IStorageItemWithPath> so
|
158 | 159 | return await CopyItemsAsync(source, destination, collisions, progress, errorCode, cancellationToken);
|
159 | 160 | }
|
160 | 161 | }
|
| 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 | + } |
161 | 169 | errorCode?.Report(HResult.Convert(copyResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
|
162 | 170 | return null;
|
163 | 171 | }
|
@@ -382,6 +390,13 @@ public async Task<IStorageHistory> DeleteItemsAsync(IList<IStorageItemWithPath>
|
382 | 390 | return await DeleteItemsAsync(source, progress, errorCode, permanently, cancellationToken);
|
383 | 391 | }
|
384 | 392 | }
|
| 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 | + } |
385 | 400 | errorCode?.Report(HResult.Convert(deleteResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
|
386 | 401 | return null;
|
387 | 402 | }
|
@@ -500,6 +515,13 @@ public async Task<IStorageHistory> MoveItemsAsync(IList<IStorageItemWithPath> so
|
500 | 515 | return await MoveItemsAsync(source, destination, collisions, progress, errorCode, cancellationToken);
|
501 | 516 | }
|
502 | 517 | }
|
| 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 | + } |
503 | 525 | errorCode?.Report(HResult.Convert(moveResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
|
504 | 526 | return null;
|
505 | 527 | }
|
@@ -557,6 +579,13 @@ public async Task<IStorageHistory> RenameAsync(IStorageItemWithPath source, stri
|
557 | 579 | return await RenameAsync(source, newName, collision, errorCode, cancellationToken);
|
558 | 580 | }
|
559 | 581 | }
|
| 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 | + } |
560 | 589 | errorCode?.Report(HResult.Convert(renameResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
|
561 | 590 | return null;
|
562 | 591 | }
|
@@ -645,6 +674,13 @@ public async Task<IStorageHistory> RestoreItemsFromTrashAsync(IList<IStorageItem
|
645 | 674 | return await RestoreItemsFromTrashAsync(source, destination, progress, errorCode, cancellationToken);
|
646 | 675 | }
|
647 | 676 | }
|
| 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 | + } |
648 | 684 | errorCode?.Report(HResult.Convert(moveResult.Items.FirstOrDefault(x => !x.Succeeded)?.HResult));
|
649 | 685 | return null;
|
650 | 686 | }
|
@@ -694,6 +730,25 @@ private async Task<bool> RequestAdminOperation()
|
694 | 730 | return false;
|
695 | 731 | }
|
696 | 732 |
|
| 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 | + |
697 | 752 | private struct HResult
|
698 | 753 | {
|
699 | 754 | // https://github.com/RickStrahl/DeleteFiles/blob/master/DeleteFiles/ZetaLongPaths/Native/FileOperations/Interop/CopyEngineResult.cs
|
|
0 commit comments