diff --git a/CommunicateService/Service.cs b/CommunicateService/Service.cs index 1b9df72c..b7cd9767 100644 --- a/CommunicateService/Service.cs +++ b/CommunicateService/Service.cs @@ -10,7 +10,7 @@ namespace CommunicateService public sealed class Service : IBackgroundTask { private BackgroundTaskDeferral Deferral; - private static readonly List ClientConnections = new List(); + private static readonly Dictionary ClientConnections = new Dictionary(); private static AppServiceConnection ServerConnection; private static readonly object Locker = new object(); @@ -49,7 +49,8 @@ public async void Run(IBackgroundTaskInstance taskInstance) { lock (Locker) { - ClientConnections.Add(IncomeConnection); + string Guid = Convert.ToString(Response.Message["Guid"]); + ClientConnections.Add(IncomeConnection, Guid); } break; @@ -107,11 +108,13 @@ private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTas lock (Locker) { AppServiceConnection DisConnection = (sender.TriggerDetails as AppServiceTriggerDetails).AppServiceConnection; - + DisConnection.RequestReceived -= Connection_RequestReceived; - if (ClientConnections.Contains(DisConnection)) + if (ClientConnections.ContainsKey(DisConnection)) { + ServerConnection.SendMessageAsync(new ValueSet { { "ExcuteType", "Excute_RequestClosePipe" }, { "Guid", ClientConnections[DisConnection] } }).AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); + ClientConnections.Remove(DisConnection); DisConnection.Dispose(); diff --git a/FullTrustProcess/FullTrustProcess.csproj b/FullTrustProcess/FullTrustProcess.csproj index fb8033e7..82321d69 100644 --- a/FullTrustProcess/FullTrustProcess.csproj +++ b/FullTrustProcess/FullTrustProcess.csproj @@ -116,7 +116,7 @@ 4.7.1 - 3.2.11 + 3.2.12 diff --git a/FullTrustProcess/Program.cs b/FullTrustProcess/Program.cs index be28ae33..707f244c 100644 --- a/FullTrustProcess/Program.cs +++ b/FullTrustProcess/Program.cs @@ -3,13 +3,15 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.IO.Pipes; using System.Linq; -using System.Reflection; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Text; using System.Threading; using System.Threading.Tasks; using Windows.ApplicationModel.AppService; using Windows.Foundation.Collections; -using Windows.System; namespace FullTrustProcess { @@ -17,6 +19,8 @@ class Program { private static AppServiceConnection Connection; + private static readonly Dictionary PipeServers = new Dictionary(); + private static readonly HashSet SpecialStringMap = new HashSet() { Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "WindowsPowerShell\\v1.0\\powershell.exe"), @@ -26,6 +30,8 @@ class Program private readonly static ManualResetEvent ExitLocker = new ManualResetEvent(false); + private static readonly object Locker = new object(); + [STAThread] static async Task Main(string[] args) { @@ -33,7 +39,7 @@ static async Task Main(string[] args) { using (Mutex LaunchLocker = new Mutex(true, "RX_Explorer_FullTrustProcess", out bool IsNotExist)) { - if(!IsNotExist) + if (!IsNotExist) { return; } @@ -53,7 +59,7 @@ static async Task Main(string[] args) ExitLocker.WaitOne(); } } - catch(Exception e) + catch (Exception e) { Debug.WriteLine($"FullTrustProcess出现异常,错误信息{e.Message}"); } @@ -62,18 +68,62 @@ static async Task Main(string[] args) Connection?.Dispose(); ExitLocker?.Dispose(); + PipeServers.Values.ToList().ForEach((Item) => + { + Item.Disconnect(); + Item.Dispose(); + }); + + PipeServers.Clear(); + Environment.Exit(0); } } + private static void InitializeNewNamedPipe(string ID) + { + NamedPipeServerStream NewPipeServer = new NamedPipeServerStream($@"Explorer_And_FullTrustProcess_NamedPipe-{ID}", PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.None, 2048, 2048, null, HandleInheritability.None, PipeAccessRights.ChangePermissions); + PipeSecurity Security = NewPipeServer.GetAccessControl(); + PipeAccessRule ClientRule = new PipeAccessRule(new SecurityIdentifier("S-1-15-2-1"), PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow); + PipeAccessRule OwnerRule = new PipeAccessRule(WindowsIdentity.GetCurrent().Owner, PipeAccessRights.FullControl, AccessControlType.Allow); + Security.AddAccessRule(ClientRule); + Security.AddAccessRule(OwnerRule); + NewPipeServer.SetAccessControl(Security); + + PipeServers.Add(ID, NewPipeServer); + } + private async static void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { - var Deferral = args.GetDeferral(); + AppServiceDeferral Deferral = args.GetDeferral(); try { switch (args.Request.Message["ExcuteType"]) { + case "Excute_RequestClosePipe": + { + string Guid = Convert.ToString(args.Request.Message["Guid"]); + + if (PipeServers.ContainsKey(Guid)) + { + PipeServers[Guid].Disconnect(); + PipeServers[Guid].Dispose(); + PipeServers.Remove(Guid); + } + break; + } + case "Excute_RequestCreateNewPipe": + { + string Guid = Convert.ToString(args.Request.Message["Guid"]); + + if (!PipeServers.ContainsKey(Guid)) + { + InitializeNewNamedPipe(Guid); + } + + break; + } case "Identity": { await args.Request.SendResponseAsync(new ValueSet { { "Identity", "FullTrustProcess" } }); @@ -226,12 +276,29 @@ private async static void Connection_RequestReceived(AppServiceConnection sender string SourcePathJson = Convert.ToString(args.Request.Message["SourcePath"]); string DestinationPath = Convert.ToString(args.Request.Message["DestinationPath"]); + string Guid = Convert.ToString(args.Request.Message["Guid"]); List> SourcePathList = JsonConvert.DeserializeObject>>(SourcePathJson); if (SourcePathList.All((Item) => Directory.Exists(Item.Key) || File.Exists(Item.Key))) { - if (StorageItemController.Copy(SourcePathList, DestinationPath)) + if (StorageItemController.Copy(SourcePathList, DestinationPath, (s, e) => + { + lock (Locker) + { + NamedPipeServerStream Server = PipeServers[Guid]; + + if (!Server.IsConnected) + { + Server.WaitForConnection(); + } + + using (StreamWriter Writer = new StreamWriter(Server, new UTF8Encoding(false), 1024, true)) + { + Writer.WriteLine(e.ProgressPercentage); + } + } + })) { Value.Add("Success", string.Empty); } @@ -245,6 +312,24 @@ private async static void Connection_RequestReceived(AppServiceConnection sender Value.Add("Error_NotFound", "SourcePath is not a file or directory"); } + if (!Value.ContainsKey("Success")) + { + lock (Locker) + { + NamedPipeServerStream Server = PipeServers[Guid]; + + if (!Server.IsConnected) + { + Server.WaitForConnection(); + } + + using (StreamWriter Writer = new StreamWriter(Server, new UTF8Encoding(false), 1024, true)) + { + Writer.WriteLine("Error_Stop_Signal"); + } + } + } + await args.Request.SendResponseAsync(Value); break; } @@ -254,6 +339,7 @@ private async static void Connection_RequestReceived(AppServiceConnection sender string SourcePathJson = Convert.ToString(args.Request.Message["SourcePath"]); string DestinationPath = Convert.ToString(args.Request.Message["DestinationPath"]); + string Guid = Convert.ToString(args.Request.Message["Guid"]); List> SourcePathList = JsonConvert.DeserializeObject>>(SourcePathJson); @@ -265,7 +351,23 @@ private async static void Connection_RequestReceived(AppServiceConnection sender } else { - if (StorageItemController.Move(SourcePathList, DestinationPath)) + if (StorageItemController.Move(SourcePathList, DestinationPath, (s, e) => + { + lock (Locker) + { + NamedPipeServerStream Server = PipeServers[Guid]; + + if (!Server.IsConnected) + { + Server.WaitForConnection(); + } + + using (StreamWriter Writer = new StreamWriter(Server, new UTF8Encoding(false), 1024, true)) + { + Writer.WriteLine(e.ProgressPercentage); + } + } + })) { Value.Add("Success", string.Empty); } @@ -280,6 +382,24 @@ private async static void Connection_RequestReceived(AppServiceConnection sender Value.Add("Error_NotFound", "SourcePath is not a file or directory"); } + if(!Value.ContainsKey("Success")) + { + lock (Locker) + { + NamedPipeServerStream Server = PipeServers[Guid]; + + if (!Server.IsConnected) + { + Server.WaitForConnection(); + } + + using (StreamWriter Writer = new StreamWriter(Server, new UTF8Encoding(false), 1024, true)) + { + Writer.WriteLine("Error_Stop_Signal"); + } + } + } + await args.Request.SendResponseAsync(Value); break; } @@ -288,6 +408,7 @@ private async static void Connection_RequestReceived(AppServiceConnection sender ValueSet Value = new ValueSet(); string ExcutePathJson = Convert.ToString(args.Request.Message["ExcutePath"]); + string Guid = Convert.ToString(args.Request.Message["Guid"]); bool PermanentDelete = Convert.ToBoolean(args.Request.Message["PermanentDelete"]); List ExcutePathList = JsonConvert.DeserializeObject>(ExcutePathJson); @@ -308,7 +429,23 @@ private async static void Connection_RequestReceived(AppServiceConnection sender Attributes = FileAttributes.Normal & FileAttributes.Directory }); - if (StorageItemController.Delete(ExcutePathList, PermanentDelete)) + if (StorageItemController.Delete(ExcutePathList, PermanentDelete, (s, e) => + { + lock (Locker) + { + NamedPipeServerStream Server = PipeServers[Guid]; + + if (!Server.IsConnected) + { + Server.WaitForConnection(); + } + + using (StreamWriter Writer = new StreamWriter(Server, new UTF8Encoding(false), 1024, true)) + { + Writer.WriteLine(e.ProgressPercentage); + } + } + })) { Value.Add("Success", string.Empty); } @@ -328,6 +465,24 @@ private async static void Connection_RequestReceived(AppServiceConnection sender Value.Add("Error_Failure", "The specified file or folder could not be deleted"); } + if (!Value.ContainsKey("Success")) + { + lock (Locker) + { + NamedPipeServerStream Server = PipeServers[Guid]; + + if (!Server.IsConnected) + { + Server.WaitForConnection(); + } + + using (StreamWriter Writer = new StreamWriter(Server, new UTF8Encoding(false), 1024, true)) + { + Writer.WriteLine("Error_Stop_Signal"); + } + } + } + await args.Request.SendResponseAsync(Value); break; } @@ -412,13 +567,13 @@ private async static void Connection_RequestReceived(AppServiceConnection sender case "Excute_Test_Connection": { await args.Request.SendResponseAsync(new ValueSet { { "Excute_Test_Connection", string.Empty } }); - + break; } case "Excute_Exit": { ExitLocker.Set(); - + break; } } diff --git a/FullTrustProcess/StorageItemController.cs b/FullTrustProcess/StorageItemController.cs index 38b41d3f..2e72417a 100644 --- a/FullTrustProcess/StorageItemController.cs +++ b/FullTrustProcess/StorageItemController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; @@ -110,17 +111,19 @@ public static bool TryUnoccupied(string Path) } } - public static bool Delete(IEnumerable Source, bool PermanentDelete) + public static bool Delete(IEnumerable Source, bool PermanentDelete, ProgressChangedEventHandler Progress) { try { - using(ShellFileOperations Operation=new ShellFileOperations + using (ShellFileOperations Operation = new ShellFileOperations { - Options= PermanentDelete + 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; + foreach (string Path in Source) { using (ShellItem Item = new ShellItem(Path)) @@ -130,6 +133,8 @@ public static bool Delete(IEnumerable Source, bool PermanentDelete) } Operation.PerformOperations(); + + Operation.UpdateProgress -= Progress; } return true; @@ -140,7 +145,7 @@ public static bool Delete(IEnumerable Source, bool PermanentDelete) } } - public static bool Copy(IEnumerable> Source, string DestinationPath) + public static bool Copy(IEnumerable> Source, string DestinationPath, ProgressChangedEventHandler Progress) { try { @@ -154,6 +159,8 @@ public static bool Copy(IEnumerable> Source, string Options = ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent }) { + Operation.UpdateProgress += Progress; + foreach (KeyValuePair SourceInfo in Source) { using (ShellItem SourceItem = new ShellItem(SourceInfo.Key)) @@ -164,6 +171,8 @@ public static bool Copy(IEnumerable> Source, string } Operation.PerformOperations(); + + Operation.UpdateProgress -= Progress; } return true; @@ -174,7 +183,7 @@ public static bool Copy(IEnumerable> Source, string } } - public static bool Move(IEnumerable> Source, string DestinationPath) + public static bool Move(IEnumerable> Source, string DestinationPath, ProgressChangedEventHandler Progress) { try { @@ -188,6 +197,8 @@ public static bool Move(IEnumerable> Source, string Options = ShellFileOperations.OperationFlags.AddUndoRecord | ShellFileOperations.OperationFlags.NoConfirmMkDir | ShellFileOperations.OperationFlags.Silent }) { + Operation.UpdateProgress += Progress; + foreach (KeyValuePair SourceInfo in Source) { using (ShellItem SourceItem = new ShellItem(SourceInfo.Key)) @@ -198,6 +209,8 @@ public static bool Move(IEnumerable> Source, string } Operation.PerformOperations(); + + Operation.UpdateProgress -= Progress; } return true; diff --git a/Package/Package.appxmanifest b/Package/Package.appxmanifest index 9a1bf260..c29ca649 100644 --- a/Package/Package.appxmanifest +++ b/Package/Package.appxmanifest @@ -14,7 +14,7 @@ + Version="5.0.6.0" /> ms-resource:App_Display_Name diff --git a/RX_Explorer/App.xaml.cs b/RX_Explorer/App.xaml.cs index 90c9365e..bac90008 100644 --- a/RX_Explorer/App.xaml.cs +++ b/RX_Explorer/App.xaml.cs @@ -1,5 +1,6 @@ using RX_Explorer.Class; using System; +using System.Diagnostics; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.ApplicationModel.Core; @@ -49,6 +50,7 @@ protected async override void OnWindowCreated(WindowCreatedEventArgs args) private void App_Suspending(object sender, SuspendingEventArgs e) { + PipeLineController.Current.Dispose(); FullTrustExcutorController.Current.Dispose(); } @@ -91,6 +93,7 @@ private void ReduceMemoryUsage() if(!FullTrustExcutorController.Current.IsNowHasAnyActionExcuting) { + PipeLineController.Current.Dispose(); FullTrustExcutorController.Current.Dispose(); } diff --git a/RX_Explorer/Assets/UpdateLog-Chinese.txt b/RX_Explorer/Assets/UpdateLog-Chinese.txt index c08d6c4b..a7f94849 100644 --- a/RX_Explorer/Assets/UpdateLog-Chinese.txt +++ b/RX_Explorer/Assets/UpdateLog-Chinese.txt @@ -1,4 +1,27 @@ ***** +###版本5.0.6.0更新 + +>#####功能更新 + +>>#####新增复制、移动、删除三大文件操作的进度跟踪 + +>>#####新增“平铺”显示模式下,鼠标悬停显示文件名称的功能 + +>>#####新增空白处右键菜单按钮“撤销” + +>>#####现在“撤销”功能可以撤销删除操作 + +>#####Bug修复 + +>>#####解决了“撤销”操作仅能够撤销最近一次操作的问题,现在可撤销所有操作 + +>>#####解决了若“撤销”操作失败,将导致崩溃的问题 + +>>#####解决了“正在复制...”等提示未覆盖左侧文件夹目录的问题 + +>>#####解决了复制、移动、删除操作时,发生错误后提供了不正确的信息的问题 + +***** ###版本5.0.3.0更新 >#####功能更新 diff --git a/RX_Explorer/Assets/UpdateLog-English.txt b/RX_Explorer/Assets/UpdateLog-English.txt index aec96c2e..6cfae6ec 100644 --- a/RX_Explorer/Assets/UpdateLog-English.txt +++ b/RX_Explorer/Assets/UpdateLog-English.txt @@ -1,4 +1,27 @@ ***** +###What's new in Ver.5.0.6.0 + +>#####Feature update + +>>#####Additional progress tracking of the operation of copying, moving and deleting three files + +>>#####Added "Tile" display mode, mouse hover to display file name function + +>>#####Add a blank space right-click menu button "Cancel" + +>>#####The "Undo" function can now undo the delete operation + +>#####Bug fix + +>>#####Fixed the problem that the "Undo" operation can only undo the most recent operation, and now all operations can be undo + +>>#####Fixed the problem of crash if "Undo" operation fails + +>>#####Fixed the problem that the "Copying..." and other prompts did not cover the left folder tree + +>>#####Fixed the problem of providing incorrect information after an error occurs during copy, move, delete operations + +***** ###What's new in Ver.5.0.3.0 >#####Feature update diff --git a/RX_Explorer/Assets/UpdateLog-French.txt b/RX_Explorer/Assets/UpdateLog-French.txt index ff9b615a..44446746 100644 --- a/RX_Explorer/Assets/UpdateLog-French.txt +++ b/RX_Explorer/Assets/UpdateLog-French.txt @@ -1,4 +1,27 @@ ***** +###Quoi de neuf dans la version 5.0.6.0 + +>#####Mise à jour des fonctionnalités + +>>#####Suivi supplémentaire de la progression de l'opération de copie, de déplacement et de suppression de trois fichiers + +>>#####Ajout du mode d'affichage "Tile", survol de la souris pour afficher la fonction de nom de fichier + +>>#####Ajouter un espace vide clic droit bouton de menu "Annuler" + +>>#####La fonction "Annuler" peut maintenant annuler l'opération de suppression + +>#####Correction d'un bug + +>>#####Correction du problème selon lequel l'opération "Annuler" ne peut annuler que l'opération la plus récente, et maintenant toutes les opérations peuvent être annulées + +>>#####Correction du problème de plantage en cas d'échec de l'opération "Annuler" + +>>#####Correction du problème selon lequel "Copie ..." et d'autres invites ne couvraient pas l'arborescence de dossiers de gauche + +>>#####Correction du problème de fournir des informations incorrectes après une erreur lors de la copie, le déplacement, la suppression des opérations + +***** ###Quoi de neuf dans la version 5.0.3.0 >#####Mise à jour des fonctionnalités diff --git a/RX_Explorer/Class/BackgroundController.cs b/RX_Explorer/Class/BackgroundController.cs index c80afa77..11a6f401 100644 --- a/RX_Explorer/Class/BackgroundController.cs +++ b/RX_Explorer/Class/BackgroundController.cs @@ -141,8 +141,8 @@ private BackgroundController() { BackgroundSource = AcrylicBackgroundSource.HostBackdrop, TintColor = ApplicationData.Current.LocalSettings.Values["AcrylicThemeColor"] is string Color ? GetColorFromHexString(Color) : Colors.LightSlateGray, - TintOpacity = 1 - Convert.ToSingle(ApplicationData.Current.LocalSettings.Values["BackgroundTintOpacity"]), - TintLuminosityOpacity = 1 - Convert.ToSingle(ApplicationData.Current.LocalSettings.Values["BackgroundTintLuminosity"]), + TintOpacity = 1 - Convert.ToDouble(ApplicationData.Current.LocalSettings.Values["BackgroundTintOpacity"]), + TintLuminosityOpacity = 1 - Convert.ToDouble(ApplicationData.Current.LocalSettings.Values["BackgroundTintLuminosity"]), FallbackColor = Colors.DimGray }; } @@ -152,7 +152,7 @@ private BackgroundController() { BackgroundSource = AcrylicBackgroundSource.HostBackdrop, TintColor = ApplicationData.Current.LocalSettings.Values["AcrylicThemeColor"] is string Color ? GetColorFromHexString(Color) : Colors.LightSlateGray, - TintOpacity = 1 - Convert.ToSingle(ApplicationData.Current.LocalSettings.Values["BackgroundTintOpacity"]), + TintOpacity = 1 - Convert.ToDouble(ApplicationData.Current.LocalSettings.Values["BackgroundTintOpacity"]), FallbackColor = Colors.DimGray }; } @@ -208,7 +208,7 @@ public double TintOpacity set { AcrylicBackgroundBrush.SetValue(AcrylicBrush.TintOpacityProperty, 1 - value); - ApplicationData.Current.LocalSettings.Values["BackgroundTintOpacity"] = value.ToString("0.0"); + ApplicationData.Current.LocalSettings.Values["BackgroundTintOpacity"] = value.ToString(); } } @@ -239,7 +239,7 @@ public double TintLuminosityOpacity else { AcrylicBackgroundBrush.SetValue(AcrylicBrush.TintLuminosityOpacityProperty, 1 - value); - ApplicationData.Current.LocalSettings.Values["BackgroundTintLuminosity"] = value.ToString("0.0"); + ApplicationData.Current.LocalSettings.Values["BackgroundTintLuminosity"] = value.ToString(); } } } diff --git a/RX_Explorer/Class/FullTrustExcutorController.cs b/RX_Explorer/Class/FullTrustExcutorController.cs index aaaf1272..66a503fe 100644 --- a/RX_Explorer/Class/FullTrustExcutorController.cs +++ b/RX_Explorer/Class/FullTrustExcutorController.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; @@ -28,7 +29,7 @@ public sealed class FullTrustExcutorController : IDisposable private const string ExcuteType_Get_RecycleBinItems = "Excute_Get_RecycleBinItems"; - private const string ExcuteType_Exit = "Excute_Exit"; + private const string ExcuteType_RequestCreateNewPipe = "Excute_RequestCreateNewPipe"; private const string ExcuteType_EmptyRecycleBin = "Excute_Empty_RecycleBin"; @@ -87,11 +88,11 @@ private async void Connection_RequestReceived(AppServiceConnection sender, AppSe { var Deferral = args.GetDeferral(); - switch(args.Request.Message["ExcuteType"]) + switch (args.Request.Message["ExcuteType"]) { case "Identity": { - await args.Request.SendResponseAsync(new ValueSet { { "Identity", "UWP" } }); + await args.Request.SendResponseAsync(new ValueSet { { "Identity", "UWP" }, { "Guid", PipeLineController.Current.GUID.ToString() } }); break; } } @@ -135,6 +136,33 @@ public async Task TryConnectToFullTrustExutor() } } + public async Task RequestCreateNewPipeLine(Guid CurrentProcessID) + { + try + { + IsNowHasAnyActionExcuting = true; + + if (await TryConnectToFullTrustExutor().ConfigureAwait(false)) + { + ValueSet Value = new ValueSet + { + {"ExcuteType", ExcuteType_RequestCreateNewPipe}, + {"Guid",CurrentProcessID.ToString() }, + }; + + await Connection.SendMessageAsync(Value); + } + } + catch + { + Debug.WriteLine("Warning: RequestCreateNewPipeLine() excute error"); + } + finally + { + IsNowHasAnyActionExcuting = false; + } + } + /// /// 启动指定路径的程序 /// @@ -513,7 +541,7 @@ public async Task TryUnlockFileOccupy(string Path) } } - public async Task DeleteAsync(IEnumerable Source, bool PermanentDelete) + public async Task DeleteAsync(IEnumerable Source, bool PermanentDelete, ProgressChangedEventHandler ProgressHandler = null) { try { @@ -521,30 +549,44 @@ public async Task DeleteAsync(IEnumerable Source, bool PermanentDelete) if (await TryConnectToFullTrustExutor().ConfigureAwait(false)) { + Task ProgressTask; + + if (await PipeLineController.Current.CreateNewNamedPipe().ConfigureAwait(true)) + { + ProgressTask = PipeLineController.Current.ListenPipeMessage(ProgressHandler); + } + else + { + ProgressTask = Task.CompletedTask; + } + ValueSet Value = new ValueSet { {"ExcuteType", ExcuteType_Delete}, {"ExcutePath", JsonConvert.SerializeObject(Source)}, - {"PermanentDelete", PermanentDelete} + {"PermanentDelete", PermanentDelete}, + {"Guid", PipeLineController.Current.GUID.ToString() } }; - AppServiceResponse Response = await Connection.SendMessageAsync(Value); + Task MessageTask = Connection.SendMessageAsync(Value).AsTask(); - if (Response.Status == AppServiceResponseStatus.Success) + await Task.WhenAll(MessageTask, ProgressTask).ConfigureAwait(true); + + if (MessageTask.Result.Status == AppServiceResponseStatus.Success) { - if (Response.Message.ContainsKey("Success")) + if (MessageTask.Result.Message.ContainsKey("Success")) { return; } - else if (Response.Message.ContainsKey("Error_NotFound")) + else if (MessageTask.Result.Message.ContainsKey("Error_NotFound")) { throw new FileNotFoundException(); } - else if (Response.Message.ContainsKey("Error_Failure")) + else if (MessageTask.Result.Message.ContainsKey("Error_Failure")) { throw new InvalidOperationException("Fail to delete item"); } - else if (Response.Message.ContainsKey("Error_Capture")) + else if (MessageTask.Result.Message.ContainsKey("Error_Capture")) { throw new FileCaputureException(); } @@ -569,37 +611,37 @@ public async Task DeleteAsync(IEnumerable Source, bool PermanentDelete) } } - public Task DeleteAsync(IEnumerable Source, bool PermanentDelete) + public Task DeleteAsync(IEnumerable Source, bool PermanentDelete, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { throw new ArgumentNullException(nameof(Source), "Parameter could not be null"); } - return DeleteAsync(Source.Select((Item) => Item.Path), PermanentDelete); + return DeleteAsync(Source.Select((Item) => Item.Path), PermanentDelete, ProgressHandler); } - public Task DeleteAsync(IStorageItem Source, bool PermanentDelete) + public Task DeleteAsync(IStorageItem Source, bool PermanentDelete, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { throw new ArgumentNullException(nameof(Source), "Parameter could not be null"); } - return DeleteAsync(new string[1] { Source.Path }, PermanentDelete); + return DeleteAsync(new string[1] { Source.Path }, PermanentDelete, ProgressHandler); } - public Task DeleteAsync(string Source, bool PermanentDelete) + public Task DeleteAsync(string Source, bool PermanentDelete, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { throw new ArgumentNullException(nameof(Source), "Parameter could not be null"); } - return DeleteAsync(new string[1] { Source }, PermanentDelete); + return DeleteAsync(new string[1] { Source }, PermanentDelete, ProgressHandler); } - public async Task MoveAsync(IEnumerable Source, string DestinationPath) + public async Task MoveAsync(IEnumerable Source, string DestinationPath, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { @@ -659,30 +701,44 @@ public async Task MoveAsync(IEnumerable Source, string DestinationPath) } } + Task ProgressTask; + + if (await PipeLineController.Current.CreateNewNamedPipe().ConfigureAwait(true)) + { + ProgressTask = PipeLineController.Current.ListenPipeMessage(ProgressHandler); + } + else + { + ProgressTask = Task.CompletedTask; + } + ValueSet Value = new ValueSet { {"ExcuteType", ExcuteType_Move}, {"SourcePath", JsonConvert.SerializeObject(MessageList)}, - {"DestinationPath", DestinationPath} + {"DestinationPath", DestinationPath}, + {"Guid", PipeLineController.Current.GUID.ToString() } }; - AppServiceResponse Response = await Connection.SendMessageAsync(Value); + Task MessageTask = Connection.SendMessageAsync(Value).AsTask(); - if (Response.Status == AppServiceResponseStatus.Success) + await Task.WhenAll(MessageTask, ProgressTask).ConfigureAwait(true); + + if (MessageTask.Result.Status == AppServiceResponseStatus.Success) { - if (Response.Message.ContainsKey("Success")) + if (MessageTask.Result.Message.ContainsKey("Success")) { return; } - else if (Response.Message.ContainsKey("Error_NotFound")) + else if (MessageTask.Result.Message.ContainsKey("Error_NotFound")) { throw new FileNotFoundException(); } - else if (Response.Message.ContainsKey("Error_Failure")) + else if (MessageTask.Result.Message.ContainsKey("Error_Failure")) { throw new InvalidOperationException("Fail to move item"); } - else if (Response.Message.ContainsKey("Error_Capture")) + else if (MessageTask.Result.Message.ContainsKey("Error_Capture")) { throw new FileCaputureException(); } @@ -707,7 +763,7 @@ public async Task MoveAsync(IEnumerable Source, string DestinationPath) } } - public Task MoveAsync(IEnumerable Source, StorageFolder Destination) + public Task MoveAsync(IEnumerable Source, StorageFolder Destination, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { @@ -719,10 +775,10 @@ public Task MoveAsync(IEnumerable Source, StorageFolder Destinatio throw new ArgumentNullException(nameof(Destination), "Parameter could not be null"); } - return MoveAsync(Source.Select((Item) => Item.Path), Destination.Path); + return MoveAsync(Source.Select((Item) => Item.Path), Destination.Path, ProgressHandler); } - public Task MoveAsync(IStorageItem Source, StorageFolder Destination) + public Task MoveAsync(IStorageItem Source, StorageFolder Destination, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { @@ -734,10 +790,10 @@ public Task MoveAsync(IStorageItem Source, StorageFolder Destination) throw new ArgumentNullException(nameof(Destination), "Parameter could not be null"); } - return MoveAsync(new string[1] { Source.Path }, Destination.Path); + return MoveAsync(new string[1] { Source.Path }, Destination.Path, ProgressHandler); } - public async Task CopyAsync(IEnumerable Source, string DestinationPath) + public async Task CopyAsync(IEnumerable Source, string DestinationPath, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { @@ -797,26 +853,40 @@ public async Task CopyAsync(IEnumerable Source, string DestinationPath) } } + Task ProgressTask; + + if (await PipeLineController.Current.CreateNewNamedPipe().ConfigureAwait(true)) + { + ProgressTask = PipeLineController.Current.ListenPipeMessage(ProgressHandler); + } + else + { + ProgressTask = Task.CompletedTask; + } + ValueSet Value = new ValueSet { {"ExcuteType", ExcuteType_Copy}, {"SourcePath", JsonConvert.SerializeObject(MessageList)}, - {"DestinationPath", DestinationPath} + {"DestinationPath", DestinationPath}, + {"Guid", PipeLineController.Current.GUID.ToString() } }; - AppServiceResponse Response = await Connection.SendMessageAsync(Value); + Task MessageTask = Connection.SendMessageAsync(Value).AsTask(); - if (Response.Status == AppServiceResponseStatus.Success) + await Task.WhenAll(MessageTask, ProgressTask).ConfigureAwait(true); + + if (MessageTask.Result.Status == AppServiceResponseStatus.Success) { - if (Response.Message.ContainsKey("Success")) + if (MessageTask.Result.Message.ContainsKey("Success")) { return; } - else if (Response.Message.ContainsKey("Error_NotFound")) + else if (MessageTask.Result.Message.ContainsKey("Error_NotFound")) { throw new FileNotFoundException(); } - else if (Response.Message.ContainsKey("Error_Failure")) + else if (MessageTask.Result.Message.ContainsKey("Error_Failure")) { throw new InvalidOperationException("Fail to copy item"); } @@ -841,7 +911,7 @@ public async Task CopyAsync(IEnumerable Source, string DestinationPath) } } - public Task CopyAsync(IEnumerable Source, StorageFolder Destination) + public Task CopyAsync(IEnumerable Source, StorageFolder Destination, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { @@ -853,10 +923,10 @@ public Task CopyAsync(IEnumerable Source, StorageFolder Destinatio throw new ArgumentNullException(nameof(Destination), "Parameter could not be null"); } - return CopyAsync(Source.Select((Item) => Item.Path), Destination.Path); + return CopyAsync(Source.Select((Item) => Item.Path), Destination.Path, ProgressHandler); } - public Task CopyAsync(IStorageItem Source, StorageFolder Destination) + public Task CopyAsync(IStorageItem Source, StorageFolder Destination, ProgressChangedEventHandler ProgressHandler = null) { if (Source == null) { @@ -868,7 +938,7 @@ public Task CopyAsync(IStorageItem Source, StorageFolder Destination) throw new ArgumentNullException(nameof(Destination), "Parameter could not be null"); } - return CopyAsync(new string[1] { Source.Path }, Destination.Path); + return CopyAsync(new string[1] { Source.Path }, Destination.Path, ProgressHandler); } public async Task RestoreItemInRecycleBinAsync(string Path) @@ -1013,6 +1083,7 @@ public void Dispose() IsConnected = false; } + Connection.RequestReceived -= Connection_RequestReceived; Connection?.Dispose(); Connection = null; diff --git a/RX_Explorer/Class/MySQL.cs b/RX_Explorer/Class/MySQL.cs index 70680977..99b64999 100644 --- a/RX_Explorer/Class/MySQL.cs +++ b/RX_Explorer/Class/MySQL.cs @@ -1,4 +1,4 @@ -using MySql.Data.MySqlClient; +using MySqlConnector; using NetworkAccess; using SQLConnectionPoolProvider; using System; diff --git a/RX_Explorer/Class/PipeLineController.cs b/RX_Explorer/Class/PipeLineController.cs new file mode 100644 index 00000000..afb7567d --- /dev/null +++ b/RX_Explorer/Class/PipeLineController.cs @@ -0,0 +1,121 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.IO.Pipes; +using System.Text; +using System.Threading.Tasks; +using Windows.ApplicationModel.Core; +using Windows.UI.Core; + +namespace RX_Explorer.Class +{ + public sealed class PipeLineController : IDisposable + { + private static PipeLineController Instance; + + private static readonly object Locker = new object(); + + public static PipeLineController Current + { + get + { + lock (Locker) + { + return Instance ??= new PipeLineController(); + } + } + } + + public Guid GUID { get; private set; } + + private NamedPipeClientStream ClientStream; + + public async Task ListenPipeMessage(ProgressChangedEventHandler Handler) + { + if (ClientStream == null) + { + throw new InvalidOperationException("Excute CreateNewNamedPipe() first"); + } + + try + { + using (StreamReader Reader = new StreamReader(ClientStream, new UTF8Encoding(false), false, 1024, true)) + { + int Percentage = 0; + + while (Percentage < 100) + { + string ReadText = await Reader.ReadLineAsync().ConfigureAwait(true); + + if (!string.IsNullOrEmpty(ReadText) && ReadText != "Error_Stop_Signal") + { + Percentage = Convert.ToInt32(ReadText); + + if (Percentage > 0) + { + await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Handler?.Invoke(this, new ProgressChangedEventArgs(Percentage, null)); + }); + } + } + else + { + break; + } + } + } + } + catch + { + await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Handler?.Invoke(this, new ProgressChangedEventArgs(0, null)); + }); + } + } + + public async Task CreateNewNamedPipe() + { + try + { + if (ClientStream != null) + { + return true; + } + + await FullTrustExcutorController.Current.RequestCreateNewPipeLine(GUID).ConfigureAwait(true); + + ClientStream = new NamedPipeClientStream(PipeDirection.InOut, false, true, WIN_Native_API.GetHandleFromNamedPipe($"Explorer_And_FullTrustProcess_NamedPipe-{GUID}")); + + return true; + } + catch (Exception e) + { + Debug.WriteLine(e.Message); + return false; + } + } + + private PipeLineController() + { + GUID = Guid.NewGuid(); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + + ClientStream?.Dispose(); + ClientStream = null; + + Instance = null; + } + + ~PipeLineController() + { + Dispose(); + } + } +} diff --git a/RX_Explorer/Class/ValueConverter.cs b/RX_Explorer/Class/ValueConverter.cs index e6d3466e..ee3dad5c 100644 --- a/RX_Explorer/Class/ValueConverter.cs +++ b/RX_Explorer/Class/ValueConverter.cs @@ -1,5 +1,4 @@ using System; -using Windows.Storage; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; @@ -54,13 +53,40 @@ public object ConvertBack(object value, Type targetType, object parameter, strin } } + public sealed class EmptyTextFiliterConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is string v) + { + return string.IsNullOrEmpty(v) ? null : v; + } + else + { + return null; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (value is string v) + { + return string.IsNullOrEmpty(v) ? null : v; + } + else + { + return null; + } + } + } + public sealed class FolderStateConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is bool IsExpanded) { - if(IsExpanded) + if (IsExpanded) { return "\xE838"; } diff --git a/RX_Explorer/Class/WIN_Native_API.cs b/RX_Explorer/Class/WIN_Native_API.cs index f6f2c7e0..ca40281a 100644 --- a/RX_Explorer/Class/WIN_Native_API.cs +++ b/RX_Explorer/Class/WIN_Native_API.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Win32.SafeHandles; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; @@ -103,7 +104,10 @@ private static extern IntPtr FindFirstFileExFromApp(string lpFileName, [DllImport("api-ms-win-core-io-l1-1-1.dll")] private static extern bool CancelIoEx(IntPtr hFile, IntPtr lpOverlapped); + const uint GENERIC_READ = 0x80000000; + const uint GENERIC_WRITE = 0x40000000; const uint FILE_LIST_DIRECTORY = 0x1; + const uint FILE_NO_SHARE = 0x0; const uint FILE_SHARE_READ = 0x1; const uint FILE_SHARE_WRITE = 0x2; const uint FILE_SHARE_DELETE = 0x4; @@ -784,5 +788,21 @@ public static List GetStorageItemsPath(string Path, ItemFilters Filter) FindClose(Ptr); } } + + public static SafePipeHandle GetHandleFromNamedPipe(string PipeName) + { + IntPtr Handle = CreateFileFromApp(@$"\\.\pipe\{PipeName}", GENERIC_READ | GENERIC_WRITE, FILE_NO_SHARE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); + + SafePipeHandle SPipeHandle = new SafePipeHandle(Handle, true); + + if (SPipeHandle.IsInvalid) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + else + { + return SPipeHandle; + } + } } } diff --git a/RX_Explorer/FullTrust/FullTrustProcess.exe b/RX_Explorer/FullTrust/FullTrustProcess.exe index f3ebac76..38641c1f 100644 Binary files a/RX_Explorer/FullTrust/FullTrustProcess.exe and b/RX_Explorer/FullTrust/FullTrustProcess.exe differ diff --git a/RX_Explorer/FullTrust/Vanara.Core.dll b/RX_Explorer/FullTrust/Vanara.Core.dll index e6bd3383..d2f06198 100644 Binary files a/RX_Explorer/FullTrust/Vanara.Core.dll and b/RX_Explorer/FullTrust/Vanara.Core.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.ComCtl32.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.ComCtl32.dll index 8f972f04..eab218eb 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.ComCtl32.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.ComCtl32.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.Cryptography.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.Cryptography.dll index 469a560d..84308038 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.Cryptography.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.Cryptography.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.Gdi32.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.Gdi32.dll index d2b66bd6..a4ef28e4 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.Gdi32.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.Gdi32.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.Kernel32.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.Kernel32.dll index 7ea2a00c..ef71655c 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.Kernel32.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.Kernel32.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.Ole.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.Ole.dll index d0713c42..b0e340a2 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.Ole.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.Ole.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.SearchApi.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.SearchApi.dll index f373b5f8..36086a91 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.SearchApi.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.SearchApi.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.Security.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.Security.dll index 496e6e5b..df3652a8 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.Security.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.Security.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.Shared.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.Shared.dll index 879096ed..711eb029 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.Shared.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.Shared.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.Shell32.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.Shell32.dll index 91d1ce0c..f9fbf4da 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.Shell32.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.Shell32.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.ShlwApi.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.ShlwApi.dll index 9e393e60..cd493bee 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.ShlwApi.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.ShlwApi.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.PInvoke.User32.dll b/RX_Explorer/FullTrust/Vanara.PInvoke.User32.dll index 20b77d22..16e8efe7 100644 Binary files a/RX_Explorer/FullTrust/Vanara.PInvoke.User32.dll and b/RX_Explorer/FullTrust/Vanara.PInvoke.User32.dll differ diff --git a/RX_Explorer/FullTrust/Vanara.Windows.Shell.dll b/RX_Explorer/FullTrust/Vanara.Windows.Shell.dll index 4d148c0e..5e5d9d34 100644 Binary files a/RX_Explorer/FullTrust/Vanara.Windows.Shell.dll and b/RX_Explorer/FullTrust/Vanara.Windows.Shell.dll differ diff --git a/RX_Explorer/MultilingualResources/RX_Explorer.fr-FR.xlf b/RX_Explorer/MultilingualResources/RX_Explorer.fr-FR.xlf index 854f8f77..8d5ceede 100644 --- a/RX_Explorer/MultilingualResources/RX_Explorer.fr-FR.xlf +++ b/RX_Explorer/MultilingualResources/RX_Explorer.fr-FR.xlf @@ -3221,6 +3221,22 @@ Illegal characters include \ / : * ? " < > |Open in new window Ouvrir dans une nouvelle fenetre + + An error occurred while copying the file, the copy operation failed + Une erreur s'est produite lors de la copie du fichier, l'opération de copie a échoué + + + An error occurred while moving the file, the move operation failed + Une erreur s'est produite lors du déplacement du fichier, l'opération de déplacement a échoué + + + An error occurred while deleting the file, the delete operation failed + Une erreur s'est produite lors de la suppression du fichier, l'opération de suppression a échoué + + + Undo + Annuler + diff --git a/RX_Explorer/MultilingualResources/RX_Explorer.zh-Hans.xlf b/RX_Explorer/MultilingualResources/RX_Explorer.zh-Hans.xlf index 0af1d178..e0f72b89 100644 --- a/RX_Explorer/MultilingualResources/RX_Explorer.zh-Hans.xlf +++ b/RX_Explorer/MultilingualResources/RX_Explorer.zh-Hans.xlf @@ -3221,6 +3221,22 @@ Illegal characters include \ / : * ? " < > |Open in new window 在新窗口中打开 + + An error occurred while copying the file, the copy operation failed + 复制文件时出现错误,复制操作已失败 + + + An error occurred while moving the file, the move operation failed + 移动文件时出现错误,移动操作已失败 + + + An error occurred while deleting the file, the delete operation failed + 删除文件时出现错误,删除操作已失败 + + + Undo + 撤销 + diff --git a/RX_Explorer/Package.appxmanifest b/RX_Explorer/Package.appxmanifest index ab0688e5..f4e6989d 100644 --- a/RX_Explorer/Package.appxmanifest +++ b/RX_Explorer/Package.appxmanifest @@ -14,7 +14,7 @@ + Version="5.0.6.0" /> diff --git a/RX_Explorer/RX_Explorer.csproj b/RX_Explorer/RX_Explorer.csproj index bf4e956b..803a4363 100644 --- a/RX_Explorer/RX_Explorer.csproj +++ b/RX_Explorer/RX_Explorer.csproj @@ -161,6 +161,7 @@ + @@ -680,7 +681,7 @@ 1.2.0 - 2.11.0 + 2.11.2 2.0.0 @@ -717,7 +718,7 @@ 2.4.2 - 0.69.6 + 1.0.0 0.0.2-alpha diff --git a/RX_Explorer/Strings/en-US/Resources.resw b/RX_Explorer/Strings/en-US/Resources.resw index 8f5450db..83ddb647 100644 --- a/RX_Explorer/Strings/en-US/Resources.resw +++ b/RX_Explorer/Strings/en-US/Resources.resw @@ -2540,4 +2540,16 @@ Illegal characters include \ / : * ? " < > | Open in new window + + An error occurred while copying the file, the copy operation failed + + + An error occurred while deleting the file, the delete operation failed + + + An error occurred while moving the file, the move operation failed + + + Undo + \ No newline at end of file diff --git a/RX_Explorer/Strings/fr-FR/Resources.resw b/RX_Explorer/Strings/fr-FR/Resources.resw index b8a62877..8a810b5a 100644 --- a/RX_Explorer/Strings/fr-FR/Resources.resw +++ b/RX_Explorer/Strings/fr-FR/Resources.resw @@ -2435,4 +2435,16 @@ Ouvrir dans une nouvelle fenetre + + Une erreur s'est produite lors de la copie du fichier, l'opération de copie a échoué + + + Une erreur s'est produite lors du déplacement du fichier, l'opération de déplacement a échoué + + + Une erreur s'est produite lors de la suppression du fichier, l'opération de suppression a échoué + + + Annuler + \ No newline at end of file diff --git a/RX_Explorer/Strings/zh-Hans/Resources.resw b/RX_Explorer/Strings/zh-Hans/Resources.resw index a8f313a5..1cce9756 100644 --- a/RX_Explorer/Strings/zh-Hans/Resources.resw +++ b/RX_Explorer/Strings/zh-Hans/Resources.resw @@ -2435,4 +2435,16 @@ 在新窗口中打开 + + 复制文件时出现错误,复制操作已失败 + + + 移动文件时出现错误,移动操作已失败 + + + 删除文件时出现错误,删除操作已失败 + + + 撤销 + \ No newline at end of file diff --git a/RX_Explorer/View/CropperPage.xaml b/RX_Explorer/View/CropperPage.xaml index e7e0a4b6..61aa56a6 100644 --- a/RX_Explorer/View/CropperPage.xaml +++ b/RX_Explorer/View/CropperPage.xaml @@ -304,7 +304,7 @@ diff --git a/RX_Explorer/View/FileControl.xaml b/RX_Explorer/View/FileControl.xaml index 0816963a..2994c350 100644 --- a/RX_Explorer/View/FileControl.xaml +++ b/RX_Explorer/View/FileControl.xaml @@ -12,7 +12,7 @@ Background="Transparent"> - + + ToolTipService.ToolTip="{Binding}" /> @@ -71,11 +71,11 @@ x:Uid="FileControlRenameFolder" Name="FolderRename" Click="FolderRename_Click" /> - + Click="FolderAdd_Click" /> + x:Uid="SearchTip" + Grid.Row="1" + Title="Tips" + Target="{x:Bind GlobeSearch}" + PreferredPlacement="BottomRight" + IsLightDismissEnabled="True" + Subtitle="清空这个搜索框或点击任意文件夹即可退出搜索状态哦!" + CloseButtonContent="知道啦"> @@ -106,8 +106,8 @@ - - + + - + TextChanged="AddressBox_TextChanged" /> + TextAlignment="Center" /> @@ -332,7 +333,29 @@ HorizontalAlignment="Center" ResizeBehavior="BasedOnAlignment" ResizeDirection="Auto" - Grid.Column="1"/> + Grid.Column="1" /> + + + + + + + + + diff --git a/RX_Explorer/View/FileControl.xaml.cs b/RX_Explorer/View/FileControl.xaml.cs index 674b810e..e4643348 100644 --- a/RX_Explorer/View/FileControl.xaml.cs +++ b/RX_Explorer/View/FileControl.xaml.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -201,6 +200,34 @@ public FileControl() } } + /// + /// 激活或关闭正在加载提示 + /// + /// 激活或关闭 + /// 提示内容 + public async Task LoadingActivation(bool IsLoading, string Info = null) + { + if (IsLoading) + { + if (TabViewContainer.ThisPage.FFInstanceContainer[this].HasFile.Visibility == Visibility.Visible) + { + TabViewContainer.ThisPage.FFInstanceContainer[this].HasFile.Visibility = Visibility.Collapsed; + } + + ProBar.IsIndeterminate = true; + ProgressInfo.Text = Info + "..."; + + MainPage.ThisPage.IsAnyTaskRunning = true; + } + else + { + await Task.Delay(1000).ConfigureAwait(true); + MainPage.ThisPage.IsAnyTaskRunning = false; + } + + LoadingControl.IsLoading = IsLoading; + } + private async void UpdateAddressButton(string Path) { if (Interlocked.Exchange(ref AddressButtonLockResource, 1) == 0) @@ -614,6 +641,8 @@ private async void FolderDelete_Click(object sender, RoutedEventArgs e) if (await QueueContenDialog.ShowAsync().ConfigureAwait(true) == ContentDialogResult.Primary) { + await LoadingActivation(true, Globalization.GetString("Progress_Tip_Deleting")).ConfigureAwait(true); + try { await FullTrustExcutorController.Current.DeleteAsync(CurrentFolder, QueueContenDialog.IsPermanentDelete).ConfigureAwait(true); @@ -645,6 +674,16 @@ private async void FolderDelete_Click(object sender, RoutedEventArgs e) await DisplayItemsInFolder(CurrentNode, true).ConfigureAwait(true); } + catch (InvalidOperationException) + { + QueueContentDialog dialog = new QueueContentDialog + { + Title = Globalization.GetString("Common_Dialog_ErrorTitle"), + Content = Globalization.GetString("QueueDialog_DeleteFailUnexpectError_Content"), + CloseButtonText = Globalization.GetString("Common_Dialog_CloseButton") + }; + _ = await dialog.ShowAsync().ConfigureAwait(true); + } catch (Exception) { QueueContentDialog Dialog = new QueueContentDialog @@ -655,6 +694,8 @@ private async void FolderDelete_Click(object sender, RoutedEventArgs e) }; _ = await Dialog.ShowAsync().ConfigureAwait(true); } + + await LoadingActivation(false).ConfigureAwait(true); } } @@ -993,7 +1034,7 @@ private async void GlobeSearch_GotFocus(object sender, RoutedEventArgs e) private async void AddressBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) { - TabViewContainer.ThisPage.FFInstanceContainer[this].LoadingControl.Focus(FocusState.Programmatic); + LoadingControl.Focus(FocusState.Programmatic); string QueryText = string.Empty; if (args.ChosenSuggestion == null) @@ -1538,7 +1579,7 @@ private async void AddressExtentionFlyout_Closing(FlyoutBase sender, FlyoutBaseC private async void AddressExtensionSubFolderList_ItemClick(object sender, ItemClickEventArgs e) { - await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { AddressExtentionFlyout.Hide(); }); @@ -1634,7 +1675,7 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) return; } - TabViewContainer.CopyAndMoveRecord.Clear(); + TabViewContainer.StorageItemOperationRecord.Clear(); if (e.DataView.Contains(StandardDataFormats.StorageItems)) { @@ -1646,14 +1687,19 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) { case DataPackageOperation.Copy: { - await TabViewContainer.ThisPage.FFInstanceContainer[this].LoadingActivation(true, Globalization.GetString("Progress_Tip_Copying")).ConfigureAwait(true); + await LoadingActivation(true, Globalization.GetString("Progress_Tip_Copying")).ConfigureAwait(true); bool IsItemNotFound = false; bool IsUnauthorized = false; + bool IsOperateFailed = false; try { - await FullTrustExcutorController.Current.CopyAsync(DragItemList, TargetFolder).ConfigureAwait(true); + await FullTrustExcutorController.Current.CopyAsync(DragItemList, TargetFolder, (s, arg) => + { + ProBar.IsIndeterminate = false; + ProBar.Value = arg.ProgressPercentage; + }).ConfigureAwait(true); if (!SettingControl.IsDetachTreeViewAndPresenter && ActualPath.StartsWith((FolderTree.RootNodes[0].Content as TreeViewNodeContent).Path)) { @@ -1664,11 +1710,11 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) { if (Item.IsOfType(StorageItemTypes.File)) { - TabViewContainer.CopyAndMoveRecord.Add($"{Item.Path}||Copy||File||{Path.Combine(TargetFolder.Path, Item.Name)}"); + TabViewContainer.StorageItemOperationRecord.Push($"{Item.Path}||Copy||File||{Path.Combine(TargetFolder.Path, Item.Name)}"); } else { - TabViewContainer.CopyAndMoveRecord.Add($"{Item.Path}||Copy||Folder||{Path.Combine(TargetFolder.Path, Item.Name)}"); + TabViewContainer.StorageItemOperationRecord.Push($"{Item.Path}||Copy||Folder||{Path.Combine(TargetFolder.Path, Item.Name)}"); } } } @@ -1676,6 +1722,10 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) { IsItemNotFound = true; } + catch (InvalidOperationException) + { + IsOperateFailed = true; + } catch (Exception) { IsUnauthorized = true; @@ -1706,20 +1756,35 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) _ = await Launcher.LaunchFolderAsync(CurrentFolder); } } + else if (IsOperateFailed) + { + QueueContentDialog dialog = new QueueContentDialog + { + Title = Globalization.GetString("Common_Dialog_ErrorTitle"), + Content = Globalization.GetString("QueueDialog_CopyFailUnexpectError_Content"), + CloseButtonText = Globalization.GetString("Common_Dialog_CloseButton") + }; + _ = await dialog.ShowAsync().ConfigureAwait(true); + } break; } case DataPackageOperation.Move: { - await TabViewContainer.ThisPage.FFInstanceContainer[this].LoadingActivation(true, Globalization.GetString("Progress_Tip_Moving")).ConfigureAwait(true); + await LoadingActivation(true, Globalization.GetString("Progress_Tip_Moving")).ConfigureAwait(true); bool IsItemNotFound = false; bool IsUnauthorized = false; bool IsCaptured = false; + bool IsOperateFailed = false; try { - await FullTrustExcutorController.Current.MoveAsync(DragItemList, TargetFolder).ConfigureAwait(true); + await FullTrustExcutorController.Current.MoveAsync(DragItemList, TargetFolder, (s, arg) => + { + ProBar.IsIndeterminate = false; + ProBar.Value = arg.ProgressPercentage; + }).ConfigureAwait(true); if (!SettingControl.IsDetachTreeViewAndPresenter && ActualPath.StartsWith((FolderTree.RootNodes[0].Content as TreeViewNodeContent).Path)) { @@ -1730,11 +1795,11 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) { if (Item.IsOfType(StorageItemTypes.File)) { - TabViewContainer.CopyAndMoveRecord.Add($"{Item.Path}||Move||File||{Path.Combine(TargetFolder.Path, Item.Name)}"); + TabViewContainer.StorageItemOperationRecord.Push($"{Item.Path}||Move||File||{Path.Combine(TargetFolder.Path, Item.Name)}"); } else { - TabViewContainer.CopyAndMoveRecord.Add($"{Item.Path}||Move||Folder||{Path.Combine(TargetFolder.Path, Item.Name)}"); + TabViewContainer.StorageItemOperationRecord.Push($"{Item.Path}||Move||Folder||{Path.Combine(TargetFolder.Path, Item.Name)}"); } } } @@ -1746,6 +1811,10 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) { IsCaptured = true; } + catch (InvalidOperationException) + { + IsOperateFailed = true; + } catch (Exception) { IsUnauthorized = true; @@ -1788,6 +1857,16 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) _ = await dialog.ShowAsync().ConfigureAwait(true); } + else if (IsOperateFailed) + { + QueueContentDialog dialog = new QueueContentDialog + { + Title = Globalization.GetString("Common_Dialog_ErrorTitle"), + Content = Globalization.GetString("QueueDialog_MoveFailUnexpectError_Content"), + CloseButtonText = Globalization.GetString("Common_Dialog_CloseButton") + }; + _ = await dialog.ShowAsync().ConfigureAwait(true); + } break; } @@ -1796,7 +1875,7 @@ private async void AddressButton_Drop(object sender, DragEventArgs e) } finally { - await TabViewContainer.ThisPage.FFInstanceContainer[this].LoadingActivation(false).ConfigureAwait(true); + await LoadingActivation(false).ConfigureAwait(true); e.Handled = true; _ = Interlocked.Exchange(ref DropLockResource, 0); } diff --git a/RX_Explorer/View/FilePresenter.xaml b/RX_Explorer/View/FilePresenter.xaml index 9e128336..82ad9616 100644 --- a/RX_Explorer/View/FilePresenter.xaml +++ b/RX_Explorer/View/FilePresenter.xaml @@ -12,10 +12,12 @@ + IsLightDismissEnabled="True" + x:Uid="RenameDialogInvalidCharTip" + Title="错误" + Subtitle="名称不能为空且不能包含非法字符 非法字符包括 \ / : * ? " < > |" /> + + @@ -45,7 +47,8 @@ x:Name="FileOpen" Click="FileOpen_Click"> - + + Click="RunWithSystemAuthority_Click"> - + - + + Click="Cut_Click" /> - + - + - + + Click="OpenFolderInNewTab_Click"> - + - + @@ -262,12 +271,12 @@ Icon="Bookmarks" x:Uid="Zip" Name="CompressFolder" - Click="CompressFolder_Click"/> - + + Click="AddToLibray_Click" /> + + Click="CreateFile_Click" /> - + Click="Refresh_Click" /> - + - + + Click="UseSystemFileMananger_Click"> - + - + @@ -340,7 +360,7 @@ Label="属性" x:Uid="FolderAttribute" x:Name="ParentProperty" - Click="ParentProperty_Click"/> + Click="ParentProperty_Click" /> @@ -395,8 +415,8 @@ x:Name="Hash_Crc32" Header="Crc32" PlaceholderText="计算中..." - Width="245" - ToolTipService.ToolTip="{x:Bind Hash_Crc32.Text,Mode=OneWay}"/> + Width="245" + ToolTipService.ToolTip="{x:Bind Hash_Crc32.Text,Mode=OneWay}" /> @@ -463,8 +483,7 @@ x:DataType="Cls:FileSystemStorageItem"> - + PointerPressed="NameLabel_PointerPressed" /> + x:DeferLoadStrategy="Lazy" /> - - - - - - + - - - - + + + + @@ -607,7 +624,7 @@ FontSize="14" x:Uid="SearchModified" VerticalAlignment="Center" - TextTrimming="CharacterEllipsis"/> + TextTrimming="CharacterEllipsis" />