public async Task <IStorageHistory> MoveItemsAsync(IEnumerable <IStorageItemWithPath> source, IEnumerable <string> destination, IEnumerable <FileNameConflictResolveOptionType> collisions, IProgress <float> progress, IProgress <FileSystemStatusCode> errorCode, CancellationToken cancellationToken) { var connection = await AppServiceConnectionHelper.Instance; if (connection == null || source.Any(x => string.IsNullOrWhiteSpace(x.Path) || x.Path.StartsWith(@"\\?\")) || destination.Any(x => string.IsNullOrWhiteSpace(x) || x.StartsWith(@"\\?\"))) { // Fallback to builtin file operations return(await filesystemOperations.MoveItemsAsync(source, destination, collisions, progress, errorCode, cancellationToken)); } source = source.Where((src, index) => collisions.ElementAt(index) != FileNameConflictResolveOptionType.Skip).ToList(); destination = destination.Where((src, index) => collisions.ElementAt(index) != FileNameConflictResolveOptionType.Skip).ToList(); collisions = collisions.Where(c => c != FileNameConflictResolveOptionType.Skip).ToList(); 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 sourceReplace = source.Where((src, index) => collisions.ElementAt(index) == FileNameConflictResolveOptionType.ReplaceExisting); var destinationReplace = destination.Where((src, index) => collisions.ElementAt(index) == FileNameConflictResolveOptionType.ReplaceExisting); var sourceRename = source.Where((src, index) => collisions.ElementAt(index) != FileNameConflictResolveOptionType.ReplaceExisting); var destinationRename = destination.Where((src, index) => collisions.ElementAt(index) != FileNameConflictResolveOptionType.ReplaceExisting); var result = (FilesystemResult)true; var movedItems = new List <string>(); var movedSources = new List <string>(); if (sourceRename.Any()) { var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "MoveItem" }, { "operationID", operationID }, { "filepath", string.Join('|', sourceRename.Select(s => s.Path)) }, { "destpath", string.Join('|', destinationRename) }, { "overwrite", false } }); result &= (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); movedItems.AddRange(JsonConvert.DeserializeObject <IEnumerable <string> >(response.Get("MovedItems", "")) ?? Enumerable.Empty <string>()); movedSources.AddRange(JsonConvert.DeserializeObject <IEnumerable <string> >(response.Get("MovedSources", "")) ?? Enumerable.Empty <string>()); } if (sourceReplace.Any()) { var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "MoveItem" }, { "operationID", operationID }, { "filepath", string.Join('|', sourceReplace.Select(s => s.Path)) }, { "destpath", string.Join('|', destinationReplace) }, { "overwrite", true } }); result &= (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); movedSources.AddRange(JsonConvert.DeserializeObject <IEnumerable <string> >(response.Get("MovedSources", "")) ?? Enumerable.Empty <string>()); } if (connection != null) { connection.RequestReceived -= handler; } if (result) { progress?.Report(100.0f); errorCode?.Report(FileSystemStatusCode.Success); if (sourceRename.Any() && movedItems.Count() == sourceRename.Count()) { return(new StorageHistory(FileOperationType.Move, sourceRename, movedItems.Select((item, index) => StorageItemHelpers.FromPathAndType(item, sourceRename.ElementAt(index).ItemType)))); } return(null); // Cannot undo overwrite operation } else { // Retry failed operations var movedZip = source.Zip(destination, (src, dest) => new { src, dest }).Zip(collisions, (z1, coll) => new { z1.src, z1.dest, coll }).Where(x => !movedSources.Contains(x.src.Path)); return(await filesystemOperations.MoveItemsAsync(movedZip.Select(x => x.src), movedZip.Select(x => x.dest), movedZip.Select(x => x.coll), progress, errorCode, cancellationToken)); } }
public async Task <IStorageHistory> MoveItemsAsync(IList <IStorageItemWithPath> source, IList <string> destination, IList <FileNameConflictResolveOptionType> collisions, IProgress <float> progress, IProgress <FileSystemStatusCode> errorCode, CancellationToken cancellationToken) { var connection = await AppServiceConnectionHelper.Instance; if (connection == null || source.Any(x => string.IsNullOrWhiteSpace(x.Path) || x.Path.StartsWith(@"\\?\", StringComparison.Ordinal)) || destination.Any(x => string.IsNullOrWhiteSpace(x) || x.StartsWith(@"\\?\", StringComparison.Ordinal) || FtpHelpers.IsFtpPath(x))) { // Fallback to builtin file operations return(await filesystemOperations.MoveItemsAsync(source, destination, collisions, progress, errorCode, cancellationToken)); } var sourceNoSkip = source.Zip(collisions, (src, coll) => new { src, coll }).Where(item => item.coll != FileNameConflictResolveOptionType.Skip).Select(item => item.src); var destinationNoSkip = destination.Zip(collisions, (src, coll) => new { src, coll }).Where(item => item.coll != FileNameConflictResolveOptionType.Skip).Select(item => item.src); var collisionsNoSkip = collisions.Where(c => c != FileNameConflictResolveOptionType.Skip); 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 sourceReplace = sourceNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll == FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var destinationReplace = destinationNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll == FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var sourceRename = sourceNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll != FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var destinationRename = destinationNoSkip.Zip(collisionsNoSkip, (src, coll) => new { src, coll }).Where(item => item.coll != FileNameConflictResolveOptionType.ReplaceExisting).Select(item => item.src); var result = (FilesystemResult)true; var moveResult = new ShellOperationResult(); if (sourceRename.Any()) { var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "MoveItem" }, { "operationID", operationID }, { "filepath", string.Join('|', sourceRename.Select(s => s.Path)) }, { "destpath", string.Join('|', destinationRename) }, { "overwrite", false }, { "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() } }); result &= (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); var shellOpResult = JsonConvert.DeserializeObject <ShellOperationResult>(response.Get("Result", "")); moveResult.Items.AddRange(shellOpResult?.Items ?? Enumerable.Empty <ShellOperationItemResult>()); } if (sourceReplace.Any()) { var(status, response) = await connection.SendMessageForResponseAsync(new ValueSet() { { "Arguments", "FileOperation" }, { "fileop", "MoveItem" }, { "operationID", operationID }, { "filepath", string.Join('|', sourceReplace.Select(s => s.Path)) }, { "destpath", string.Join('|', destinationReplace) }, { "overwrite", true }, { "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() } }); result &= (FilesystemResult)(status == AppServiceResponseStatus.Success && response.Get("Success", false)); var shellOpResult = JsonConvert.DeserializeObject <ShellOperationResult>(response.Get("Result", "")); moveResult.Items.AddRange(shellOpResult?.Items ?? Enumerable.Empty <ShellOperationItemResult>()); } if (connection != null) { connection.RequestReceived -= handler; } result &= (FilesystemResult)moveResult.Items.All(x => x.Succeeded); if (result) { progress?.Report(100.0f); errorCode?.Report(FileSystemStatusCode.Success); var movedSources = moveResult.Items.Where(x => x.Succeeded && x.Destination != null && x.Source != x.Destination); if (movedSources.Any()) { var sourceMatch = await movedSources.Select(x => sourceRename.SingleOrDefault(s => s.Path == x.Source)).Where(x => x != null).ToListAsync(); return(new StorageHistory(FileOperationType.Move, sourceMatch, await movedSources.Zip(sourceMatch, (rSrc, oSrc) => new { rSrc, oSrc }) .Select(item => StorageHelpers.FromPathAndType(item.rSrc.Destination, item.oSrc.ItemType)).ToListAsync())); } return(null); // Cannot undo overwrite operation } else { // Retry failed operations var failedSources = moveResult.Items.Where(x => !x.Succeeded); var moveZip = sourceNoSkip.Zip(destinationNoSkip, (src, dest) => new { src, dest }).Zip(collisionsNoSkip, (z1, coll) => new { z1.src, z1.dest, coll }); var sourceMatch = await failedSources.Select(x => moveZip.SingleOrDefault(s => s.src.Path == x.Source)).Where(x => x != null).ToListAsync(); return(await filesystemOperations.MoveItemsAsync( await sourceMatch.Select(x => x.src).ToListAsync(), await sourceMatch.Select(x => x.dest).ToListAsync(), await sourceMatch.Select(x => x.coll).ToListAsync(), progress, errorCode, cancellationToken)); } }