Exemplo n.º 1
0
        public void NewItemTest()
        {
            var files = new[] { "test.docx", "test.txt", "test.xlsx" };

            using (var op = new ShellFileOperations())
            {
                foreach (var file in files)
                {
                    op.QueueNewItemOperation(ShellFolder.Desktop, file);
                }
                op.PostNewItem += HandleEvent;
                op.PerformOperations();
            }
            foreach (var file in files)
            {
                var fn = Path.Combine(@"C:\Users\dahall\Desktop", file);
                Assert.That(File.Exists(fn), Is.True);
                File.Delete(fn);
            }

            void HandleEvent(object sender, ShellFileOperations.ShellFileOpEventArgs args)
            {
                Debug.WriteLine(args);
                Assert.That(args.Result.Succeeded, Is.True);
            }
        }
Exemplo n.º 2
0
        public static bool Delete(IEnumerable <string> Source, bool PermanentDelete, ProgressChangedEventHandler Progress, EventHandler <ShellFileOperations.ShellFileOpEventArgs> PostDeleteEvent)
        {
            try
            {
                using (ShellFileOperations Operation = new ShellFileOperations
                {
                    Options = PermanentDelete
                    ? ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent | ShellFileOperations.OperationFlags.NoConfirmation
                    : ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.Silent | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.RecycleOnDelete
                })
                {
                    Operation.UpdateProgress += Progress;
                    Operation.PostDeleteItem += PostDeleteEvent;

                    foreach (string Path in Source)
                    {
                        using (ShellItem Item = new ShellItem(Path))
                        {
                            Operation.QueueDeleteOperation(Item);
                        }
                    }

                    Operation.PerformOperations();

                    Operation.PostDeleteItem -= PostDeleteEvent;
                    Operation.UpdateProgress -= Progress;
                }

                return(true);
            }
            catch
            {
                return(false);
            }
        }
Exemplo n.º 3
0
        public void SetPropsTest2()
        {
            const string fn      = "test.docx";
            var          fp      = Path.Combine(ShellFolder.Desktop.FileSystemPath, fn);
            var          authors = new[] { "David" };

            using (var p = new ShellItemPropertyUpdates {
                { PROPERTYKEY.System.Author, authors }
            })
                using (var op = new ShellFileOperations())
                {
                    op.PostNewItem += HandleEvent;
                    op.QueueNewItemOperation(ShellFolder.Desktop, fn, template: @"C:\Users\dahall\Documents\Custom Office Templates\blank.dotx");
                    op.QueueApplyPropertiesOperation(new ShellItem(fp), p);
                    op.PerformOperations();
                }
            Assert.That(new ShellItem(fp).Properties[PROPERTYKEY.System.Author], Is.EquivalentTo(authors));
            File.Delete(fp);

            void HandleEvent(object sender, ShellFileOperations.ShellFileOpEventArgs args)
            {
                Debug.WriteLine(args);
                Assert.That(args.Result.Succeeded, Is.True);
            }
        }
Exemplo n.º 4
0
        public void CopyWithProgressTest()
        {
            // Setup hidden copy op with progress handler
            bool progressShown = false;

            using var op       = new ShellFileOperations();
            op.Options         = ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent;
            op.UpdateProgress += Op_UpdateProgress;

            // Run the operation
            using var shi = new ShellItem(TestCaseSources.LargeFile);
            op.QueueCopyOperation(shi, ShellFolder.Desktop);
            op.PerformOperations();

            // Asert and clean
            Assert.IsTrue(progressShown);
            var dest = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), Path.GetFileName(TestCaseSources.LargeFile));

            File.Delete(dest);

            void Op_UpdateProgress(object sender, System.ComponentModel.ProgressChangedEventArgs args)
            {
                Debug.WriteLine($"{args.UserState}: {args.ProgressPercentage}%");
                progressShown = true;
            }
        }
Exemplo n.º 5
0
        public void MultOpsTest()
        {
            using (var op = new ShellFileOperations())
            {
                op.Options |= ShellFileOperations.OperationFlags.NoMinimizeBox;
                var shi = new ShellItem(@"C:\Users\dahall\Downloads\lubuntu-16.04.2-desktop-amd64.iso");
                op.PostCopyItem += HandleEvent;
                op.QueueCopyOperation(shi, ShellFolder.Desktop);
                shi = new ShellItem(@"C:\Users\dahall\Desktop\lubuntu-16.04.2-desktop-amd64.iso");
                op.QueueMoveOperation(shi, new ShellFolder(KNOWNFOLDERID.FOLDERID_Documents));
                op.PostMoveItem += HandleEvent;
                shi              = new ShellItem(@"C:\Users\dahall\Documents\lubuntu-16.04.2-desktop-amd64.iso");
                op.QueueRenameOperation(shi, "MuchLongerNameForTheFile.iso");
                op.PostRenameItem += HandleEvent;
                shi = new ShellItem(@"C:\Users\dahall\Documents\MuchLongerNameForTheFile.iso");
                op.QueueDeleteOperation(shi);
                op.PostDeleteItem += HandleEvent;
                op.PerformOperations();
            }

            void HandleEvent(object sender, ShellFileOperations.ShellFileOpEventArgs args)
            {
                Debug.WriteLine(args);
                Assert.That(args.Result.Succeeded, Is.True);
            }
        }
Exemplo n.º 6
0
        public static bool Rename(string Source, string DesireName, EventHandler <ShellFileOperations.ShellFileOpEventArgs> PostRenameEvent)
        {
            try
            {
                using (ShellFileOperations Operation = new ShellFileOperations
                {
                    Options = ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent | ShellFileOperations.OperationFlags.RequireElevation | ShellFileOperations.OperationFlags.RenameOnCollision
                })
                {
                    Operation.PostRenameItem += PostRenameEvent;

                    using (ShellItem Item = new ShellItem(Source))
                    {
                        Operation.QueueRenameOperation(Item, DesireName);
                    }

                    Operation.PerformOperations();

                    Operation.PostRenameItem -= PostRenameEvent;
                }

                return(true);
            }
            catch
            {
                return(false);
            }
        }
Exemplo n.º 7
0
        public static bool Copy(IEnumerable <KeyValuePair <string, string> > Source, string DestinationPath, ProgressChangedEventHandler Progress, EventHandler <ShellFileOperations.ShellFileOpEventArgs> PostCopyEvent)
        {
            try
            {
                if (!Directory.Exists(DestinationPath))
                {
                    _ = Directory.CreateDirectory(DestinationPath);
                }

                ShellFileOperations.OperationFlags Options = Source.All((Item) => Path.GetDirectoryName(Item.Key) == DestinationPath)
                                                             ? ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent | ShellFileOperations.OperationFlags.RenameOnCollision
                                                             : ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent;

                using (ShellFileOperations Operation = new ShellFileOperations
                {
                    Options = Options
                })
                {
                    Operation.UpdateProgress += Progress;
                    Operation.PostCopyItem   += PostCopyEvent;

                    foreach (KeyValuePair <string, string> SourceInfo in Source)
                    {
                        using (ShellItem SourceItem = new ShellItem(SourceInfo.Key))
                            using (ShellFolder DestItem = new ShellFolder(DestinationPath))
                            {
                                Operation.QueueCopyOperation(SourceItem, DestItem, string.IsNullOrEmpty(SourceInfo.Value) ? null : SourceInfo.Value);
                            }
                    }

                    Operation.PerformOperations();

                    Operation.PostCopyItem   -= PostCopyEvent;
                    Operation.UpdateProgress -= Progress;
                }

                return(true);
            }
            catch
            {
                return(false);
            }
        }
Exemplo n.º 8
0
        public static bool Move(IEnumerable <KeyValuePair <string, string> > Source, string DestinationPath, ProgressChangedEventHandler Progress, EventHandler <ShellFileOperations.ShellFileOpEventArgs> PostMoveEvent)
        {
            try
            {
                if (!Directory.Exists(DestinationPath))
                {
                    _ = Directory.CreateDirectory(DestinationPath);
                }

                using (ShellFileOperations Operation = new ShellFileOperations
                {
                    Options = ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent | ShellFileOperations.OperationFlags.RequireElevation
                })
                {
                    Operation.UpdateProgress += Progress;
                    Operation.PostMoveItem   += PostMoveEvent;

                    foreach (KeyValuePair <string, string> SourceInfo in Source)
                    {
                        using (ShellItem SourceItem = new ShellItem(SourceInfo.Key))
                            using (ShellFolder DestItem = new ShellFolder(DestinationPath))
                            {
                                Operation.QueueMoveOperation(SourceItem, DestItem, string.IsNullOrEmpty(SourceInfo.Value) ? null : SourceInfo.Value);
                            }
                    }

                    Operation.PerformOperations();

                    Operation.PostMoveItem   -= PostMoveEvent;
                    Operation.UpdateProgress -= Progress;
                }

                return(true);
            }
            catch
            {
                return(false);
            }
        }
Exemplo n.º 9
0
        public static bool Rename(string Source, string DesireName)
        {
            try
            {
                using (ShellFileOperations Operation = new ShellFileOperations
                {
                    Options = ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent
                })
                {
                    using (ShellItem Item = new ShellItem(Source))
                    {
                        Operation.QueueRenameOperation(Item, DesireName);
                    }

                    Operation.PerformOperations();
                }

                return(true);
            }
            catch
            {
                return(false);
            }
        }
Exemplo n.º 10
0
        public void MultOpsTest()
        {
            const string newLargeFile = "MuchLongerNameForTheFile.bin";

            using (var op = new ShellFileOperations())
            {
                op.Options |= ShellFileOperations.OperationFlags.NoMinimizeBox;
                var shi = new ShellItem(TestCaseSources.LargeFile);
                op.PostCopyItem += HandleEvent;
                op.QueueCopyOperation(shi, ShellFolder.Desktop);
                var dest = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), Path.GetFileName(TestCaseSources.LargeFile));
                shi = new ShellItem(dest);
                op.QueueMoveOperation(shi, new ShellFolder(KNOWNFOLDERID.FOLDERID_Documents));
                op.PostMoveItem += HandleEvent;
                dest             = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileName(TestCaseSources.LargeFile));
                shi              = new ShellItem(dest);
                op.QueueRenameOperation(shi, newLargeFile);
                op.PostRenameItem += HandleEvent;
                dest = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), newLargeFile);
                shi  = new ShellItem(dest);
                op.QueueDeleteOperation(shi);
                op.PostDeleteItem += HandleEvent;
                op.PerformOperations();
            }
Exemplo n.º 11
0
        public void MoveItemTest2()
        {
            var tmp    = new TempFile();
            var winDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);

            using ShellFileOperations Operation = new ShellFileOperations
                  {
                      Options = ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent
                  };
            Operation.UpdateProgress += Operation_UpdateProgress;
            Operation.PostMoveItem   += Operation_PostMoveItem;

            using (var fld = new ShellFolder(winDir))
                using (var item = new ShellItem(tmp.FullName))
                    Assert.That(() => Operation.QueueMoveOperation(item, fld), Throws.Nothing);
            Assert.That(() => Operation.PerformOperations(), Throws.Nothing);

            var destFile = Path.Combine(winDir, Path.GetFileName(tmp.FullName));

            Assert.IsTrue(File.Exists(destFile));
            File.Delete(destFile);

            Operation.PostMoveItem   -= Operation_PostMoveItem;
            Operation.UpdateProgress -= Operation_UpdateProgress;
Exemplo n.º 12
0
        private async Task ParseFileOperationAsync(NamedPipeServerStream connection, Dictionary <string, object> message)
        {
            switch (message.Get("fileop", ""))
            {
            case "Clipboard":
                await Win32API.StartSTATask(() =>
                {
                    System.Windows.Forms.Clipboard.Clear();
                    var fileToCopy = (string)message["filepath"];
                    var operation  = (DataPackageOperation)(long)message["operation"];
                    var fileList   = new System.Collections.Specialized.StringCollection();
                    fileList.AddRange(fileToCopy.Split('|'));
                    if (operation == DataPackageOperation.Copy)
                    {
                        System.Windows.Forms.Clipboard.SetFileDropList(fileList);
                    }
                    else if (operation == DataPackageOperation.Move)
                    {
                        byte[] moveEffect       = new byte[] { 2, 0, 0, 0 };
                        MemoryStream dropEffect = new MemoryStream();
                        dropEffect.Write(moveEffect, 0, moveEffect.Length);
                        var data = new System.Windows.Forms.DataObject();
                        data.SetFileDropList(fileList);
                        data.SetData("Preferred DropEffect", dropEffect);
                        System.Windows.Forms.Clipboard.SetDataObject(data, true);
                    }
                    return(true);
                });

                break;

            case "DragDrop":
                var dropPath = (string)message["droppath"];
                var result   = await Win32API.StartSTATask(() =>
                {
                    var rdo = new RemoteDataObject(System.Windows.Forms.Clipboard.GetDataObject());

                    foreach (RemoteDataObject.DataPackage package in rdo.GetRemoteData())
                    {
                        try
                        {
                            if (package.ItemType == RemoteDataObject.StorageType.File)
                            {
                                string directoryPath = Path.GetDirectoryName(dropPath);
                                if (!Directory.Exists(directoryPath))
                                {
                                    Directory.CreateDirectory(directoryPath);
                                }

                                string uniqueName = Win32API.GenerateUniquePath(Path.Combine(dropPath, package.Name));
                                using (FileStream stream = new FileStream(uniqueName, FileMode.CreateNew))
                                {
                                    package.ContentStream.CopyTo(stream);
                                }
                            }
                            else
                            {
                                string directoryPath = Path.Combine(dropPath, package.Name);
                                if (!Directory.Exists(directoryPath))
                                {
                                    Directory.CreateDirectory(directoryPath);
                                }
                            }
                        }
                        finally
                        {
                            package.Dispose();
                        }
                    }
                    return(true);
                });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                {
                    { "Success", result }
                }, message.Get("RequestID", (string)null));

                break;

            case "DeleteItem":
            {
                var fileToDeletePath = ((string)message["filepath"]).Split('|');
                var permanently      = (bool)message["permanently"];
                var operationID      = (string)message["operationID"];
                var(succcess, deletedItems, recycledItems) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            op.Options = ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoConfirmation
                                         | ShellFileOperations.OperationFlags.NoErrorUI
                                         | ShellFileOperations.OperationFlags.EarlyFailure;
                            if (!permanently)
                            {
                                op.Options |= ShellFileOperations.OperationFlags.RecycleOnDelete
                                              | ShellFileOperations.OperationFlags.WantNukeWarning;
                            }
                            List <string> deletedItems  = new List <string>();
                            List <string> recycledItems = new List <string>();

                            for (var i = 0; i < fileToDeletePath.Length; i++)
                            {
                                using var shi = new ShellItem(fileToDeletePath[i]);
                                op.QueueDeleteOperation(shi);
                            }

                            handleTable.SetValue(operationID, false);
                            var deleteTcs      = new TaskCompletionSource <bool>();
                            op.PostDeleteItem += (s, e) =>
                            {
                                if (e.Result.Succeeded)
                                {
                                    if (!fileToDeletePath.Any(x => x == e.SourceItem.FileSystemPath))
                                    {
                                        return;
                                    }
                                    deletedItems.Add(e.SourceItem.FileSystemPath);
                                    if (e.DestItem != null)
                                    {
                                        recycledItems.Add(e.DestItem.FileSystemPath);
                                    }
                                }
                            };
                            op.FinishOperations += (s, e) => deleteTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += async(s, e) => await Win32API.SendMessageAsync(connection, new ValueSet()
                            {
                                { "Progress", e.ProgressPercentage },
                                { "OperationID", operationID }
                            });
                            op.UpdateProgress += (s, e) =>
                            {
                                if (handleTable.GetValue <bool>(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                deleteTcs.TrySetResult(false);
                            }

                            handleTable.RemoveValue(operationID);

                            return(await deleteTcs.Task && deletedItems.Count == fileToDeletePath.Length, deletedItems, recycledItems);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", succcess },
                        { "DeletedItems", JsonConvert.SerializeObject(deletedItems) },
                        { "RecycledItems", JsonConvert.SerializeObject(recycledItems) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "RenameItem":
            {
                var fileToRenamePath  = (string)message["filepath"];
                var newName           = (string)message["newName"];
                var operationID       = (string)message["operationID"];
                var overwriteOnRename = (bool)message["overwrite"];
                var(succcess, renamedItems) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            List <string> renamedItems = new List <string>();

                            op.Options = ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI
                                         | ShellFileOperations.OperationFlags.EarlyFailure;
                            op.Options |= !overwriteOnRename ? ShellFileOperations.OperationFlags.RenameOnCollision : 0;

                            using var shi = new ShellItem(fileToRenamePath);
                            op.QueueRenameOperation(shi, newName);

                            handleTable.SetValue(operationID, false);
                            var renameTcs      = new TaskCompletionSource <bool>();
                            op.PostRenameItem += (s, e) =>
                            {
                                if (e.Result.Succeeded)
                                {
                                    renamedItems.Add($"{Path.Combine(Path.GetDirectoryName(e.SourceItem.FileSystemPath), e.Name)}");
                                }
                            };
                            op.FinishOperations += (s, e) => renameTcs.TrySetResult(e.Result.Succeeded);

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                renameTcs.TrySetResult(false);
                            }

                            handleTable.RemoveValue(operationID);

                            return(await renameTcs.Task && renamedItems.Count == 1, renamedItems);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", succcess },
                        { "RenamedItems", JsonConvert.SerializeObject(renamedItems) },
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "MoveItem":
            {
                var fileToMovePath  = ((string)message["filepath"]).Split('|');
                var moveDestination = ((string)message["destpath"]).Split('|');
                var operationID     = (string)message["operationID"];
                var overwriteOnMove = (bool)message["overwrite"];
                var(succcess, movedItems, movedSources) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            List <string> movedItems   = new List <string>();
                            List <string> movedSources = new List <string>();

                            op.Options = ShellFileOperations.OperationFlags.NoConfirmMkDir
                                         | ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI
                                         | ShellFileOperations.OperationFlags.EarlyFailure;

                            op.Options |= !overwriteOnMove ? ShellFileOperations.OperationFlags.PreserveFileExtensions | ShellFileOperations.OperationFlags.RenameOnCollision
                                    : ShellFileOperations.OperationFlags.NoConfirmation;

                            for (var i = 0; i < fileToMovePath.Length; i++)
                            {
                                using (ShellItem shi = new ShellItem(fileToMovePath[i]))
                                    using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(moveDestination[i])))
                                    {
                                        op.QueueMoveOperation(shi, shd, Path.GetFileName(moveDestination[i]));
                                    }
                            }

                            handleTable.SetValue(operationID, false);
                            var moveTcs      = new TaskCompletionSource <bool>();
                            op.PostMoveItem += (s, e) =>
                            {
                                if (e.Result.Succeeded)
                                {
                                    if (!fileToMovePath.Any(x => x == e.SourceItem.FileSystemPath))
                                    {
                                        return;
                                    }
                                    if (e.DestFolder != null && !string.IsNullOrEmpty(e.Name))
                                    {
                                        movedItems.Add($"{Path.Combine(e.DestFolder.FileSystemPath, e.Name)}");
                                        movedSources.Add(e.SourceItem.FileSystemPath);
                                    }
                                }
                            };
                            op.FinishOperations += (s, e) => moveTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += async(s, e) => await Win32API.SendMessageAsync(connection, new ValueSet()
                            {
                                { "Progress", e.ProgressPercentage },
                                { "OperationID", operationID }
                            });
                            op.UpdateProgress += (s, e) =>
                            {
                                if (handleTable.GetValue <bool>(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                moveTcs.TrySetResult(false);
                            }

                            handleTable.RemoveValue(operationID);

                            return(await moveTcs.Task && movedItems.Count == fileToMovePath.Length, movedItems, movedSources);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", succcess },
                        { "MovedItems", JsonConvert.SerializeObject(movedItems) },
                        { "MovedSources", JsonConvert.SerializeObject(movedSources) },
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "CopyItem":
            {
                var fileToCopyPath  = ((string)message["filepath"]).Split('|');
                var copyDestination = ((string)message["destpath"]).Split('|');
                var operationID     = (string)message["operationID"];
                var overwriteOnCopy = (bool)message["overwrite"];
                var(succcess, copiedItems, copiedSources) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            List <string> copiedItems   = new List <string>();
                            List <string> copiedSources = new List <string>();

                            op.Options = ShellFileOperations.OperationFlags.NoConfirmMkDir
                                         | ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI
                                         | ShellFileOperations.OperationFlags.EarlyFailure;

                            op.Options |= !overwriteOnCopy ? ShellFileOperations.OperationFlags.PreserveFileExtensions | ShellFileOperations.OperationFlags.RenameOnCollision
                                    : ShellFileOperations.OperationFlags.NoConfirmation;

                            for (var i = 0; i < fileToCopyPath.Length; i++)
                            {
                                using (ShellItem shi = new ShellItem(fileToCopyPath[i]))
                                    using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(copyDestination[i])))
                                    {
                                        op.QueueCopyOperation(shi, shd, Path.GetFileName(copyDestination[i]));
                                    }
                            }

                            handleTable.SetValue(operationID, false);
                            var copyTcs      = new TaskCompletionSource <bool>();
                            op.PostCopyItem += (s, e) =>
                            {
                                if (e.Result.Succeeded)
                                {
                                    if (!fileToCopyPath.Any(x => x == e.SourceItem.FileSystemPath))
                                    {
                                        return;
                                    }
                                    if (e.DestFolder != null && !string.IsNullOrEmpty(e.Name))
                                    {
                                        copiedItems.Add($"{Path.Combine(e.DestFolder.FileSystemPath, e.Name)}");
                                        copiedSources.Add(e.SourceItem.FileSystemPath);
                                    }
                                }
                            };
                            op.FinishOperations += (s, e) => copyTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += async(s, e) => await Win32API.SendMessageAsync(connection, new ValueSet()
                            {
                                { "Progress", e.ProgressPercentage },
                                { "OperationID", operationID }
                            });
                            op.UpdateProgress += (s, e) =>
                            {
                                if (handleTable.GetValue <bool>(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                copyTcs.TrySetResult(false);
                            }

                            handleTable.RemoveValue(operationID);

                            return(await copyTcs.Task && copiedItems.Count == fileToCopyPath.Length, copiedItems, copiedSources);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", succcess },
                        { "CopiedItems", JsonConvert.SerializeObject(copiedItems) },
                        { "CopiedSources", JsonConvert.SerializeObject(copiedSources) },
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "CancelOperation":
            {
                var operationID = (string)message["operationID"];
                handleTable.SetValue(operationID, true);
            }
            break;

            case "ParseLink":
                var linkPath = (string)message["filepath"];
                try
                {
                    if (linkPath.EndsWith(".lnk"))
                    {
                        using var link = new ShellLink(linkPath, LinkResolution.NoUIWithMsgPump, null, TimeSpan.FromMilliseconds(100));
                        await Win32API.SendMessageAsync(connection, new ValueSet()
                        {
                            { "TargetPath", link.TargetPath },
                            { "Arguments", link.Arguments },
                            { "WorkingDirectory", link.WorkingDirectory },
                            { "RunAsAdmin", link.RunAsAdministrator },
                            { "IsFolder", !string.IsNullOrEmpty(link.TargetPath) && link.Target.IsFolder }
                        }, message.Get("RequestID", (string)null));
                    }
                    else if (linkPath.EndsWith(".url"))
                    {
                        var linkUrl = await Win32API.StartSTATask(() =>
                        {
                            var ipf = new Url.IUniformResourceLocator();
                            (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Load(linkPath, 0);
                            ipf.GetUrl(out var retVal);
                            return(retVal);
                        });

                        await Win32API.SendMessageAsync(connection, new ValueSet()
                        {
                            { "TargetPath", linkUrl },
                            { "Arguments", null },
                            { "WorkingDirectory", null },
                            { "RunAsAdmin", false },
                            { "IsFolder", false }
                        }, message.Get("RequestID", (string)null));
                    }
                }
                catch (Exception ex)
                {
                    // Could not parse shortcut
                    Program.Logger.Warn(ex, ex.Message);
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "TargetPath", null },
                        { "Arguments", null },
                        { "WorkingDirectory", null },
                        { "RunAsAdmin", false },
                        { "IsFolder", false }
                    }, message.Get("RequestID", (string)null));
                }
                break;

            case "CreateLink":
            case "UpdateLink":
                var linkSavePath = (string)message["filepath"];
                var targetPath   = (string)message["targetpath"];
                if (linkSavePath.EndsWith(".lnk"))
                {
                    var arguments        = (string)message["arguments"];
                    var workingDirectory = (string)message["workingdir"];
                    var runAsAdmin       = (bool)message["runasadmin"];
                    using var newLink          = new ShellLink(targetPath, arguments, workingDirectory);
                    newLink.RunAsAdministrator = runAsAdmin;
                    newLink.SaveAs(linkSavePath);     // Overwrite if exists
                }
                else if (linkSavePath.EndsWith(".url"))
                {
                    await Win32API.StartSTATask(() =>
                    {
                        var ipf = new Url.IUniformResourceLocator();
                        ipf.SetUrl(targetPath, Url.IURL_SETURL_FLAGS.IURL_SETURL_FL_GUESS_PROTOCOL);
                        (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Save(linkSavePath, false);     // Overwrite if exists
                        return(true);
                    });
                }
                break;

            case "GetFilePermissions":
            {
                var filePathForPerm = (string)message["filepath"];
                var isFolder        = (bool)message["isfolder"];
                var filePermissions = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "FilePermissions", JsonConvert.SerializeObject(filePermissions) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetFilePermissions":
            {
                var filePermissionsString = (string)message["permissions"];
                var filePermissionsToSet  = JsonConvert.DeserializeObject <FilePermissions>(filePermissionsString);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", filePermissionsToSet.SetPermissions() }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetFileOwner":
            {
                var filePathForPerm = (string)message["filepath"];
                var isFolder        = (bool)message["isfolder"];
                var ownerSid        = (string)message["ownersid"];
                var fp = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", fp.SetOwner(ownerSid) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetAccessRuleProtection":
            {
                var filePathForPerm     = (string)message["filepath"];
                var isFolder            = (bool)message["isfolder"];
                var isProtected         = (bool)message["isprotected"];
                var preserveInheritance = (bool)message["preserveinheritance"];
                var fp = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", fp.SetAccessRuleProtection(isProtected, preserveInheritance) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "OpenObjectPicker":
                var hwnd         = (long)message["HWND"];
                var pickedObject = await FilePermissions.OpenObjectPicker(hwnd);

                await Win32API.SendMessageAsync(connection, new ValueSet()
                {
                    { "PickedObject", pickedObject }
                }, message.Get("RequestID", (string)null));

                break;
            }
        }
Exemplo n.º 13
0
        private static async Task parseFileOperation(AppServiceRequestReceivedEventArgs args)
        {
            var fileOp = (string)args.Request.Message["fileop"];

            switch (fileOp)
            {
            case "Clipboard":
                await Win32API.StartSTATask(() =>
                {
                    System.Windows.Forms.Clipboard.Clear();
                    var fileToCopy = (string)args.Request.Message["filepath"];
                    var operation  = (DataPackageOperation)(int)args.Request.Message["operation"];
                    var fileList   = new System.Collections.Specialized.StringCollection();
                    fileList.AddRange(fileToCopy.Split('|'));
                    if (operation == DataPackageOperation.Copy)
                    {
                        System.Windows.Forms.Clipboard.SetFileDropList(fileList);
                    }
                    else if (operation == DataPackageOperation.Move)
                    {
                        byte[] moveEffect       = new byte[] { 2, 0, 0, 0 };
                        MemoryStream dropEffect = new MemoryStream();
                        dropEffect.Write(moveEffect, 0, moveEffect.Length);
                        var data = new System.Windows.Forms.DataObject();
                        data.SetFileDropList(fileList);
                        data.SetData("Preferred DropEffect", dropEffect);
                        System.Windows.Forms.Clipboard.SetDataObject(data, true);
                    }
                    return(true);
                });

                break;

            case "MoveToBin":
                var fileToDeletePath = (string)args.Request.Message["filepath"];
                using (var op = new ShellFileOperations())
                {
                    op.Options    = ShellFileOperations.OperationFlags.AllowUndo | ShellFileOperations.OperationFlags.NoUI;
                    using var shi = new ShellItem(fileToDeletePath);
                    op.QueueDeleteOperation(shi);
                    op.PerformOperations();
                }
                //ShellFileOperations.Delete(fileToDeletePath, ShellFileOperations.OperationFlags.AllowUndo | ShellFileOperations.OperationFlags.NoUI);
                break;

            case "ParseLink":
                var linkPath = (string)args.Request.Message["filepath"];
                try
                {
                    if (linkPath.EndsWith(".lnk"))
                    {
                        using var link = new ShellLink(linkPath, LinkResolution.NoUIWithMsgPump, null, TimeSpan.FromMilliseconds(100));
                        await args.Request.SendResponseAsync(new ValueSet()
                        {
                            { "TargetPath", link.TargetPath },
                            { "Arguments", link.Arguments },
                            { "WorkingDirectory", link.WorkingDirectory },
                            { "RunAsAdmin", link.RunAsAdministrator },
                            { "IsFolder", !string.IsNullOrEmpty(link.TargetPath) && link.Target.IsFolder }
                        });
                    }
                    else if (linkPath.EndsWith(".url"))
                    {
                        var linkUrl = await Win32API.StartSTATask(() =>
                        {
                            var ipf = new Url.IUniformResourceLocator();
                            (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Load(linkPath, 0);
                            ipf.GetUrl(out var retVal);
                            return(retVal);
                        });

                        await args.Request.SendResponseAsync(new ValueSet()
                        {
                            { "TargetPath", linkUrl },
                            { "Arguments", null },
                            { "WorkingDirectory", null },
                            { "RunAsAdmin", false },
                            { "IsFolder", false }
                        });
                    }
                }
                catch (Exception ex)
                {
                    // Could not parse shortcut
                    Logger.Warn(ex, ex.Message);
                    await args.Request.SendResponseAsync(new ValueSet()
                    {
                        { "TargetPath", null },
                        { "Arguments", null },
                        { "WorkingDirectory", null },
                        { "RunAsAdmin", false },
                        { "IsFolder", false }
                    });
                }
                break;

            case "CreateLink":
            case "UpdateLink":
                var linkSavePath = (string)args.Request.Message["filepath"];
                var targetPath   = (string)args.Request.Message["targetpath"];
                if (linkSavePath.EndsWith(".lnk"))
                {
                    var arguments        = (string)args.Request.Message["arguments"];
                    var workingDirectory = (string)args.Request.Message["workingdir"];
                    var runAsAdmin       = (bool)args.Request.Message["runasadmin"];
                    using var newLink          = new ShellLink(targetPath, arguments, workingDirectory);
                    newLink.RunAsAdministrator = runAsAdmin;
                    newLink.SaveAs(linkSavePath);     // Overwrite if exists
                }
                else if (linkSavePath.EndsWith(".url"))
                {
                    await Win32API.StartSTATask(() =>
                    {
                        var ipf = new Url.IUniformResourceLocator();
                        ipf.SetUrl(targetPath, Url.IURL_SETURL_FLAGS.IURL_SETURL_FL_GUESS_PROTOCOL);
                        (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Save(linkSavePath, false);     // Overwrite if exists
                        return(true);
                    });
                }
                break;
            }
        }
Exemplo n.º 14
0
        private async Task ParseFileOperationAsync(PipeStream connection, Dictionary <string, object> message)
        {
            switch (message.Get("fileop", ""))
            {
            case "GetFileHandle":
            {
                var filePath  = (string)message["filepath"];
                var readWrite = (bool)message["readwrite"];
                using var hFile = Kernel32.CreateFile(filePath, Kernel32.FileAccess.GENERIC_READ | (readWrite ? Kernel32.FileAccess.GENERIC_WRITE : 0), FileShare.ReadWrite, null, FileMode.Open, FileFlagsAndAttributes.FILE_ATTRIBUTE_NORMAL);
                if (hFile.IsInvalid)
                {
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                        {
                            { "Success", false }
                        }, message.Get("RequestID", (string)null));

                    return;
                }
                var processId = (int)(long)message["processid"];
                using var uwpProces = System.Diagnostics.Process.GetProcessById(processId);
                if (!Kernel32.DuplicateHandle(Kernel32.GetCurrentProcess(), hFile.DangerousGetHandle(), uwpProces.Handle, out var targetHandle, 0, false, Kernel32.DUPLICATE_HANDLE_OPTIONS.DUPLICATE_SAME_ACCESS))
                {
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                        {
                            { "Success", false }
                        }, message.Get("RequestID", (string)null));

                    return;
                }
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", true },
                        { "Handle", targetHandle.ToInt64() }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "Clipboard":
                await Win32API.StartSTATask(() =>
                {
                    System.Windows.Forms.Clipboard.Clear();
                    var fileToCopy = (string)message["filepath"];
                    var operation  = (DataPackageOperation)(long)message["operation"];
                    var fileList   = new System.Collections.Specialized.StringCollection();
                    fileList.AddRange(fileToCopy.Split('|'));
                    if (operation == DataPackageOperation.Copy)
                    {
                        System.Windows.Forms.Clipboard.SetFileDropList(fileList);
                    }
                    else if (operation == DataPackageOperation.Move)
                    {
                        byte[] moveEffect       = new byte[] { 2, 0, 0, 0 };
                        MemoryStream dropEffect = new MemoryStream();
                        dropEffect.Write(moveEffect, 0, moveEffect.Length);
                        var data = new System.Windows.Forms.DataObject();
                        data.SetFileDropList(fileList);
                        data.SetData("Preferred DropEffect", dropEffect);
                        System.Windows.Forms.Clipboard.SetDataObject(data, true);
                    }
                    return(true);
                });

                break;

            case "DragDrop":
                var dropPath = (string)message["droppath"];
                var result   = await Win32API.StartSTATask(() =>
                {
                    var rdo = new RemoteDataObject(System.Windows.Forms.Clipboard.GetDataObject());

                    foreach (RemoteDataObject.DataPackage package in rdo.GetRemoteData())
                    {
                        try
                        {
                            if (package.ItemType == RemoteDataObject.StorageType.File)
                            {
                                string directoryPath = Path.GetDirectoryName(dropPath);
                                if (!Directory.Exists(directoryPath))
                                {
                                    Directory.CreateDirectory(directoryPath);
                                }

                                string uniqueName = Win32API.GenerateUniquePath(Path.Combine(dropPath, package.Name));
                                using (FileStream stream = new FileStream(uniqueName, FileMode.CreateNew))
                                {
                                    package.ContentStream.CopyTo(stream);
                                }
                            }
                            else
                            {
                                string directoryPath = Path.Combine(dropPath, package.Name);
                                if (!Directory.Exists(directoryPath))
                                {
                                    Directory.CreateDirectory(directoryPath);
                                }
                            }
                        }
                        finally
                        {
                            package.Dispose();
                        }
                    }
                    return(true);
                });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                {
                    { "Success", result }
                }, message.Get("RequestID", (string)null));

                break;

            case "CreateFile":
            case "CreateFolder":
            {
                var filePath = (string)message["filepath"];
                var template = message.Get("template", (string)null);
                var dataStr  = message.Get("data", (string)null);
                var(success, shellOperationResult) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            op.Options = ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoConfirmMkDir
                                         | ShellFileOperations.OperationFlags.RenameOnCollision
                                         | ShellFileOperations.OperationFlags.NoErrorUI;

                            var shellOperationResult = new ShellOperationResult();

                            using var shd = new ShellFolder(Path.GetDirectoryName(filePath));
                            op.QueueNewItemOperation(shd, Path.GetFileName(filePath),
                                                     (string)message["fileop"] == "CreateFolder" ? FileAttributes.Directory : FileAttributes.Normal, template);

                            var createTcs   = new TaskCompletionSource <bool>();
                            op.PostNewItem += (s, e) =>
                            {
                                shellOperationResult.Items.Add(new ShellOperationItemResult()
                                {
                                    Succeeded   = e.Result.Succeeded,
                                    Destination = e.DestItem?.FileSystemPath,
                                    HRresult    = (int)e.Result
                                });
                            };
                            op.FinishOperations += (s, e) => createTcs.TrySetResult(e.Result.Succeeded);

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                createTcs.TrySetResult(false);
                            }

                            if (dataStr != null && (shellOperationResult.Items.SingleOrDefault()?.Succeeded ?? false))
                            {
                                Extensions.IgnoreExceptions(() =>
                                {
                                    var dataBytes = Convert.FromBase64String(dataStr);
                                    using (var fs = new FileStream(shellOperationResult.Items.Single().Destination, FileMode.Open))
                                    {
                                        fs.Write(dataBytes, 0, dataBytes.Length);
                                        fs.Flush();
                                    }
                                }, Program.Logger);
                            }

                            return(await createTcs.Task, shellOperationResult);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", success },
                        { "Result", JsonConvert.SerializeObject(shellOperationResult) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "DeleteItem":
            {
                var fileToDeletePath = ((string)message["filepath"]).Split('|');
                var permanently      = (bool)message["permanently"];
                var operationID      = (string)message["operationID"];
                var ownerHwnd        = (long)message["HWND"];
                var(success, shellOperationResult) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            op.Options = ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoConfirmation
                                         | ShellFileOperations.OperationFlags.NoErrorUI;
                            op.OwnerWindow = Win32API.Win32Window.FromLong(ownerHwnd);
                            if (!permanently)
                            {
                                op.Options |= ShellFileOperations.OperationFlags.RecycleOnDelete
                                              | ShellFileOperations.OperationFlags.WantNukeWarning;
                            }

                            var shellOperationResult = new ShellOperationResult();

                            for (var i = 0; i < fileToDeletePath.Length; i++)
                            {
                                using var shi = new ShellItem(fileToDeletePath[i]);
                                op.QueueDeleteOperation(shi);
                            }

                            progressHandler.OwnerWindow = op.OwnerWindow;
                            progressHandler.AddOperation(operationID);

                            var deleteTcs     = new TaskCompletionSource <bool>();
                            op.PreDeleteItem += (s, e) =>
                            {
                                if (!permanently && !e.Flags.HasFlag(ShellFileOperations.TransferFlags.DeleteRecycleIfPossible))
                                {
                                    throw new Win32Exception(HRESULT.COPYENGINE_E_RECYCLE_BIN_NOT_FOUND);     // E_FAIL, stops operation
                                }
                            };
                            op.PostDeleteItem += (s, e) =>
                            {
                                shellOperationResult.Items.Add(new ShellOperationItemResult()
                                {
                                    Succeeded   = e.Result.Succeeded,
                                    Source      = e.SourceItem.FileSystemPath ?? e.SourceItem.ParsingName,
                                    Destination = e.DestItem?.FileSystemPath,
                                    HRresult    = (int)e.Result
                                });
                            };
                            op.PostDeleteItem   += (s, e) => UpdateFileTageDb(s, e, "delete");
                            op.FinishOperations += (s, e) => deleteTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += (s, e) =>
                            {
                                if (progressHandler.CheckCanceled(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                                progressHandler.UpdateOperation(operationID, e.ProgressPercentage);
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                deleteTcs.TrySetResult(false);
                            }

                            progressHandler.RemoveOperation(operationID);

                            return(await deleteTcs.Task, shellOperationResult);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", success },
                        { "Result", JsonConvert.SerializeObject(shellOperationResult) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "RenameItem":
            {
                var fileToRenamePath  = (string)message["filepath"];
                var newName           = (string)message["newName"];
                var operationID       = (string)message["operationID"];
                var overwriteOnRename = (bool)message["overwrite"];
                var(success, shellOperationResult) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            var shellOperationResult = new ShellOperationResult();

                            op.Options = ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI;
                            op.Options |= !overwriteOnRename ? ShellFileOperations.OperationFlags.RenameOnCollision : 0;

                            using var shi = new ShellItem(fileToRenamePath);
                            op.QueueRenameOperation(shi, newName);

                            progressHandler.OwnerWindow = op.OwnerWindow;
                            progressHandler.AddOperation(operationID);

                            var renameTcs      = new TaskCompletionSource <bool>();
                            op.PostRenameItem += (s, e) =>
                            {
                                shellOperationResult.Items.Add(new ShellOperationItemResult()
                                {
                                    Succeeded   = e.Result.Succeeded,
                                    Source      = e.SourceItem.FileSystemPath ?? e.SourceItem.ParsingName,
                                    Destination = !string.IsNullOrEmpty(e.Name) ? Path.Combine(Path.GetDirectoryName(e.SourceItem.FileSystemPath), e.Name) : null,
                                    HRresult    = (int)e.Result
                                });
                            };
                            op.PostRenameItem   += (s, e) => UpdateFileTageDb(s, e, "rename");
                            op.FinishOperations += (s, e) => renameTcs.TrySetResult(e.Result.Succeeded);

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                renameTcs.TrySetResult(false);
                            }

                            progressHandler.RemoveOperation(operationID);

                            return(await renameTcs.Task, shellOperationResult);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", success },
                        { "Result", JsonConvert.SerializeObject(shellOperationResult) },
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "MoveItem":
            {
                var fileToMovePath  = ((string)message["filepath"]).Split('|');
                var moveDestination = ((string)message["destpath"]).Split('|');
                var operationID     = (string)message["operationID"];
                var overwriteOnMove = (bool)message["overwrite"];
                var ownerHwnd       = (long)message["HWND"];
                var(success, shellOperationResult) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            var shellOperationResult = new ShellOperationResult();

                            op.Options = ShellFileOperations.OperationFlags.NoConfirmMkDir
                                         | ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI;
                            op.OwnerWindow = Win32API.Win32Window.FromLong(ownerHwnd);
                            op.Options    |= !overwriteOnMove ? ShellFileOperations.OperationFlags.PreserveFileExtensions | ShellFileOperations.OperationFlags.RenameOnCollision
                                    : ShellFileOperations.OperationFlags.NoConfirmation;

                            for (var i = 0; i < fileToMovePath.Length; i++)
                            {
                                using (ShellItem shi = new ShellItem(fileToMovePath[i]))
                                    using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(moveDestination[i])))
                                    {
                                        op.QueueMoveOperation(shi, shd, Path.GetFileName(moveDestination[i]));
                                    }
                            }

                            progressHandler.OwnerWindow = op.OwnerWindow;
                            progressHandler.AddOperation(operationID);

                            var moveTcs      = new TaskCompletionSource <bool>();
                            op.PostMoveItem += (s, e) =>
                            {
                                shellOperationResult.Items.Add(new ShellOperationItemResult()
                                {
                                    Succeeded   = e.Result.Succeeded,
                                    Source      = e.SourceItem.FileSystemPath ?? e.SourceItem.ParsingName,
                                    Destination = e.DestFolder?.FileSystemPath != null && !string.IsNullOrEmpty(e.Name) ? Path.Combine(e.DestFolder.FileSystemPath, e.Name) : null,
                                    HRresult    = (int)e.Result
                                });
                            };
                            op.PostMoveItem     += (s, e) => UpdateFileTageDb(s, e, "move");
                            op.FinishOperations += (s, e) => moveTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += (s, e) =>
                            {
                                if (progressHandler.CheckCanceled(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                                progressHandler.UpdateOperation(operationID, e.ProgressPercentage);
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                moveTcs.TrySetResult(false);
                            }

                            progressHandler.RemoveOperation(operationID);

                            return(await moveTcs.Task, shellOperationResult);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", success },
                        { "Result", JsonConvert.SerializeObject(shellOperationResult) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "CopyItem":
            {
                var fileToCopyPath  = ((string)message["filepath"]).Split('|');
                var copyDestination = ((string)message["destpath"]).Split('|');
                var operationID     = (string)message["operationID"];
                var overwriteOnCopy = (bool)message["overwrite"];
                var ownerHwnd       = (long)message["HWND"];
                var(success, shellOperationResult) = await Win32API.StartSTATask(async() =>
                    {
                        using (var op = new ShellFileOperations())
                        {
                            var shellOperationResult = new ShellOperationResult();

                            op.Options = ShellFileOperations.OperationFlags.NoConfirmMkDir
                                         | ShellFileOperations.OperationFlags.Silent
                                         | ShellFileOperations.OperationFlags.NoErrorUI;
                            op.OwnerWindow = Win32API.Win32Window.FromLong(ownerHwnd);
                            op.Options    |= !overwriteOnCopy ? ShellFileOperations.OperationFlags.PreserveFileExtensions | ShellFileOperations.OperationFlags.RenameOnCollision
                                    : ShellFileOperations.OperationFlags.NoConfirmation;

                            for (var i = 0; i < fileToCopyPath.Length; i++)
                            {
                                using (ShellItem shi = new ShellItem(fileToCopyPath[i]))
                                    using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(copyDestination[i])))
                                    {
                                        op.QueueCopyOperation(shi, shd, Path.GetFileName(copyDestination[i]));
                                    }
                            }

                            progressHandler.OwnerWindow = op.OwnerWindow;
                            progressHandler.AddOperation(operationID);

                            var copyTcs      = new TaskCompletionSource <bool>();
                            op.PostCopyItem += (s, e) =>
                            {
                                shellOperationResult.Items.Add(new ShellOperationItemResult()
                                {
                                    Succeeded   = e.Result.Succeeded,
                                    Source      = e.SourceItem.FileSystemPath ?? e.SourceItem.ParsingName,
                                    Destination = e.DestFolder?.FileSystemPath != null && !string.IsNullOrEmpty(e.Name) ? Path.Combine(e.DestFolder.FileSystemPath, e.Name) : null,
                                    HRresult    = (int)e.Result
                                });
                            };
                            op.PostCopyItem     += (s, e) => UpdateFileTageDb(s, e, "copy");
                            op.FinishOperations += (s, e) => copyTcs.TrySetResult(e.Result.Succeeded);
                            op.UpdateProgress   += (s, e) =>
                            {
                                if (progressHandler.CheckCanceled(operationID))
                                {
                                    throw new Win32Exception(unchecked ((int)0x80004005));    // E_FAIL, stops operation
                                }
                                progressHandler.UpdateOperation(operationID, e.ProgressPercentage);
                            };

                            try
                            {
                                op.PerformOperations();
                            }
                            catch
                            {
                                copyTcs.TrySetResult(false);
                            }

                            progressHandler.RemoveOperation(operationID);

                            return(await copyTcs.Task, shellOperationResult);
                        }
                    });

                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", success },
                        { "Result", JsonConvert.SerializeObject(shellOperationResult) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "CancelOperation":
            {
                var operationID = (string)message["operationID"];
                progressHandler.TryCancel(operationID);
            }
            break;

            case "ParseLink":
                try
                {
                    var linkPath = (string)message["filepath"];
                    if (linkPath.EndsWith(".lnk"))
                    {
                        using var link = new ShellLink(linkPath, LinkResolution.NoUIWithMsgPump, null, TimeSpan.FromMilliseconds(100));
                        await Win32API.SendMessageAsync(connection, new ValueSet()
                        {
                            { "TargetPath", link.TargetPath },
                            { "Arguments", link.Arguments },
                            { "WorkingDirectory", link.WorkingDirectory },
                            { "RunAsAdmin", link.RunAsAdministrator },
                            { "IsFolder", !string.IsNullOrEmpty(link.TargetPath) && link.Target.IsFolder }
                        }, message.Get("RequestID", (string)null));
                    }
                    else if (linkPath.EndsWith(".url"))
                    {
                        var linkUrl = await Win32API.StartSTATask(() =>
                        {
                            var ipf = new Url.IUniformResourceLocator();
                            (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Load(linkPath, 0);
                            ipf.GetUrl(out var retVal);
                            return(retVal);
                        });

                        await Win32API.SendMessageAsync(connection, new ValueSet()
                        {
                            { "TargetPath", linkUrl },
                            { "Arguments", null },
                            { "WorkingDirectory", null },
                            { "RunAsAdmin", false },
                            { "IsFolder", false }
                        }, message.Get("RequestID", (string)null));
                    }
                }
                catch (Exception ex)
                {
                    // Could not parse shortcut
                    Program.Logger.Warn(ex, ex.Message);
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "TargetPath", null },
                        { "Arguments", null },
                        { "WorkingDirectory", null },
                        { "RunAsAdmin", false },
                        { "IsFolder", false }
                    }, message.Get("RequestID", (string)null));
                }
                break;

            case "CreateLink":
            case "UpdateLink":
                try
                {
                    var linkSavePath = (string)message["filepath"];
                    var targetPath   = (string)message["targetpath"];

                    bool success = false;
                    if (linkSavePath.EndsWith(".lnk"))
                    {
                        var arguments        = (string)message["arguments"];
                        var workingDirectory = (string)message["workingdir"];
                        var runAsAdmin       = (bool)message["runasadmin"];
                        using var newLink          = new ShellLink(targetPath, arguments, workingDirectory);
                        newLink.RunAsAdministrator = runAsAdmin;
                        newLink.SaveAs(linkSavePath);     // Overwrite if exists
                        success = true;
                    }
                    else if (linkSavePath.EndsWith(".url"))
                    {
                        success = await Win32API.StartSTATask(() =>
                        {
                            var ipf = new Url.IUniformResourceLocator();
                            ipf.SetUrl(targetPath, Url.IURL_SETURL_FLAGS.IURL_SETURL_FL_GUESS_PROTOCOL);
                            (ipf as System.Runtime.InteropServices.ComTypes.IPersistFile).Save(linkSavePath, false);     // Overwrite if exists
                            return(true);
                        });
                    }
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", success }
                    }, message.Get("RequestID", (string)null));
                }
                catch (Exception ex)
                {
                    // Could not create shortcut
                    Program.Logger.Warn(ex, ex.Message);
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", false }
                    }, message.Get("RequestID", (string)null));
                }
                break;

            case "SetLinkIcon":
                try
                {
                    var linkPath = (string)message["filepath"];
                    using var link    = new ShellLink(linkPath, LinkResolution.NoUIWithMsgPump, null, TimeSpan.FromMilliseconds(100));
                    link.IconLocation = new IconLocation((string)message["iconFile"], (int)message.Get("iconIndex", 0L));
                    link.SaveAs(linkPath);     // Overwrite if exists
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", true }
                    }, message.Get("RequestID", (string)null));
                }
                catch (Exception ex)
                {
                    // Could not create shortcut
                    Program.Logger.Warn(ex, ex.Message);
                    await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", false }
                    }, message.Get("RequestID", (string)null));
                }
                break;

            case "GetFilePermissions":
            {
                var filePathForPerm = (string)message["filepath"];
                var isFolder        = (bool)message["isfolder"];
                var filePermissions = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "FilePermissions", JsonConvert.SerializeObject(filePermissions) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetFilePermissions":
            {
                var filePermissionsString = (string)message["permissions"];
                var filePermissionsToSet  = JsonConvert.DeserializeObject <FilePermissions>(filePermissionsString);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", filePermissionsToSet.SetPermissions() }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetFileOwner":
            {
                var filePathForPerm = (string)message["filepath"];
                var isFolder        = (bool)message["isfolder"];
                var ownerSid        = (string)message["ownersid"];
                var fp = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", fp.SetOwner(ownerSid) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetAccessRuleProtection":
            {
                var filePathForPerm     = (string)message["filepath"];
                var isFolder            = (bool)message["isfolder"];
                var isProtected         = (bool)message["isprotected"];
                var preserveInheritance = (bool)message["preserveinheritance"];
                var fp = FilePermissions.FromFilePath(filePathForPerm, isFolder);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "Success", fp.SetAccessRuleProtection(isProtected, preserveInheritance) }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "OpenObjectPicker":
                var hwnd         = (long)message["HWND"];
                var pickedObject = await FilePermissions.OpenObjectPicker(hwnd);

                await Win32API.SendMessageAsync(connection, new ValueSet()
                {
                    { "PickedObject", pickedObject }
                }, message.Get("RequestID", (string)null));

                break;

            case "ReadCompatOptions":
            {
                var filePath      = (string)message["filepath"];
                var compatOptions = Extensions.IgnoreExceptions(() =>
                    {
                        using var compatKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers");
                        if (compatKey == null)
                        {
                            return(null);
                        }
                        return((string)compatKey.GetValue(filePath, null));
                    }, Program.Logger);
                await Win32API.SendMessageAsync(connection, new ValueSet()
                    {
                        { "CompatOptions", compatOptions }
                    }, message.Get("RequestID", (string)null));
            }
            break;

            case "SetCompatOptions":
            {
                var  filePath      = (string)message["filepath"];
                var  compatOptions = (string)message["options"];
                bool success       = false;
                if (string.IsNullOrEmpty(compatOptions) || compatOptions == "~")
                {
                    success = Win32API.RunPowershellCommand(@$ "Remove-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers' -Name '{filePath}' | Out-Null", false);
                }
                else
                {
                    success = Win32API.RunPowershellCommand(@$ "New-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers' -Name '{filePath}' -Value '{compatOptions}' -PropertyType String -Force | Out-Null", false);