From 4eb7697079a66b2df687dde86b5bfbdce37c4735 Mon Sep 17 00:00:00 2001 From: mor39a <89531894+mor39a@users.noreply.github.com> Date: Fri, 8 Aug 2025 22:15:01 -0500 Subject: [PATCH 1/9] Fix launch at startup for app in Microsoft Store Additionally, the space in executable was removed to avoid errors. --- RunCat365/ContextMenuManager.cs | 87 +++++++++++------------- RunCat365/Program.cs | 48 ++++++++++++- RunCat365/RunCat365.csproj | 2 +- WapForStore/Package.StoreAssociation.xml | 2 +- WapForStore/Package.appxmanifest | 20 ++++-- 5 files changed, 105 insertions(+), 54 deletions(-) diff --git a/RunCat365/ContextMenuManager.cs b/RunCat365/ContextMenuManager.cs index 979139c3..d348a032 100644 --- a/RunCat365/ContextMenuManager.cs +++ b/RunCat365/ContextMenuManager.cs @@ -15,10 +15,8 @@ using RunCat365.Properties; using System.ComponentModel; -namespace RunCat365 -{ - internal class ContextMenuManager - { +namespace RunCat365 { + internal class ContextMenuManager { private readonly CustomToolStripMenuItem systemInfoMenu = new(); private readonly NotifyIcon notifyIcon = new(); private readonly List icons = []; @@ -33,18 +31,18 @@ internal ContextMenuManager( Action setManualTheme, Func getFPSMaxLimit, Action setFPSMaxLimit, + Func getStartup, + Func toggleStartup, Action openRepository, Action onExit - ) - { + ) { systemInfoMenu.Text = "-\n-\n-\n-\n-"; systemInfoMenu.Enabled = false; var runnersMenu = new CustomToolStripMenuItem("Runners"); runnersMenu.SetupSubMenusFromEnum( r => r.GetString(), - (parent, sender, e) => - { + (parent, sender, e) => { HandleMenuItemSelection( parent, sender, @@ -60,8 +58,7 @@ Action onExit var themeMenu = new CustomToolStripMenuItem("Theme"); themeMenu.SetupSubMenusFromEnum( t => t.GetString(), - (parent, sender, e) => - { + (parent, sender, e) => { HandleMenuItemSelection( parent, sender, @@ -77,8 +74,7 @@ Action onExit var fpsMaxLimitMenu = new CustomToolStripMenuItem("FPS Max Limit"); fpsMaxLimitMenu.SetupSubMenusFromEnum( f => f.GetString(), - (parent, sender, e) => - { + (parent, sender, e) => { HandleMenuItemSelection( parent, sender, @@ -91,10 +87,16 @@ Action onExit _ => null ); + var startupMenu = new CustomToolStripMenuItem("Launch at startup") { + Checked = getStartup() + }; + startupMenu.Click += (sender, e) => HandleStartupMenuClick(sender, toggleStartup); + var settingsMenu = new CustomToolStripMenuItem("Settings"); settingsMenu.DropDownItems.AddRange( themeMenu, - fpsMaxLimitMenu + fpsMaxLimitMenu, + startupMenu ); var endlessGameMenu = new CustomToolStripMenuItem("Endless Game"); @@ -102,8 +104,7 @@ Action onExit var appVersionMenu = new CustomToolStripMenuItem( $"{Application.ProductName} v{Application.ProductVersion}" - ) - { + ) { Enabled = false }; @@ -146,37 +147,31 @@ private static void HandleMenuItemSelection( object? sender, CustomTryParseDelegate tryParseMethod, Action assignValueAction - ) - { + ) { if (sender is null) return; var item = (ToolStripMenuItem)sender; - foreach (ToolStripMenuItem childItem in parentMenu.DropDownItems) - { + foreach (ToolStripMenuItem childItem in parentMenu.DropDownItems) { childItem.Checked = false; } item.Checked = true; - if (tryParseMethod(item.Text, out T parsedValue)) - { + if (tryParseMethod(item.Text, out T parsedValue)) { assignValueAction(parsedValue); } } - private static Bitmap? GetRunnerThumbnailBitmap(Theme systemTheme, Runner runner) - { + private static Bitmap? GetRunnerThumbnailBitmap(Theme systemTheme, Runner runner) { var iconName = $"{systemTheme.GetString()}_{runner.GetString()}_0".ToLower(); var obj = Resources.ResourceManager.GetObject(iconName); return obj is Icon icon ? icon.ToBitmap() : null; } - internal void SetIcons(Theme systemTheme, Theme manualTheme, Runner runner) - { + internal void SetIcons(Theme systemTheme, Theme manualTheme, Runner runner) { var prefix = (manualTheme == Theme.System ? systemTheme : manualTheme).GetString(); var runnerName = runner.GetString(); var rm = Resources.ResourceManager; var capacity = runner.GetFrameNumber(); var list = new List(capacity); - for (int i = 0; i < capacity; i++) - { + for (int i = 0; i < capacity; i++) { var iconName = $"{prefix}_{runnerName}_{i}".ToLower(); var icon = rm.GetObject(iconName); if (icon is null) continue; @@ -187,53 +182,51 @@ internal void SetIcons(Theme systemTheme, Theme manualTheme, Runner runner) icons.AddRange(list); } - private void ShowOrActivateGameWindow(Func getSystemTheme) - { - if (endlessGameForm is null) - { + private static void HandleStartupMenuClick(object? sender, Func toggleStartup) { + if (sender is null) return; + var item = (ToolStripMenuItem)sender; + if (toggleStartup(item.Checked)) { + item.Checked = !item.Checked; + } + } + + private void ShowOrActivateGameWindow(Func getSystemTheme) { + if (endlessGameForm is null) { endlessGameForm = new EndlessGameForm(getSystemTheme()); - endlessGameForm.FormClosed += (sender, e) => - { + endlessGameForm.FormClosed += (sender, e) => { endlessGameForm = null; }; endlessGameForm.Show(); - } - else - { + } else { endlessGameForm.Activate(); } } - internal void ShowBalloonTip() - { + internal void ShowBalloonTip() { var message = "App has launched. " + "If the icon is not on the taskbar, it has been omitted, " + "so please move it manually and pin it."; notifyIcon.ShowBalloonTip(5000, "RunCat 365", message, ToolTipIcon.Info); } - internal void AdvanceFrame() - { + internal void AdvanceFrame() { if (icons.Count <= current) current = 0; notifyIcon.Icon = icons[current]; current = (current + 1) % icons.Count; } - internal void SetSystemInfoMenuText(string text) - { + internal void SetSystemInfoMenuText(string text) { systemInfoMenu.Text = text; } - internal void SetNotifyIconText(string text) - { + internal void SetNotifyIconText(string text) { notifyIcon.Text = text; } - internal void HideNotifyIcon() - { + internal void HideNotifyIcon() { notifyIcon.Visible = false; } private delegate bool CustomTryParseDelegate(string? value, out T result); } -} +} \ No newline at end of file diff --git a/RunCat365/Program.cs b/RunCat365/Program.cs index e7b538a0..de76ddb6 100644 --- a/RunCat365/Program.cs +++ b/RunCat365/Program.cs @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -using FormsTimer = System.Windows.Forms.Timer; using Microsoft.Win32; using RunCat365.Properties; using System.Diagnostics; +using Windows.ApplicationModel; +using FormsTimer = System.Windows.Forms.Timer; namespace RunCat365 { @@ -56,6 +57,7 @@ public class RunCat365ApplicationContext : ApplicationContext private Theme manualTheme = Theme.System; private FPSMaxLimit fpsMaxLimit = FPSMaxLimit.FPS40; private int fetchCounter = 5; + private static StartupTask? startupTask; public RunCat365ApplicationContext() { @@ -79,6 +81,8 @@ public RunCat365ApplicationContext() t => manualTheme = t, () => fpsMaxLimit, f => fpsMaxLimit = f, + () => GetStartupAsync().Result, + s => SetStartupAsync(s).Result, () => OpenRepository(), () => Exit() ); @@ -111,6 +115,48 @@ private static Theme GetSystemTheme() return (int)value == 0 ? Theme.Dark : Theme.Light; } + private static async Task GetStartupAsync() { + if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); + if (startupTask is null) return false; + if (startupTask.State == StartupTaskState.Enabled) return true; + return false; + + } + + private static async Task SetStartupAsync(bool isChecked) { + bool active = !isChecked; + bool changeCheck = false; + if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); + if (active) { + switch (startupTask.State) { + case StartupTaskState.Enabled: + changeCheck = true; + break; + case StartupTaskState.Disabled: + StartupTaskState newState = await startupTask.RequestEnableAsync(); + if (newState == StartupTaskState.Enabled) { + changeCheck = true; + } else { + MessageBox.Show("Launch at Startup could not be activated.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + changeCheck = false; + } + break; + case StartupTaskState.DisabledByUser: + MessageBox.Show("Launch at startup was disabled by the user, enable it in Task Manager > Startup, search RunCat365 and enable it.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + changeCheck = false; + break; + case StartupTaskState.DisabledByPolicy: + MessageBox.Show("Launch at startup was disabled by policy.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + changeCheck = false; + break; + } + } else if (!active) { + if (startupTask.State == StartupTaskState.Enabled) startupTask.Disable(); + changeCheck = true; + } + return changeCheck; + } + private void ShowBalloonTip() { if (UserSettings.Default.FirstLaunch) diff --git a/RunCat365/RunCat365.csproj b/RunCat365/RunCat365.csproj index b73177a3..fe4df9c5 100644 --- a/RunCat365/RunCat365.csproj +++ b/RunCat365/RunCat365.csproj @@ -7,7 +7,7 @@ true enable 10.0.19041.0 - RunCat 365 + RunCat365 resources\app_icon.ico Takuto Nakamura Studio Kyome diff --git a/WapForStore/Package.StoreAssociation.xml b/WapForStore/Package.StoreAssociation.xml index b30f0a7c..34dd1577 100644 --- a/WapForStore/Package.StoreAssociation.xml +++ b/WapForStore/Package.StoreAssociation.xml @@ -363,7 +363,7 @@ StudioKyome.RunCat - RunCat 365 + RunCat365 diff --git a/WapForStore/Package.appxmanifest b/WapForStore/Package.appxmanifest index 34e439c9..73f5b189 100644 --- a/WapForStore/Package.appxmanifest +++ b/WapForStore/Package.appxmanifest @@ -3,16 +3,18 @@ + IgnorableNamespaces="uap uap5 desktop rescap"> + Version="3.0.3.0" /> - RunCat 365 + RunCat365 Studio Kyome Images\StoreLogo.png @@ -31,7 +33,7 @@ Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$"> + + + + + + From 4ee8bc32706be1313d61fc5fa1e1ae31bea825f7 Mon Sep 17 00:00:00 2001 From: mor39a <89531894+mor39a@users.noreply.github.com> Date: Fri, 8 Aug 2025 22:32:42 -0500 Subject: [PATCH 2/9] Added Launch at Startup option when not installed from Microsoft Store. --- RunCat365/Program.cs | 99 ++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/RunCat365/Program.cs b/RunCat365/Program.cs index de76ddb6..a39ddb0b 100644 --- a/RunCat365/Program.cs +++ b/RunCat365/Program.cs @@ -116,45 +116,80 @@ private static Theme GetSystemTheme() } private static async Task GetStartupAsync() { - if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); - if (startupTask is null) return false; - if (startupTask.State == StartupTaskState.Enabled) return true; - return false; - + if (IsRunningAsPackaged()) { + if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); + if (startupTask is null) return false; + if (startupTask.State == StartupTaskState.Enabled) return true; + return false; + } else { + var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; + using var rKey = Registry.CurrentUser.OpenSubKey(keyName); + if (rKey is null) return false; + var value = (rKey.GetValue(Application.ProductName) is not null); + rKey.Close(); + return value; + } } private static async Task SetStartupAsync(bool isChecked) { - bool active = !isChecked; - bool changeCheck = false; - if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); - if (active) { - switch (startupTask.State) { - case StartupTaskState.Enabled: - changeCheck = true; - break; - case StartupTaskState.Disabled: - StartupTaskState newState = await startupTask.RequestEnableAsync(); - if (newState == StartupTaskState.Enabled) { + if (IsRunningAsPackaged()) { + bool active = !isChecked; + bool changeCheck = false; + if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); + if (active) { + switch (startupTask.State) { + case StartupTaskState.Enabled: changeCheck = true; - } else { - MessageBox.Show("Launch at Startup could not be activated.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + break; + case StartupTaskState.Disabled: + StartupTaskState newState = await startupTask.RequestEnableAsync(); + if (newState == StartupTaskState.Enabled) { + changeCheck = true; + } else { + MessageBox.Show("Launch at Startup could not be activated.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + changeCheck = false; + } + break; + case StartupTaskState.DisabledByUser: + MessageBox.Show("Launch at startup was disabled by the user, enable it in Task Manager > Startup, search RunCat365 and enable it.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); changeCheck = false; - } - break; - case StartupTaskState.DisabledByUser: - MessageBox.Show("Launch at startup was disabled by the user, enable it in Task Manager > Startup, search RunCat365 and enable it.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - changeCheck = false; - break; - case StartupTaskState.DisabledByPolicy: - MessageBox.Show("Launch at startup was disabled by policy.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - changeCheck = false; - break; + break; + case StartupTaskState.DisabledByPolicy: + MessageBox.Show("Launch at startup was disabled by policy.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + changeCheck = false; + break; + } + } else if (!active) { + if (startupTask.State == StartupTaskState.Enabled) startupTask.Disable(); + changeCheck = true; + } + return changeCheck; + } else { + var productName = Application.ProductName; + if (productName is null) return false; + var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; + using var rKey = Registry.CurrentUser.OpenSubKey(keyName, true); + if (rKey is null) return false; + if (isChecked) { + rKey.DeleteValue(productName, false); + } else { + var fileName = Environment.ProcessPath; + if (fileName != null) { + rKey.SetValue(productName, fileName); + } } - } else if (!active) { - if (startupTask.State == StartupTaskState.Enabled) startupTask.Disable(); - changeCheck = true; + rKey.Close(); + return true; + } + } + + private static bool IsRunningAsPackaged() { + try { + var _ = Package.Current; + return true; + } catch { + return false; } - return changeCheck; } private void ShowBalloonTip() From 547fd042e6ebffc776138ee59ce9928620dc04b3 Mon Sep 17 00:00:00 2001 From: Santiago Mora <89531894+mor39a@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:26:36 -0500 Subject: [PATCH 3/9] Apply suggestion from @Kyome22 Co-authored-by: Takuto NAKAMURA (Kyome) --- RunCat365/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RunCat365/Program.cs b/RunCat365/Program.cs index a39ddb0b..542cd0c1 100644 --- a/RunCat365/Program.cs +++ b/RunCat365/Program.cs @@ -133,8 +133,8 @@ private static async Task GetStartupAsync() { private static async Task SetStartupAsync(bool isChecked) { if (IsRunningAsPackaged()) { - bool active = !isChecked; - bool changeCheck = false; + var active = !isChecked; + var changeCheck = false; if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); if (active) { switch (startupTask.State) { From 2d0c9b7cb21fbc93f5103b16bfd7b00d255d48a7 Mon Sep 17 00:00:00 2001 From: Santiago Mora <89531894+mor39a@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:28:33 -0500 Subject: [PATCH 4/9] Apply suggestion from @Kyome22 Co-authored-by: Takuto NAKAMURA (Kyome) --- RunCat365/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RunCat365/Program.cs b/RunCat365/Program.cs index 542cd0c1..b788968c 100644 --- a/RunCat365/Program.cs +++ b/RunCat365/Program.cs @@ -159,7 +159,7 @@ private static async Task SetStartupAsync(bool isChecked) { changeCheck = false; break; } - } else if (!active) { + } else { if (startupTask.State == StartupTaskState.Enabled) startupTask.Disable(); changeCheck = true; } From de8d3fb771d87bd5b8367fabddccaca1de88bbab Mon Sep 17 00:00:00 2001 From: mor39a <89531894+mor39a@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:48:09 -0500 Subject: [PATCH 5/9] Return the name from RunCat365 to RunCat 365 --- RunCat365/RunCat365.csproj | 2 +- WapForStore/Package.StoreAssociation.xml | 2 +- WapForStore/Package.appxmanifest | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/RunCat365/RunCat365.csproj b/RunCat365/RunCat365.csproj index fe4df9c5..b73177a3 100644 --- a/RunCat365/RunCat365.csproj +++ b/RunCat365/RunCat365.csproj @@ -7,7 +7,7 @@ true enable 10.0.19041.0 - RunCat365 + RunCat 365 resources\app_icon.ico Takuto Nakamura Studio Kyome diff --git a/WapForStore/Package.StoreAssociation.xml b/WapForStore/Package.StoreAssociation.xml index 34dd1577..b30f0a7c 100644 --- a/WapForStore/Package.StoreAssociation.xml +++ b/WapForStore/Package.StoreAssociation.xml @@ -363,7 +363,7 @@ StudioKyome.RunCat - RunCat365 + RunCat 365 diff --git a/WapForStore/Package.appxmanifest b/WapForStore/Package.appxmanifest index 73f5b189..4b03ffcb 100644 --- a/WapForStore/Package.appxmanifest +++ b/WapForStore/Package.appxmanifest @@ -14,7 +14,7 @@ Version="3.0.3.0" /> - RunCat365 + RunCat 365 Studio Kyome Images\StoreLogo.png @@ -33,7 +33,7 @@ Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$"> + DisplayName="RunCat 365"/> From e9566e049e3c5119d10c37a5e1de273cbddff8d1 Mon Sep 17 00:00:00 2001 From: mor39a <89531894+mor39a@users.noreply.github.com> Date: Tue, 12 Aug 2025 21:09:57 -0500 Subject: [PATCH 6/9] Refactoring startup Move functions to new class. The logic of the packaged app is separated from the unpackaged app. --- RunCat365/ContextMenuManager.cs | 78 +++++++++++------ RunCat365/Program.cs | 83 +----------------- RunCat365/StartupManager.cs | 145 +++++++++++++++++++++++++++++++ WapForStore/Package.appxmanifest | 2 +- 4 files changed, 200 insertions(+), 108 deletions(-) create mode 100644 RunCat365/StartupManager.cs diff --git a/RunCat365/ContextMenuManager.cs b/RunCat365/ContextMenuManager.cs index d348a032..8b740b51 100644 --- a/RunCat365/ContextMenuManager.cs +++ b/RunCat365/ContextMenuManager.cs @@ -15,8 +15,10 @@ using RunCat365.Properties; using System.ComponentModel; -namespace RunCat365 { - internal class ContextMenuManager { +namespace RunCat365 +{ + internal class ContextMenuManager + { private readonly CustomToolStripMenuItem systemInfoMenu = new(); private readonly NotifyIcon notifyIcon = new(); private readonly List icons = []; @@ -35,14 +37,16 @@ internal ContextMenuManager( Func toggleStartup, Action openRepository, Action onExit - ) { + ) + { systemInfoMenu.Text = "-\n-\n-\n-\n-"; systemInfoMenu.Enabled = false; var runnersMenu = new CustomToolStripMenuItem("Runners"); runnersMenu.SetupSubMenusFromEnum( r => r.GetString(), - (parent, sender, e) => { + (parent, sender, e) => + { HandleMenuItemSelection( parent, sender, @@ -58,7 +62,8 @@ Action onExit var themeMenu = new CustomToolStripMenuItem("Theme"); themeMenu.SetupSubMenusFromEnum( t => t.GetString(), - (parent, sender, e) => { + (parent, sender, e) => + { HandleMenuItemSelection( parent, sender, @@ -74,7 +79,8 @@ Action onExit var fpsMaxLimitMenu = new CustomToolStripMenuItem("FPS Max Limit"); fpsMaxLimitMenu.SetupSubMenusFromEnum( f => f.GetString(), - (parent, sender, e) => { + (parent, sender, e) => + { HandleMenuItemSelection( parent, sender, @@ -87,7 +93,8 @@ Action onExit _ => null ); - var startupMenu = new CustomToolStripMenuItem("Launch at startup") { + var startupMenu = new CustomToolStripMenuItem("Launch at startup") + { Checked = getStartup() }; startupMenu.Click += (sender, e) => HandleStartupMenuClick(sender, toggleStartup); @@ -104,7 +111,8 @@ Action onExit var appVersionMenu = new CustomToolStripMenuItem( $"{Application.ProductName} v{Application.ProductVersion}" - ) { + ) + { Enabled = false }; @@ -147,31 +155,37 @@ private static void HandleMenuItemSelection( object? sender, CustomTryParseDelegate tryParseMethod, Action assignValueAction - ) { + ) + { if (sender is null) return; var item = (ToolStripMenuItem)sender; - foreach (ToolStripMenuItem childItem in parentMenu.DropDownItems) { + foreach (ToolStripMenuItem childItem in parentMenu.DropDownItems) + { childItem.Checked = false; } item.Checked = true; - if (tryParseMethod(item.Text, out T parsedValue)) { + if (tryParseMethod(item.Text, out T parsedValue)) + { assignValueAction(parsedValue); } } - private static Bitmap? GetRunnerThumbnailBitmap(Theme systemTheme, Runner runner) { + private static Bitmap? GetRunnerThumbnailBitmap(Theme systemTheme, Runner runner) + { var iconName = $"{systemTheme.GetString()}_{runner.GetString()}_0".ToLower(); var obj = Resources.ResourceManager.GetObject(iconName); return obj is Icon icon ? icon.ToBitmap() : null; } - internal void SetIcons(Theme systemTheme, Theme manualTheme, Runner runner) { + internal void SetIcons(Theme systemTheme, Theme manualTheme, Runner runner) + { var prefix = (manualTheme == Theme.System ? systemTheme : manualTheme).GetString(); var runnerName = runner.GetString(); var rm = Resources.ResourceManager; var capacity = runner.GetFrameNumber(); var list = new List(capacity); - for (int i = 0; i < capacity; i++) { + for (int i = 0; i < capacity; i++) + { var iconName = $"{prefix}_{runnerName}_{i}".ToLower(); var icon = rm.GetObject(iconName); if (icon is null) continue; @@ -182,51 +196,63 @@ internal void SetIcons(Theme systemTheme, Theme manualTheme, Runner runner) { icons.AddRange(list); } - private static void HandleStartupMenuClick(object? sender, Func toggleStartup) { + private static void HandleStartupMenuClick(object? sender, Func toggleStartup) + { if (sender is null) return; var item = (ToolStripMenuItem)sender; - if (toggleStartup(item.Checked)) { + if (toggleStartup(item.Checked)) + { item.Checked = !item.Checked; } } - private void ShowOrActivateGameWindow(Func getSystemTheme) { - if (endlessGameForm is null) { + private void ShowOrActivateGameWindow(Func getSystemTheme) + { + if (endlessGameForm is null) + { endlessGameForm = new EndlessGameForm(getSystemTheme()); - endlessGameForm.FormClosed += (sender, e) => { + endlessGameForm.FormClosed += (sender, e) => + { endlessGameForm = null; }; endlessGameForm.Show(); - } else { + } + else + { endlessGameForm.Activate(); } } - internal void ShowBalloonTip() { + internal void ShowBalloonTip() + { var message = "App has launched. " + "If the icon is not on the taskbar, it has been omitted, " + "so please move it manually and pin it."; notifyIcon.ShowBalloonTip(5000, "RunCat 365", message, ToolTipIcon.Info); } - internal void AdvanceFrame() { + internal void AdvanceFrame() + { if (icons.Count <= current) current = 0; notifyIcon.Icon = icons[current]; current = (current + 1) % icons.Count; } - internal void SetSystemInfoMenuText(string text) { + internal void SetSystemInfoMenuText(string text) + { systemInfoMenu.Text = text; } - internal void SetNotifyIconText(string text) { + internal void SetNotifyIconText(string text) + { notifyIcon.Text = text; } - internal void HideNotifyIcon() { + internal void HideNotifyIcon() + { notifyIcon.Visible = false; } private delegate bool CustomTryParseDelegate(string? value, out T result); } -} \ No newline at end of file +} diff --git a/RunCat365/Program.cs b/RunCat365/Program.cs index b788968c..8390cc91 100644 --- a/RunCat365/Program.cs +++ b/RunCat365/Program.cs @@ -15,7 +15,6 @@ using Microsoft.Win32; using RunCat365.Properties; using System.Diagnostics; -using Windows.ApplicationModel; using FormsTimer = System.Windows.Forms.Timer; namespace RunCat365 @@ -57,7 +56,6 @@ public class RunCat365ApplicationContext : ApplicationContext private Theme manualTheme = Theme.System; private FPSMaxLimit fpsMaxLimit = FPSMaxLimit.FPS40; private int fetchCounter = 5; - private static StartupTask? startupTask; public RunCat365ApplicationContext() { @@ -81,8 +79,8 @@ public RunCat365ApplicationContext() t => manualTheme = t, () => fpsMaxLimit, f => fpsMaxLimit = f, - () => GetStartupAsync().Result, - s => SetStartupAsync(s).Result, + () => StartupManager.GetStartup(), + s => StartupManager.SetStartup(s), () => OpenRepository(), () => Exit() ); @@ -115,83 +113,6 @@ private static Theme GetSystemTheme() return (int)value == 0 ? Theme.Dark : Theme.Light; } - private static async Task GetStartupAsync() { - if (IsRunningAsPackaged()) { - if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); - if (startupTask is null) return false; - if (startupTask.State == StartupTaskState.Enabled) return true; - return false; - } else { - var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; - using var rKey = Registry.CurrentUser.OpenSubKey(keyName); - if (rKey is null) return false; - var value = (rKey.GetValue(Application.ProductName) is not null); - rKey.Close(); - return value; - } - } - - private static async Task SetStartupAsync(bool isChecked) { - if (IsRunningAsPackaged()) { - var active = !isChecked; - var changeCheck = false; - if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); - if (active) { - switch (startupTask.State) { - case StartupTaskState.Enabled: - changeCheck = true; - break; - case StartupTaskState.Disabled: - StartupTaskState newState = await startupTask.RequestEnableAsync(); - if (newState == StartupTaskState.Enabled) { - changeCheck = true; - } else { - MessageBox.Show("Launch at Startup could not be activated.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - changeCheck = false; - } - break; - case StartupTaskState.DisabledByUser: - MessageBox.Show("Launch at startup was disabled by the user, enable it in Task Manager > Startup, search RunCat365 and enable it.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - changeCheck = false; - break; - case StartupTaskState.DisabledByPolicy: - MessageBox.Show("Launch at startup was disabled by policy.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - changeCheck = false; - break; - } - } else { - if (startupTask.State == StartupTaskState.Enabled) startupTask.Disable(); - changeCheck = true; - } - return changeCheck; - } else { - var productName = Application.ProductName; - if (productName is null) return false; - var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; - using var rKey = Registry.CurrentUser.OpenSubKey(keyName, true); - if (rKey is null) return false; - if (isChecked) { - rKey.DeleteValue(productName, false); - } else { - var fileName = Environment.ProcessPath; - if (fileName != null) { - rKey.SetValue(productName, fileName); - } - } - rKey.Close(); - return true; - } - } - - private static bool IsRunningAsPackaged() { - try { - var _ = Package.Current; - return true; - } catch { - return false; - } - } - private void ShowBalloonTip() { if (UserSettings.Default.FirstLaunch) diff --git a/RunCat365/StartupManager.cs b/RunCat365/StartupManager.cs new file mode 100644 index 00000000..1080e185 --- /dev/null +++ b/RunCat365/StartupManager.cs @@ -0,0 +1,145 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Win32; +using Windows.ApplicationModel; + +namespace RunCat365 +{ + internal static class StartupManager + { + + private static StartupTask? startupTask; + + public static bool GetStartup() + { + if (IsRunningAsPackaged()) + { + return GetStartupAsync_Packed().Result; + } + else + { + return GetStartup_NotPacked(); + } + } + + public static bool SetStartup(bool isChecked) + { + if (IsRunningAsPackaged()) + { + return SetStartupAsync_Packed(isChecked).Result; + } + else + { + return SetStartup_NotPacked(isChecked); + } + } + + private static async Task GetStartupAsync_Packed() + { + if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); + if (startupTask is null) return false; + if (startupTask.State == StartupTaskState.Enabled) return true; + return false; + } + + private static bool GetStartup_NotPacked() + { + var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; + using var rKey = Registry.CurrentUser.OpenSubKey(keyName); + if (rKey is null) return false; + var value = (rKey.GetValue(Application.ProductName) is not null); + rKey.Close(); + return value; + } + + private static async Task SetStartupAsync_Packed(bool isChecked) + { + var active = !isChecked; + var changeCheck = false; + if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); + if (active) + { + switch (startupTask.State) + { + case StartupTaskState.Enabled: + changeCheck = true; + break; + case StartupTaskState.Disabled: + StartupTaskState newState = await startupTask.RequestEnableAsync(); + if (newState == StartupTaskState.Enabled) + { + changeCheck = true; + } + else + { + MessageBox.Show("Launch at Startup could not be activated.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + changeCheck = false; + } + break; + case StartupTaskState.DisabledByUser: + MessageBox.Show("Launch at startup was disabled by the user, enable it in Task Manager > Startup, search RunCat 365 and enable it.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + changeCheck = false; + break; + case StartupTaskState.DisabledByPolicy: + MessageBox.Show("Launch at startup was disabled by policy.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + changeCheck = false; + break; + } + } + else + { + if (startupTask.State == StartupTaskState.Enabled) startupTask.Disable(); + changeCheck = true; + } + return changeCheck; + } + + private static bool SetStartup_NotPacked(bool isChecked) + { + var productName = Application.ProductName; + if (productName is null) return false; + var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; + using var rKey = Registry.CurrentUser.OpenSubKey(keyName, true); + if (rKey is null) return false; + if (isChecked) + { + rKey.DeleteValue(productName, false); + } + else + { + var fileName = Environment.ProcessPath; + if (fileName != null) + { + rKey.SetValue(productName, fileName); + } + } + rKey.Close(); + return true; + } + + private static bool IsRunningAsPackaged() + { + try + { + var _ = Package.Current; + return true; + } + catch + { + return false; + } + } + } +} diff --git a/WapForStore/Package.appxmanifest b/WapForStore/Package.appxmanifest index 4b03ffcb..18908e02 100644 --- a/WapForStore/Package.appxmanifest +++ b/WapForStore/Package.appxmanifest @@ -11,7 +11,7 @@ + Version="3.0.2.0" /> RunCat 365 From 89eddafc9cbd1618f634c7a4d5c8eaf6d527b95b Mon Sep 17 00:00:00 2001 From: mor39a <89531894+mor39a@users.noreply.github.com> Date: Wed, 13 Aug 2025 17:31:24 -0500 Subject: [PATCH 7/9] Refactoring LaunchAtStartupManager --- RunCat365/ContextMenuManager.cs | 16 ++-- ...upManager.cs => LaunchAtStartupManager.cs} | 95 ++++++++++--------- RunCat365/Program.cs | 8 +- 3 files changed, 61 insertions(+), 58 deletions(-) rename RunCat365/{StartupManager.cs => LaunchAtStartupManager.cs} (71%) diff --git a/RunCat365/ContextMenuManager.cs b/RunCat365/ContextMenuManager.cs index 8b740b51..b6b0296a 100644 --- a/RunCat365/ContextMenuManager.cs +++ b/RunCat365/ContextMenuManager.cs @@ -33,8 +33,8 @@ internal ContextMenuManager( Action setManualTheme, Func getFPSMaxLimit, Action setFPSMaxLimit, - Func getStartup, - Func toggleStartup, + Func getLaunchAtStartup, + Func toggleLaunchAtStartup, Action openRepository, Action onExit ) @@ -93,17 +93,17 @@ Action onExit _ => null ); - var startupMenu = new CustomToolStripMenuItem("Launch at startup") + var launchAtStartupMenu = new CustomToolStripMenuItem("Launch at startup") { - Checked = getStartup() + Checked = getLaunchAtStartup() }; - startupMenu.Click += (sender, e) => HandleStartupMenuClick(sender, toggleStartup); + launchAtStartupMenu.Click += (sender, e) => HandleStartupMenuClick(sender, toggleLaunchAtStartup); var settingsMenu = new CustomToolStripMenuItem("Settings"); settingsMenu.DropDownItems.AddRange( themeMenu, fpsMaxLimitMenu, - startupMenu + launchAtStartupMenu ); var endlessGameMenu = new CustomToolStripMenuItem("Endless Game"); @@ -196,11 +196,11 @@ internal void SetIcons(Theme systemTheme, Theme manualTheme, Runner runner) icons.AddRange(list); } - private static void HandleStartupMenuClick(object? sender, Func toggleStartup) + private static void HandleStartupMenuClick(object? sender, Func toggleLaunchAtStartup) { if (sender is null) return; var item = (ToolStripMenuItem)sender; - if (toggleStartup(item.Checked)) + if (toggleLaunchAtStartup(item.Checked)) { item.Checked = !item.Checked; } diff --git a/RunCat365/StartupManager.cs b/RunCat365/LaunchAtStartupManager.cs similarity index 71% rename from RunCat365/StartupManager.cs rename to RunCat365/LaunchAtStartupManager.cs index 1080e185..bbb3fb40 100644 --- a/RunCat365/StartupManager.cs +++ b/RunCat365/LaunchAtStartupManager.cs @@ -17,59 +17,30 @@ namespace RunCat365 { - internal static class StartupManager - { - - private static StartupTask? startupTask; - public static bool GetStartup() - { - if (IsRunningAsPackaged()) - { - return GetStartupAsync_Packed().Result; - } - else - { - return GetStartup_NotPacked(); - } - } + internal interface ILaunchAtStartupManager + { + bool GetEnabled(); + bool SetEnabled(bool enabled); + } - public static bool SetStartup(bool isChecked) - { - if (IsRunningAsPackaged()) - { - return SetStartupAsync_Packed(isChecked).Result; - } - else - { - return SetStartup_NotPacked(isChecked); - } - } + internal sealed class PackagedLaunchAtStartupManager : ILaunchAtStartupManager + { + private StartupTask? startupTask; - private static async Task GetStartupAsync_Packed() + public bool GetEnabled() { - if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); + if (startupTask is null) startupTask = Task.Run(async () => await StartupTask.GetAsync("RunCatStartup")).Result; if (startupTask is null) return false; if (startupTask.State == StartupTaskState.Enabled) return true; return false; } - private static bool GetStartup_NotPacked() - { - var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; - using var rKey = Registry.CurrentUser.OpenSubKey(keyName); - if (rKey is null) return false; - var value = (rKey.GetValue(Application.ProductName) is not null); - rKey.Close(); - return value; - } - - private static async Task SetStartupAsync_Packed(bool isChecked) + public bool SetEnabled(bool enabled) { - var active = !isChecked; var changeCheck = false; - if (startupTask is null) startupTask = await StartupTask.GetAsync("RunCatStartup"); - if (active) + if (startupTask is null) startupTask = Task.Run(async () => await StartupTask.GetAsync("RunCatStartup")).Result; + if (!enabled) { switch (startupTask.State) { @@ -77,8 +48,8 @@ private static async Task SetStartupAsync_Packed(bool isChecked) changeCheck = true; break; case StartupTaskState.Disabled: - StartupTaskState newState = await startupTask.RequestEnableAsync(); - if (newState == StartupTaskState.Enabled) + StartupTaskState newStartupState = Task.Run(async () => await startupTask.RequestEnableAsync()).Result; + if (newStartupState == StartupTaskState.Enabled) { changeCheck = true; } @@ -105,15 +76,28 @@ private static async Task SetStartupAsync_Packed(bool isChecked) } return changeCheck; } + } + + internal sealed class UnpackagedLaunchAtStartupManager : ILaunchAtStartupManager + { + public bool GetEnabled() + { + var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; + using var rKey = Registry.CurrentUser.OpenSubKey(keyName); + if (rKey is null) return false; + var value = (rKey.GetValue(Application.ProductName) is not null); + rKey.Close(); + return value; + } - private static bool SetStartup_NotPacked(bool isChecked) + public bool SetEnabled(bool enabled) { var productName = Application.ProductName; if (productName is null) return false; var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; using var rKey = Registry.CurrentUser.OpenSubKey(keyName, true); if (rKey is null) return false; - if (isChecked) + if (enabled) { rKey.DeleteValue(productName, false); } @@ -128,8 +112,25 @@ private static bool SetStartup_NotPacked(bool isChecked) rKey.Close(); return true; } + } + + internal class LaunchAtStartupManager + { + + private readonly ILaunchAtStartupManager _launchAtStartupManager; + + public LaunchAtStartupManager() + { + _launchAtStartupManager = IsRunningAsPackaged() + ? new PackagedLaunchAtStartupManager() + : new UnpackagedLaunchAtStartupManager(); + } + + public bool GetStartup() => _launchAtStartupManager.GetEnabled(); + + public bool SetStartup(bool enabled) => _launchAtStartupManager.SetEnabled(enabled); - private static bool IsRunningAsPackaged() + private bool IsRunningAsPackaged() { try { diff --git a/RunCat365/Program.cs b/RunCat365/Program.cs index 8390cc91..4772b22f 100644 --- a/RunCat365/Program.cs +++ b/RunCat365/Program.cs @@ -1,4 +1,4 @@ -// Copyright 2020 Takuto Nakamura +// Copyright 2020 Takuto Nakamura // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -49,6 +49,7 @@ public class RunCat365ApplicationContext : ApplicationContext private readonly CPURepository cpuRepository; private readonly MemoryRepository memoryRepository; private readonly StorageRepository storageRepository; + private readonly LaunchAtStartupManager launchAtStartupManager; private readonly ContextMenuManager contextMenuManager; private readonly FormsTimer fetchTimer; private readonly FormsTimer animateTimer; @@ -70,6 +71,7 @@ public RunCat365ApplicationContext() cpuRepository = new CPURepository(); memoryRepository = new MemoryRepository(); storageRepository = new StorageRepository(); + launchAtStartupManager = new LaunchAtStartupManager(); contextMenuManager = new ContextMenuManager( () => runner, @@ -79,8 +81,8 @@ public RunCat365ApplicationContext() t => manualTheme = t, () => fpsMaxLimit, f => fpsMaxLimit = f, - () => StartupManager.GetStartup(), - s => StartupManager.SetStartup(s), + () => launchAtStartupManager.GetStartup(), + s => launchAtStartupManager.SetStartup(s), () => OpenRepository(), () => Exit() ); From b64b4f60af4f4fd6cdcaf4ff87c8ab10a1ba5fe4 Mon Sep 17 00:00:00 2001 From: mor39a <89531894+mor39a@users.noreply.github.com> Date: Wed, 13 Aug 2025 17:58:49 -0500 Subject: [PATCH 8/9] UI is separated from the logic, of LaunchAtStartupManager --- RunCat365/ContextMenuManager.cs | 12 ++++++++++-- RunCat365/LaunchAtStartupManager.cs | 11 +++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/RunCat365/ContextMenuManager.cs b/RunCat365/ContextMenuManager.cs index b6b0296a..5226035d 100644 --- a/RunCat365/ContextMenuManager.cs +++ b/RunCat365/ContextMenuManager.cs @@ -200,10 +200,18 @@ private static void HandleStartupMenuClick(object? sender, Func togg { if (sender is null) return; var item = (ToolStripMenuItem)sender; - if (toggleLaunchAtStartup(item.Checked)) + try { - item.Checked = !item.Checked; + if (toggleLaunchAtStartup(item.Checked)) + { + item.Checked = !item.Checked; + } + } + catch (InvalidOperationException ex) + { + MessageBox.Show(ex.Message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); } + } private void ShowOrActivateGameWindow(Func getSystemTheme) diff --git a/RunCat365/LaunchAtStartupManager.cs b/RunCat365/LaunchAtStartupManager.cs index bbb3fb40..9d86e299 100644 --- a/RunCat365/LaunchAtStartupManager.cs +++ b/RunCat365/LaunchAtStartupManager.cs @@ -55,18 +55,13 @@ public bool SetEnabled(bool enabled) } else { - MessageBox.Show("Launch at Startup could not be activated.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - changeCheck = false; + throw new InvalidOperationException("Launch at Startup could not be activated."); } break; case StartupTaskState.DisabledByUser: - MessageBox.Show("Launch at startup was disabled by the user, enable it in Task Manager > Startup, search RunCat 365 and enable it.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - changeCheck = false; - break; + throw new InvalidOperationException("Launch at startup was disabled by the user, enable it in Task Manager > Startup, search RunCat 365 and enable it."); case StartupTaskState.DisabledByPolicy: - MessageBox.Show("Launch at startup was disabled by policy.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - changeCheck = false; - break; + throw new InvalidOperationException("Launch at startup was disabled by policy."); } } else From 9b52575f2240cf84b0aa3b3a78ad7950f72df022 Mon Sep 17 00:00:00 2001 From: mor39a <89531894+mor39a@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:34:14 -0500 Subject: [PATCH 9/9] Keep StartupTask static --- RunCat365/LaunchAtStartupManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RunCat365/LaunchAtStartupManager.cs b/RunCat365/LaunchAtStartupManager.cs index 9d86e299..9646436b 100644 --- a/RunCat365/LaunchAtStartupManager.cs +++ b/RunCat365/LaunchAtStartupManager.cs @@ -26,7 +26,7 @@ internal interface ILaunchAtStartupManager internal sealed class PackagedLaunchAtStartupManager : ILaunchAtStartupManager { - private StartupTask? startupTask; + private static StartupTask? startupTask; public bool GetEnabled() {