From 58f6e83452d535498d003c03bbecdb6f668647dc Mon Sep 17 00:00:00 2001 From: MikiraSora Date: Sat, 2 Mar 2024 23:29:08 +0800 Subject: [PATCH] use task to refactory AbortableThread --- OngekiFumenEditor/AppBootstrapper.cs | 65 +-- .../ViewModels/ProgramSettingViewModel.cs | 400 +++++++++--------- OngekiFumenEditor/Utils/AbortableThread.cs | 86 ++-- OngekiFumenEditor/Utils/ImageLoader.cs | 6 +- 4 files changed, 275 insertions(+), 282 deletions(-) diff --git a/OngekiFumenEditor/AppBootstrapper.cs b/OngekiFumenEditor/AppBootstrapper.cs index 389ed857..77893057 100644 --- a/OngekiFumenEditor/AppBootstrapper.cs +++ b/OngekiFumenEditor/AppBootstrapper.cs @@ -2,6 +2,7 @@ using ControlzEx.Standard; using Gemini.Framework.Services; using Gemini.Modules.Output; +using MahApps.Metro.Controls; using MahApps.Metro.Controls.Dialogs; using OngekiFumenEditor.Base; using OngekiFumenEditor.Base.Collections; @@ -262,7 +263,6 @@ private void InitIPCServer() }) { Name = "OngekiFumenEditorIPCThread", - IsBackground = true, }; ipcThread.Start(); } @@ -292,12 +292,16 @@ private void InitExceptionCatcher() { var recHandle = new HashSet(); - async void ProcessException(object sender, Exception exception) + async void ProcessException(object sender, Exception exception, string trigSource) { if (exceptionHandling) + { return; + } exceptionHandling = true; + await FileLogOutput.WriteLog($"trigged by {trigSource}"); + try { foreach (var visual in Application.Current.Windows.OfType()) @@ -332,41 +336,54 @@ void exceptionDump(Exception e, int level = 0) sb.AppendLine($"----------------------------"); await FileLogOutput.WriteLog(sb.ToString()); #if !DEBUG - var exceptionHandle = Marshal.GetExceptionPointers(); - var dumpFile = string.Empty; - if (exceptionHandle != IntPtr.Zero && !recHandle.Contains(exceptionHandle)) - { - dumpFile = DumpFileHelper.WriteMiniDump(exceptionHandle); - recHandle.Add(exceptionHandle); - } - await FileLogOutput.WriteLog("FumenRescue.Rescue() Begin"); - var resuceFolders = await FumenRescue.Rescue(); - await FileLogOutput.WriteLog("FumenRescue.Rescue() End"); - - var logFile = FileLogOutput.GetCurrentLogFile(); - - var exceptionWindow = new ExceptionTermWindow(innerMessage, resuceFolders, logFile, dumpFile); - exceptionWindow.ShowDialog(); - - exceptionHandling = true; - Environment.Exit(-1); + var exceptionHandle = Marshal.GetExceptionPointers(); + var dumpFile = string.Empty; + if (exceptionHandle != IntPtr.Zero && !recHandle.Contains(exceptionHandle)) + { + dumpFile = DumpFileHelper.WriteMiniDump(exceptionHandle); + recHandle.Add(exceptionHandle); + } + await FileLogOutput.WriteLog("FumenRescue.Rescue() Begin\n"); + var resuceFolders = await FumenRescue.Rescue(); + await FileLogOutput.WriteLog("FumenRescue.Rescue() End\n"); + + var logFile = FileLogOutput.GetCurrentLogFile(); + + var apartmentState = Thread.CurrentThread.GetApartmentState(); + await FileLogOutput.WriteLog($"current apartmentState: {apartmentState}\n"); + if (apartmentState == ApartmentState.STA) + { + var exceptionWindow = new ExceptionTermWindow(innerMessage, resuceFolders, logFile, dumpFile); + exceptionWindow.ShowDialog(); + } + else + { + var result = Application.Current.Invoke(() => + { + var exceptionWindow = new ExceptionTermWindow(innerMessage, resuceFolders, logFile, dumpFile); + return exceptionWindow.ShowDialog(); + }); + } + + exceptionHandling = true; + Environment.Exit(-1); #else throw exception; #endif } - AppDomain.CurrentDomain.UnhandledException += (sender, e) => + AppDomain.CurrentDomain.UnhandledException += async (sender, e) => { - ProcessException(sender, e.ExceptionObject as Exception); + ProcessException(sender, e.ExceptionObject as Exception, "AppDomain.CurrentDomain.UnhandledException"); }; Application.Current.DispatcherUnhandledException += (sender, e) => { - ProcessException(sender, e.Exception); + ProcessException(sender, e.Exception, "Application.Current.DispatcherUnhandledException"); e.Handled = true; }; TaskScheduler.UnobservedTaskException += (sender, e) => { - ProcessException(sender, e.Exception); + ProcessException(sender, e.Exception, "TaskScheduler.UnobservedTaskException"); e.SetObserved(); }; } diff --git a/OngekiFumenEditor/Kernel/SettingPages/Program/ViewModels/ProgramSettingViewModel.cs b/OngekiFumenEditor/Kernel/SettingPages/Program/ViewModels/ProgramSettingViewModel.cs index 3d0e42cd..1d0559b3 100644 --- a/OngekiFumenEditor/Kernel/SettingPages/Program/ViewModels/ProgramSettingViewModel.cs +++ b/OngekiFumenEditor/Kernel/SettingPages/Program/ViewModels/ProgramSettingViewModel.cs @@ -9,207 +9,209 @@ using System.IO; using System.Reflection; using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; namespace OngekiFumenEditor.Kernel.SettingPages.Program.ViewModels { - [Export(typeof(ISettingsEditor))] - [PartCreationPolicy(CreationPolicy.Shared)] - public class ProgramSettingViewModel : PropertyChangedBase, ISettingsEditor - { - public ProgramSetting Setting => ProgramSetting.Default; - - public bool EnableAssociate => !AssociationUtility.IsRegistered("OngekiFumenEditor", "NyagekiFumenProject"); - - private bool enableAssociateNyagekiProj = true; - public bool EnableAssociateNyagekiProj - { - get => enableAssociateNyagekiProj; - set => Set(ref enableAssociateNyagekiProj, value); - } - - private bool enableAssociateNyageki = true; - public bool EnableAssociateNyageki - { - get => enableAssociateNyageki; - set => Set(ref enableAssociateNyageki, value); - } - - private bool enableAssociateOgkr = true; - public bool EnableAssociateOgkr - { - get => enableAssociateOgkr; - set => Set(ref enableAssociateOgkr, value); - } - - public ProgramSettingViewModel() - { - Setting.PropertyChanged += SettingPropertyChanged; - } - - private void SettingPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - Log.LogDebug($"logs setting property changed : {e.PropertyName}"); - } - - public string SettingsPageName => Resources.TabProgram; - - public string SettingsPagePath => Resources.TabEnviorment; - - public void ApplyChanges() - { - Setting.Save(); - } - - public void OnDumpFolderPathButtonClick() - { - using var openFolderDialog = new FolderBrowserDialog(); - openFolderDialog.ShowNewFolderButton = true; - openFolderDialog.SelectedPath = Path.GetFullPath(Setting.DumpFileDirPath); - if (openFolderDialog.ShowDialog() == DialogResult.OK) - { - var folderPath = openFolderDialog.SelectedPath; - if (!Directory.Exists(folderPath)) - { - MessageBox.Show(Resources.ErrorFolderIsEmpty); - OnDumpFolderPathButtonClick(); - return; - } - Setting.DumpFileDirPath = folderPath; - ApplyChanges(); - } - } - - public void ThrowException() - { - throw new Exception("塔塔开!"); - } - - public async void RegisterNyagekiAssociations() - { - var iconFolder = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "Resources", "FileAssociationIcons"); - var iconFilePath = Path.Combine(iconFolder, "icon.ico"); - - if (!File.Exists(iconFilePath)) - { - Directory.CreateDirectory(iconFolder); - var streamInfo = System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/OngekiFumenEditor;component/Resources/Icons/logo32.ico")); - using var fs = streamInfo.Stream; - using var fs2 = File.OpenWrite(iconFilePath); - await fs.CopyToAsync(fs2); - } - - var software = new Software - { - Name = "OngekiFumenEditor", - CompanyName = "NyagekiFumenProject", - Description = "Make Offgeki Great Again!", - Icon = iconFilePath, - }; - - if (EnableAssociateNyagekiProj) - { - software.Identifiers.Add(new ProgrammaticID - { - Type = new FileType - { - Extension = ".nyagekiProj", - ContentType = "application/sample", - PerceivedType = PerceivedTypes.Application, - }, - Command = new ShellCommand - { - Path = Application.ExecutablePath, - Argument = "%1" - }, - Description = "Ongeki Fumen Editor Fumen Project File", - Icon = iconFilePath, - }); - } - - if (EnableAssociateNyageki) - { - software.Identifiers.Add(new ProgrammaticID - { - Type = new FileType - { - Extension = ".nyageki", - ContentType = "application/sample", - PerceivedType = PerceivedTypes.Application, - }, - Command = new ShellCommand - { - Path = Application.ExecutablePath, - Argument = "%1" - }, - Description = "Ongeki Fumen Editor Fumen File", - Icon = iconFilePath, - }); - } - - if (EnableAssociateOgkr) - { - software.Identifiers.Add(new ProgrammaticID - { - Type = new FileType - { - Extension = ".ogkr", - ContentType = "application/sample", - PerceivedType = PerceivedTypes.Application, - }, - Command = new ShellCommand - { - Path = Application.ExecutablePath, - Argument = "%1" - }, - Description = "Ongeki Fumen File", - Icon = iconFilePath, - }); - } - - if (software.Identifiers.Count == 0) - { - MessageBox.Show(Resources.RegisterOneFileTypeAtLeast, Resources.FileAssociation); - return; - } - - try - { - var content = JsonSerializer.Serialize(software); - Log.LogDebug($"software = {content}"); - - if (AssociationUtility.Register(software)) - MessageBox.Show(Resources.RegisterSuccess, Resources.FileAssociation); - else - MessageBox.Show(Resources.RegisterFail, Resources.FileAssociation); - } - catch (UnauthorizedAccessException) - { - MessageBox.Show(Resources.RequestAdminPermission, Resources.FileAssociation); - } - - NotifyOfPropertyChange(() => EnableAssociate); - } - - public void UnRegisterNyagekiAssociations() - { - try - { - if (AssociationUtility.Unregister("OngekiFumenEditor", "NyagekiFumenProject")) - MessageBox.Show(Resources.UnregisterSuccess, Resources.FileAssociation); - else - MessageBox.Show(Resources.UnregisterFail, Resources.FileAssociation); - } - catch (UnauthorizedAccessException) - { - MessageBox.Show(Resources.RequestAdminPermission, Resources.FileAssociation); - } - catch - { - MessageBox.Show(Resources.UnregisterFail, Resources.FileAssociation); - } - - NotifyOfPropertyChange(() => EnableAssociate); - } - } + [Export(typeof(ISettingsEditor))] + [PartCreationPolicy(CreationPolicy.Shared)] + public class ProgramSettingViewModel : PropertyChangedBase, ISettingsEditor + { + public ProgramSetting Setting => ProgramSetting.Default; + + public bool EnableAssociate => !AssociationUtility.IsRegistered("OngekiFumenEditor", "NyagekiFumenProject"); + + private bool enableAssociateNyagekiProj = true; + public bool EnableAssociateNyagekiProj + { + get => enableAssociateNyagekiProj; + set => Set(ref enableAssociateNyagekiProj, value); + } + + private bool enableAssociateNyageki = true; + public bool EnableAssociateNyageki + { + get => enableAssociateNyageki; + set => Set(ref enableAssociateNyageki, value); + } + + private bool enableAssociateOgkr = true; + public bool EnableAssociateOgkr + { + get => enableAssociateOgkr; + set => Set(ref enableAssociateOgkr, value); + } + + public ProgramSettingViewModel() + { + Setting.PropertyChanged += SettingPropertyChanged; + } + + private void SettingPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + Log.LogDebug($"logs setting property changed : {e.PropertyName}"); + } + + public string SettingsPageName => Resources.TabProgram; + + public string SettingsPagePath => Resources.TabEnviorment; + + public void ApplyChanges() + { + Setting.Save(); + } + + public void OnDumpFolderPathButtonClick() + { + using var openFolderDialog = new FolderBrowserDialog(); + openFolderDialog.ShowNewFolderButton = true; + openFolderDialog.SelectedPath = Path.GetFullPath(Setting.DumpFileDirPath); + if (openFolderDialog.ShowDialog() == DialogResult.OK) + { + var folderPath = openFolderDialog.SelectedPath; + if (!Directory.Exists(folderPath)) + { + MessageBox.Show(Resources.ErrorFolderIsEmpty); + OnDumpFolderPathButtonClick(); + return; + } + Setting.DumpFileDirPath = folderPath; + ApplyChanges(); + } + } + + public void ThrowException() + { + Task.Run(() => throw new Exception("塔塔开!")); + } + + public async void RegisterNyagekiAssociations() + { + var iconFolder = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "Resources", "FileAssociationIcons"); + var iconFilePath = Path.Combine(iconFolder, "icon.ico"); + + if (!File.Exists(iconFilePath)) + { + Directory.CreateDirectory(iconFolder); + var streamInfo = System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/OngekiFumenEditor;component/Resources/Icons/logo32.ico")); + using var fs = streamInfo.Stream; + using var fs2 = File.OpenWrite(iconFilePath); + await fs.CopyToAsync(fs2); + } + + var software = new Software + { + Name = "OngekiFumenEditor", + CompanyName = "NyagekiFumenProject", + Description = "Make Offgeki Great Again!", + Icon = iconFilePath, + }; + + if (EnableAssociateNyagekiProj) + { + software.Identifiers.Add(new ProgrammaticID + { + Type = new FileType + { + Extension = ".nyagekiProj", + ContentType = "application/sample", + PerceivedType = PerceivedTypes.Application, + }, + Command = new ShellCommand + { + Path = Application.ExecutablePath, + Argument = "%1" + }, + Description = "Ongeki Fumen Editor Fumen Project File", + Icon = iconFilePath, + }); + } + + if (EnableAssociateNyageki) + { + software.Identifiers.Add(new ProgrammaticID + { + Type = new FileType + { + Extension = ".nyageki", + ContentType = "application/sample", + PerceivedType = PerceivedTypes.Application, + }, + Command = new ShellCommand + { + Path = Application.ExecutablePath, + Argument = "%1" + }, + Description = "Ongeki Fumen Editor Fumen File", + Icon = iconFilePath, + }); + } + + if (EnableAssociateOgkr) + { + software.Identifiers.Add(new ProgrammaticID + { + Type = new FileType + { + Extension = ".ogkr", + ContentType = "application/sample", + PerceivedType = PerceivedTypes.Application, + }, + Command = new ShellCommand + { + Path = Application.ExecutablePath, + Argument = "%1" + }, + Description = "Ongeki Fumen File", + Icon = iconFilePath, + }); + } + + if (software.Identifiers.Count == 0) + { + MessageBox.Show(Resources.RegisterOneFileTypeAtLeast, Resources.FileAssociation); + return; + } + + try + { + var content = JsonSerializer.Serialize(software); + Log.LogDebug($"software = {content}"); + + if (AssociationUtility.Register(software)) + MessageBox.Show(Resources.RegisterSuccess, Resources.FileAssociation); + else + MessageBox.Show(Resources.RegisterFail, Resources.FileAssociation); + } + catch (UnauthorizedAccessException) + { + MessageBox.Show(Resources.RequestAdminPermission, Resources.FileAssociation); + } + + NotifyOfPropertyChange(() => EnableAssociate); + } + + public void UnRegisterNyagekiAssociations() + { + try + { + if (AssociationUtility.Unregister("OngekiFumenEditor", "NyagekiFumenProject")) + MessageBox.Show(Resources.UnregisterSuccess, Resources.FileAssociation); + else + MessageBox.Show(Resources.UnregisterFail, Resources.FileAssociation); + } + catch (UnauthorizedAccessException) + { + MessageBox.Show(Resources.RequestAdminPermission, Resources.FileAssociation); + } + catch + { + MessageBox.Show(Resources.UnregisterFail, Resources.FileAssociation); + } + + NotifyOfPropertyChange(() => EnableAssociate); + } + } } diff --git a/OngekiFumenEditor/Utils/AbortableThread.cs b/OngekiFumenEditor/Utils/AbortableThread.cs index 67c7e3a9..e2a8fc78 100644 --- a/OngekiFumenEditor/Utils/AbortableThread.cs +++ b/OngekiFumenEditor/Utils/AbortableThread.cs @@ -1,60 +1,38 @@ using System; using System.Threading; +using System.Threading.Tasks; namespace OngekiFumenEditor.Utils { - public class AbortableThread - { - private Thread thread; - private CancellationTokenSource cancellationTokenSource; - - public CancellationToken CancellationToken => cancellationTokenSource.Token; - - public AbortableThread(Action cancellableMethod) - { - cancellationTokenSource = new CancellationTokenSource(); - thread = new Thread(() => cancellableMethod?.Invoke(cancellationTokenSource.Token)); - Name = $"AbortableThread:{cancellableMethod}"; - } - - public bool IsBackground - { - get - { - return thread.IsBackground; - } - - set - { - thread.IsBackground = value; - } - } - - public string Name - { - get - { - return thread.Name; - } - set - { - thread.Name = value; - } - } - - public void Start() - { - thread.Start(); - Log.LogDebug($"Thread {Name} started.", prefix: "AbortableThread"); - } - - public void Abort(bool waitForTask = true) - { - Log.LogDebug($"Begin to abort thread {Name}.", prefix: "AbortableThread"); - cancellationTokenSource.Cancel(); - if (waitForTask) - thread?.Join(); - Log.LogDebug($"Aborted thread {Name}.", prefix: "AbortableThread"); - } - } + public class AbortableThread + { + private CancellationTokenSource cancellationTokenSource; + private Task task; + + public CancellationToken CancellationToken => cancellationTokenSource.Token; + + public AbortableThread(Action cancellableMethod) + { + cancellationTokenSource = new CancellationTokenSource(); + task = new Task(() => cancellableMethod?.Invoke(CancellationToken), CancellationToken, TaskCreationOptions.LongRunning); + Name = $"AbortableThread:{cancellableMethod}"; + } + + public string Name { get; set; } + + public void Start() + { + task.Start(); + Log.LogDebug($"Thread {Name} started.", prefix: "AbortableThread"); + } + + public void Abort(bool waitForTask = true) + { + Log.LogDebug($"Begin to abort thread {Name}.", prefix: "AbortableThread"); + cancellationTokenSource.Cancel(); + if (waitForTask) + task?.Wait(); + Log.LogDebug($"Aborted thread {Name}.", prefix: "AbortableThread"); + } + } } diff --git a/OngekiFumenEditor/Utils/ImageLoader.cs b/OngekiFumenEditor/Utils/ImageLoader.cs index cb5e1cbf..e8d71948 100644 --- a/OngekiFumenEditor/Utils/ImageLoader.cs +++ b/OngekiFumenEditor/Utils/ImageLoader.cs @@ -29,11 +29,7 @@ public class ImageLoader public ImageLoader() { - new Thread(PrcessQueue) - { - Name = "DefaultImageLoader.PrcessQueue()", - IsBackground = true - }.Start(); + Task.Run(PrcessQueue); } public Task LoadImage(string url, CancellationToken cancellationToken)