From 1e5f0dba4ea3481cb4d146cec4530704aa2a9d75 Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Sat, 1 Apr 2023 08:27:30 -0400 Subject: [PATCH] Added method interrupt handling. --- SrcMod/Shell/Interop/Winmm.cs | 26 +++++----- SrcMod/Shell/Shell.cs | 92 +++++++++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 33 deletions(-) diff --git a/SrcMod/Shell/Interop/Winmm.cs b/SrcMod/Shell/Interop/Winmm.cs index 58a6d67..ac1e97a 100644 --- a/SrcMod/Shell/Interop/Winmm.cs +++ b/SrcMod/Shell/Interop/Winmm.cs @@ -8,18 +8,18 @@ internal static partial class Winmm public enum PlaySoundFlags : uint { - SND_SYNC = 0x00000000, - SND_ASYNC = 0x00000001, - SND_NODEFAULT = 0x00000002, - SND_MEMORY = 0x00000004, - SND_LOOP = 0x00000008, - SND_NOSTOP = 0x00000010, - SND_PURGE = 0x00000040, - SND_APPLICATION = 0x00000080, - SND_NOWAIT = 0x00002000, - SND_ALIAS = 0x00010000, - SND_FILENAME = 0x00020000, - SND_RESOURCE = 0x00040000, - SND_ALIAS_ID = 0x00100000, + Sync = 0x00000000, + Async = 0x00000001, + NoDefault = 0x00000002, + Memory = 0x00000004, + Loop = 0x00000008, + NoStop = 0x00000010, + Purge = 0x00000040, + Application = 0x00000080, + NoWait = 0x00002000, + Alias = 0x00010000, + FileName = 0x00020000, + Resource = 0x00040000, + AliasId = 0x00100000, } } diff --git a/SrcMod/Shell/Shell.cs b/SrcMod/Shell/Shell.cs index 02c25e1..4cb5190 100644 --- a/SrcMod/Shell/Shell.cs +++ b/SrcMod/Shell/Shell.cs @@ -1,10 +1,12 @@ -namespace SrcMod.Shell; +using System.ComponentModel; + +namespace SrcMod.Shell; public class Shell { public const string Author = "That_One_Nerd"; public const string Name = "SrcMod"; - public const string Version = "Alpha 0.3.0"; + public const string Version = "Alpha 0.3.1"; public readonly string? ShellDirectory; @@ -19,6 +21,8 @@ public class Shell private bool lastCancel; private bool printedCancel; + private BackgroundWorker? activeCommand; + public Shell() { Console.CursorVisible = false; @@ -83,6 +87,7 @@ public class Shell Write($"{Author}", ConsoleColor.DarkYellow); lastCancel = false; + activeCommand = null; Console.CancelKeyPress += HandleCancel; ActiveGame = null; @@ -131,10 +136,7 @@ public class Shell Console.CursorTop -= 3; Write("Press ^C again to exit the shell.", ConsoleColor.Red); - // Send a warning sound. - - Winmm.PlaySound("SystemAsterisk", nint.Zero, - (uint)(Winmm.PlaySoundFlags.SND_ALIAS | Winmm.PlaySoundFlags.SND_ASYNC)); + PlayWarningSound(); printedCancel = true; Console.CursorTop += 2; @@ -213,24 +215,41 @@ public class Shell int start = module.NameIsPrefix ? 2 : 1; string[] args = parts.GetRange(start, parts.Count - start).ToArray(); + void runCommand(object? sender, DoWorkEventArgs e) + { #if RELEASE - try - { + try + { #endif - command.Invoke(args); + command.Invoke(args); #if RELEASE - } - catch (TargetInvocationException ex) - { - Write($"[ERROR] {ex.InnerException!.Message}", ConsoleColor.Red); - if (LoadingBarEnabled) LoadingBarEnd(); - } - catch (Exception ex) - { - Write($"[ERROR] {ex.Message}", ConsoleColor.Red); - if (LoadingBarEnabled) LoadingBarEnd(); - } + } + catch (TargetInvocationException ex) + { + Write($"[ERROR] {ex.InnerException!.Message}", ConsoleColor.Red); + if (LoadingBarEnabled) LoadingBarEnd(); + } + catch (Exception ex) + { + Write($"[ERROR] {ex.Message}", ConsoleColor.Red); + if (LoadingBarEnabled) LoadingBarEnd(); + } #endif + } + + activeCommand = new(); + activeCommand.DoWork += runCommand; + activeCommand.RunWorkerAsync(); + + activeCommand.WorkerSupportsCancellation = true; + + while (activeCommand is not null && activeCommand.IsBusy) Thread.Yield(); + + if (activeCommand is not null) + { + activeCommand.Dispose(); + activeCommand = null; + } return; } } @@ -238,6 +257,17 @@ public class Shell Write($"[ERROR] Could not find command \"{cmd}\".", ConsoleColor.Red); } + private static void PlayErrorSound() + { + Winmm.PlaySound("SystemHand", nint.Zero, + (uint)(Winmm.PlaySoundFlags.Alias | Winmm.PlaySoundFlags.Async)); + } + private static void PlayWarningSound() + { + Winmm.PlaySound("SystemAsterisk", nint.Zero, + (uint)(Winmm.PlaySoundFlags.Alias | Winmm.PlaySoundFlags.Async)); + } + public void ReloadDirectoryInfo() { ActiveMod = Mod.ReadDirectory(WorkingDirectory); @@ -250,6 +280,28 @@ public class Shell private void HandleCancel(object? sender, ConsoleCancelEventArgs args) { + if (activeCommand is not null && activeCommand.IsBusy) + { + if (activeCommand.WorkerSupportsCancellation) + { + // Kill the active command. + activeCommand.CancelAsync(); + activeCommand.Dispose(); + activeCommand = null; + } + else + { + // Command doesn't support cancellation. + // Warn the user. + PlayErrorSound(); + } + + lastCancel = false; + printedCancel = false; + args.Cancel = true; + return; + } + // Due to some funny multithreading issues, we want to make the warning label // single-threaded on the shell. if (!lastCancel)