public async Task <IStorageHistory> DeleteItemsAsync(IEnumerable <IStorageItemWithPath> source, IProgress <float> progress, IProgress <FileSystemStatusCode> errorCode, bool permanently, CancellationToken cancellationToken) { var connection = await AppServiceConnectionHelper.Instance; if (connection == null || source.Any(x => string.IsNullOrWhiteSpace(x.Path) || x.Path.StartsWith(@"\\?\"))) { // Fallback to builtin file operations return(await filesystemOperations.DeleteItemsAsync(source, progress, errorCode, permanently, cancellationToken)); } var deleleFilePaths = source.Select(s => s.Path); var deleteFromRecycleBin = source.Any() ? recycleBinHelpers.IsPathUnderRecycleBin(source.ElementAt(0).Path) : false; permanently |= deleteFromRecycleBin; if (deleteFromRecycleBin) { // Recycle bin also stores a file starting with $I for each item deleleFilePaths = deleleFilePaths.Concat(source.Select(x => Path.Combine(Path.GetDirectoryName(x.Path), Path.GetFileName(x.Path).Replace("$R", "$I")))); } var operationID = Guid.NewGuid().ToString(); using var _ = cancellationToken.Register(CancelOperation, operationID, false); EventHandler <Dictionary <string, object> > handler = (s, e) => OnProgressUpdated(s, e, progress); connection.RequestReceived += handler; var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "DeleteItem" }, { "operationID", operationID }, { "filepath", string.Join('|', deleleFilePaths) }, { "permanently", permanently } }); var result = (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); if (connection != null) { connection.RequestReceived -= handler; } if (result) { progress?.Report(100.0f); var deletedItems = JsonConvert.DeserializeObject <IEnumerable <string> >(response["DeletedItems"] as string); var recycledItems = JsonConvert.DeserializeObject <IEnumerable <string> >(response["RecycledItems"] as string); errorCode?.Report(FileSystemStatusCode.Success); if (deletedItems != null) { foreach (var item in deletedItems) { await associatedInstance.FilesystemViewModel.RemoveFileOrFolderAsync(item); } } if (!permanently && recycledItems != null && recycledItems.Count() == source.Count()) { return(new StorageHistory(FileOperationType.Recycle, source, recycledItems.Select((item, index) => StorageItemHelpers.FromPathAndType(item, source.ElementAt(index).ItemType)))); } return(new StorageHistory(FileOperationType.Delete, source, null)); } else { // Retry failed operations var deletedSources = JsonConvert.DeserializeObject <IEnumerable <string> >(response.Get("DeletedItems", "")) ?? Enumerable.Empty <string>(); var deletedZip = source.Where(x => !deletedSources.Contains(x.Path)); return(await filesystemOperations.DeleteItemsAsync(deletedZip, progress, errorCode, permanently, cancellationToken)); } }
public async Task <IStorageHistory> DeleteItemsAsync(IEnumerable <IStorageItemWithPath> source, IProgress <float> progress, IProgress <FileSystemStatusCode> errorCode, bool permanently, CancellationToken cancellationToken) { var connection = await AppServiceConnectionHelper.Instance; if (connection == null || source.Any(x => string.IsNullOrWhiteSpace(x.Path) || x.Path.StartsWith(@"\\?\", StringComparison.Ordinal) || FtpHelpers.IsFtpPath(x.Path))) { // Fallback to builtin file operations return(await filesystemOperations.DeleteItemsAsync(source, progress, errorCode, permanently, cancellationToken)); } source = source.DistinctBy(x => x.Path); // #5771 var deleleFilePaths = source.Select(s => s.Path); var deleteFromRecycleBin = source.Any() ? recycleBinHelpers.IsPathUnderRecycleBin(source.ElementAt(0).Path) : false; permanently |= deleteFromRecycleBin; if (deleteFromRecycleBin) { // Recycle bin also stores a file starting with $I for each item deleleFilePaths = deleleFilePaths.Concat(source.Select(x => Path.Combine(Path.GetDirectoryName(x.Path), Path.GetFileName(x.Path).Replace("$R", "$I", StringComparison.Ordinal)))).Distinct(); } var operationID = Guid.NewGuid().ToString(); using var r = cancellationToken.Register(CancelOperation, operationID, false); EventHandler <Dictionary <string, object> > handler = (s, e) => OnProgressUpdated(s, e, operationID, progress); connection.RequestReceived += handler; var deleteResult = new ShellOperationResult(); var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "DeleteItem" }, { "operationID", operationID }, { "filepath", string.Join('|', deleleFilePaths) }, { "permanently", permanently }, { "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() } }); var result = (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); var shellOpResult = JsonConvert.DeserializeObject <ShellOperationResult>(response.Get("Result", "")); deleteResult.Items.AddRange(shellOpResult?.Items ?? Enumerable.Empty <ShellOperationItemResult>()); if (connection != null) { connection.RequestReceived -= handler; } result &= (FilesystemResult)deleteResult.Items.All(x => x.Succeeded); if (result) { progress?.Report(100.0f); errorCode?.Report(FileSystemStatusCode.Success); foreach (var item in deleteResult.Items) { await associatedInstance.FilesystemViewModel.RemoveFileOrFolderAsync(item.Source); } var recycledSources = deleteResult.Items.Where(x => source.Select(s => s.Path).Contains(x.Source)).Where(x => x.Succeeded && x.Destination != null && x.Source != x.Destination); if (recycledSources.Any()) { return(new StorageHistory(FileOperationType.Recycle, recycledSources.Select(x => source.Single(s => s.Path == x.Source)), recycledSources.Select(item => StorageHelpers.FromPathAndType(item.Destination, source.Single(s => s.Path == item.Source).ItemType)))); } return(new StorageHistory(FileOperationType.Delete, source, null)); } else { // Retry failed operations var failedSources = deleteResult.Items.Where(x => source.Select(s => s.Path).Contains(x.Source)) .Where(x => !x.Succeeded && x.HResult != HResult.COPYENGINE_E_USER_CANCELLED && x.HResult != HResult.COPYENGINE_E_RECYCLE_BIN_NOT_FOUND); return(await filesystemOperations.DeleteItemsAsync( failedSources.Select(x => source.Single(s => s.Path == x.Source)), progress, errorCode, permanently, cancellationToken)); } }