diff --git a/src/MSCFixer/AppManagerService.cs b/src/MSCFixer/AppManagerService.cs new file mode 100644 index 0000000..f6b801a --- /dev/null +++ b/src/MSCFixer/AppManagerService.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.Management.Deployment; + +namespace Crapfixer +{ + public class AppAnalysisResult + { + public string AppName { get; set; } + public string FullName { get; set; } + } + + public class AppManagerService + { + private Dictionary _appDirectory = new Dictionary(); + + // Loads all apps and stores them in the directory (name -> fullName) + private async Task LoadAppsAsync() + { + _appDirectory.Clear(); + + var pm = new PackageManager(); + var packages = await Task.Run(() => + pm.FindPackagesForUserWithPackageTypes(string.Empty, PackageTypes.Main)); + + foreach (var p in packages) + { + string name = p.Id.Name; + string fullName = p.Id.FullName; + + if (!_appDirectory.ContainsKey(name)) + { + _appDirectory[name] = fullName; + } + } + Logger.Log($"(Checked against {_appDirectory.Count} apps from the system)"); + } + + // Aanalyzes the apps based on predefined apps (from resources) and returns matching apps + public async Task> AnalyzeAppsAsync(string[] predefinedApps) + { + Logger.Log("\n🧩 APPS ANALYSIS", LogLevel.Info); + Logger.Log(new string('=', 50), LogLevel.Info); + + await LoadAppsAsync(); // Load all apps before analysis + + var result = new List(); + + // Check each app's name against predefined patterns + foreach (var app in _appDirectory) + { + foreach (string pattern in predefinedApps) + { + if (app.Key.ToLower().Contains(pattern.ToLower())) + { + result.Add(new AppAnalysisResult + { + AppName = app.Key, + FullName = app.Value // Store the full name + }); + break; + } + } + } + + return result; + } + + // Uninstall an app by its full name + public async Task UninstallApp(string fullName) + { + try + { + var pm = new PackageManager(); + var operation = pm.RemovePackageAsync(fullName); + + var resetEvent = new ManualResetEvent(false); + operation.Completed = (o, s) => resetEvent.Set(); + await Task.Run(() => resetEvent.WaitOne()); + + if (operation.Status == AsyncStatus.Completed) + { + Logger.Log($"Successfully uninstalled app: {fullName}"); + return true; + } + else + { + Logger.Log($"Failed to uninstall appe: {fullName}", LogLevel.Warning); + return false; + } + } + catch (Exception ex) + { + Logger.Log($"Error during uninstalling app {fullName}: {ex.Message}", LogLevel.Warning); + return false; + } + } + + // Uninstall selected apps and log the results + public async Task> UninstallSelectedAppsAsync(List selectedApps) + { + List removedApps = new List(); + + foreach (var fullName in selectedApps) + { + Logger.Log($"🗑️ Removing app: {fullName}..."); + + // Uninstall the app using its full name + var success = await UninstallApp(fullName); + if (success) + { + removedApps.Add(fullName); + } + } + + // Log results for apps that were successfully removed + foreach (var app in removedApps) + { + Logger.Log($"🗑️ Removed Store App: {app}"); + } + + // Log failed attempts + var failedApps = selectedApps.Except(removedApps).ToList(); + foreach (var app in failedApps) + { + Logger.Log($"⚠️ Failed to remove Store App: {app}", LogLevel.Warning); + } + + Logger.Log("App cleanup complete."); + + return removedApps; // Return removed apps to update the UI + } + } +} \ No newline at end of file diff --git a/src/MSCFixer/FeatureLoader.cs b/src/MSCFixer/FeatureLoader.cs index b259b82..034067a 100644 --- a/src/MSCFixer/FeatureLoader.cs +++ b/src/MSCFixer/FeatureLoader.cs @@ -32,6 +32,9 @@ namespace Features new FeatureNode(new BSODDetails()), new FeatureNode(new VerboseStatus()), new FeatureNode(new SpeedUpShutdown()), + new FeatureNode(new NetworkThrottling()), + new FeatureNode(new SystemResponsiveness()), + new FeatureNode(new MenuShowDelay()), } }, @@ -115,8 +118,7 @@ namespace Features Children = { new FeatureNode(new CopilotTaskbar()), - new FeatureNode(new RecallMachine()), - new FeatureNode(new RecallUser()), + new FeatureNode(new Recall()), } }, }; diff --git a/src/MSCFixer/FeatureManager.cs b/src/MSCFixer/FeatureManager.cs new file mode 100644 index 0000000..fe8b6e0 --- /dev/null +++ b/src/MSCFixer/FeatureManager.cs @@ -0,0 +1,230 @@ +using Features; +using System.Drawing; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Crapfixer +{ + /// + /// Provides operations to load, analyze, fix, restore, and show help for FeatureNodes. + /// + public static class FeatureNodeManager + { + private static int totalChecked; + private static int issuesFound; + + // Public properties to access the analysis results + public static int TotalChecked => totalChecked; + public static int IssuesFound => issuesFound; + + public static void ResetAnalysis() + { + totalChecked = 0; + issuesFound = 0; + } + + /// + /// Loads all features into the TreeView. + /// + public static void LoadFeatures(TreeView tree) + { + var features = FeatureLoader.Load(); + tree.Nodes.Clear(); + + foreach (var feature in features) + AddNode(tree.Nodes, feature); + + // root nodes (categories) + foreach (TreeNode root in tree.Nodes) + { + root.NodeFont = new Font(tree.Font, FontStyle.Bold); + root.ForeColor = Color.RoyalBlue; // category color + } + + tree.ExpandAll(); // expand all nodes + } + + /// + /// Recursively adds a FeatureNode and its children into the TreeView. + /// + private static void AddNode(TreeNodeCollection treeNodes, FeatureNode featureNode) + { + string text = featureNode.IsCategory + ? " " + featureNode.Name + " " // add extra space to avoid clipping + : featureNode.Name; + + TreeNode node = new TreeNode(text) + { + Tag = featureNode, + Checked = true, + }; + treeNodes.Add(node); + + foreach (var child in featureNode.Children) + AddNode(node.Nodes, child); + } + + /// + /// Analyzes all checked features recursively and logs only issues. + /// + public static void AnalyzeAll(TreeNodeCollection nodes) + { + ResetAnalysis(); + + foreach (TreeNode node in nodes) + AnalyzeCheckedRecursive(node); + + Logger.Log("🔎 ANALYSIS COMPLETE", LogLevel.Info); + Logger.Log(new string('=', 50), LogLevel.Info); + + int ok = totalChecked - issuesFound; + Logger.Log($"Summary: {ok} of {totalChecked} checked settings are OK; {issuesFound} require attention.", + issuesFound > 0 ? LogLevel.Warning : LogLevel.Info); + } + + /// Recursively checks all features and logs misconfigurations. + /// + private static void AnalyzeCheckedRecursive(TreeNode node) + { + if (node.Tag is FeatureNode fn) + { + if (!fn.IsCategory && node.Checked && fn.Feature != null) + { + totalChecked++; + bool isOk = fn.Feature.CheckFeature(); + + if (!isOk) + { + issuesFound++; + node.ForeColor = Color.Red; // misconfigured + string category = node.Parent?.Text ?? "General"; + Logger.Log($"❌ [{category}] {fn.Name} - Not configured as recommended."); + Logger.Log($" ➤ {fn.Feature.GetFeatureDetails()}"); + // log a separator when an issue was found + Logger.Log(new string('-', 50), LogLevel.Info); + } + else + { + node.ForeColor = Color.Gray; // properly configured + } + } + + foreach (TreeNode child in node.Nodes) + AnalyzeCheckedRecursive(child); + } + } + + /// + /// Analyzes a selected feature and logs its status. + /// + public static void AnalyzeFeature(TreeNode node) + { + if (node.Tag is FeatureNode fn && !fn.IsCategory && node.Checked && fn.Feature != null) + { + bool isOk = fn.Feature.CheckFeature(); + node.ForeColor = isOk ? Color.Gray : Color.Red; + + Logger.Log(isOk + ? $"✅ Feature: {fn.Name} is properly configured." + : $"❌ Feature: {fn.Name} requires attention.\n ➤ {fn.Feature.GetFeatureDetails()}", + isOk ? LogLevel.Info : LogLevel.Warning); + + if (!isOk) + Logger.Log(new string('-', 50), LogLevel.Info); + } + } + + /// + /// Fixes all checked features recursively. + /// + public static async Task FixChecked(TreeNode node) + { + if (node.Tag is FeatureNode fn) + { + if (!fn.IsCategory && node.Checked && fn.Feature != null) + { + bool result = await fn.Feature.DoFeature(); + Logger.Log(result + ? $"🔧 {fn.Name} - Fixed" + : $"❌ {fn.Name} - ⚠️ Fix failed (This feature may require admin privileges)", + result ? LogLevel.Info : LogLevel.Error); + } + + foreach (TreeNode child in node.Nodes) + await FixChecked(child); + } + } + + /// + /// Restores all checked features recursively. + /// + public static void RestoreChecked(TreeNode node) + { + if (node.Tag is FeatureNode fn) + { + if (!fn.IsCategory && node.Checked && fn.Feature != null) + { + bool ok = fn.Feature.UndoFeature(); + string category = node.Parent?.Text ?? "General"; + Logger.Log(ok + ? $"↩️ [{category}] {fn.Name} - Restored" + : $"❌ [{category}] {fn.Name} - Restore failed", + ok ? LogLevel.Info : LogLevel.Error); + } + + foreach (TreeNode child in node.Nodes) + RestoreChecked(child); + } + } + + /// + /// Attempts to fix the selected feature and logs the result. + /// + public static async Task FixFeature(TreeNode node) + { + if (node.Tag is FeatureNode fn && !fn.IsCategory && node.Checked && fn.Feature != null) + { + bool result = await fn.Feature.DoFeature(); + Logger.Log(result + ? $"🔧 {fn.Name} - Fixed" + : $"❌ {fn.Name} - ⚠️ Fix failed (This feature may require admin privileges)", + result ? LogLevel.Info : LogLevel.Error); + } + } + + /// + /// Restores the selected feature to its original state and logs the result. + /// + public static void RestoreFeature(TreeNode node) + { + if (node.Tag is FeatureNode fn && !fn.IsCategory && node.Checked && fn.Feature != null) + { + bool ok = fn.Feature.UndoFeature(); + Logger.Log(ok + ? $"↩️ {fn.Name} - Restored" + : $"❌ {fn.Name} - Restore failed", + ok ? LogLevel.Info : LogLevel.Error); + } + } + + /// + /// Displays help information for the selected feature. + /// + public static void ShowHelp(TreeNode node) + { + if (node.Tag is FeatureNode fn && fn.Feature != null) + { + string info = fn.Feature.Info(); + MessageBox.Show( + !string.IsNullOrEmpty(info) ? info : "No additional information available.", + $"Help: {fn.Name}", + MessageBoxButtons.OK, + MessageBoxIcon.Information); + } + else + { + MessageBox.Show("⚠️ No feature selected or feature is invalid.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} \ No newline at end of file diff --git a/src/MSCFixer/Features/AI/Recall.cs b/src/MSCFixer/Features/AI/Recall.cs new file mode 100644 index 0000000..e0e5c95 --- /dev/null +++ b/src/MSCFixer/Features/AI/Recall.cs @@ -0,0 +1,75 @@ +using Microsoft.Win32; +using System; +using Crapfixer; +using System.Threading.Tasks; + +namespace Settings.AI +{ + internal class Recall : FeatureBase + { + private const string keyName = @"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; + private const string valueName = "AllowRecallEnablement"; + private const int recommendedValue = 0; + + public override string GetFeatureDetails() + { + return $"{keyName} | Value: {valueName} | Recommended: {recommendedValue} (Recall off – protect your privacy, prevent AI from accessing personal data)"; + } + + + public override string ID() + { + return "Turn off Recall in Windows 11"; + } + + public override string Info() + { + return "This will remove Recall from Windows 11 24H2"; + } + + public override bool CheckFeature() + { + // Check if reg key exists + object value = Registry.GetValue(keyName, valueName, null); + if (value == null) + { + // Key does not exist, turn off feature + return false; + } + + // Key exists, check if value is desired value + return (int)value != recommendedValue; + } + + public override Task DoFeature() + { + try + { + Registry.SetValue(keyName, valueName, 1, Microsoft.Win32.RegistryValueKind.DWord); + Logger.Log("You've even disabled system-wide Snapshots for all users now.", LogLevel.Info); + return Task.FromResult(true); + } + catch (Exception ex) + { + Logger.Log("Code red in " + ex.Message, LogLevel.Error); + } + + return Task.FromResult(false); + } + + public override bool UndoFeature() + { + try + { + Registry.SetValue(keyName, valueName, recommendedValue, Microsoft.Win32.RegistryValueKind.DWord); + return true; + } + catch (Exception ex) + { + Logger.Log("Code red in " + ex.Message, LogLevel.Error); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/MSCFixer/Features/Issues/BasicCleanup.cs b/src/MSCFixer/Features/Issues/BasicCleanup.cs index a416bd0..f4337ef 100644 --- a/src/MSCFixer/Features/Issues/BasicCleanup.cs +++ b/src/MSCFixer/Features/Issues/BasicCleanup.cs @@ -12,7 +12,7 @@ namespace Settings.Issues private readonly string tempPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Temp"); private const int cleanupSetNumber = 1; - public override string ID() => "Basic Cleanup"; + public override string ID() => "Basic Disk Cleanup"; public override string Info() => "Deletes all temporary files from the user's Temp folder. Then, the built-in Disk Cleanup utility (cleanmgr) is run."; @@ -23,11 +23,7 @@ namespace Settings.Issues var totalSize = GetDirectorySize(tempPath); bool isOk = totalSize <= 50; - if (!isOk) - { - Logger.Log($"Temp folder size is {totalSize} MB (cleanup recommended!) We need also to include cleanup in the next run.", LogLevel.Warning); - - } + return isOk; } @@ -44,7 +40,7 @@ namespace Settings.Issues try { var totalSize = GetDirectorySize(tempPath); - return $"Temp folder size: {totalSize} MB"; + return $"Temp folder size: {totalSize} MB (We need also to include cleanmgr in the next run)"; } catch (Exception ex) { diff --git a/src/MSCFixer/Features/Privacy/Telemetry.cs b/src/MSCFixer/Features/Privacy/Telemetry.cs index 31e812b..e37d4bd 100644 --- a/src/MSCFixer/Features/Privacy/Telemetry.cs +++ b/src/MSCFixer/Features/Privacy/Telemetry.cs @@ -33,7 +33,7 @@ namespace Settings.Privacy try { Registry.SetValue(dataCollection, "AllowTelemetry", 0, RegistryValueKind.DWord); - Registry.SetValue(diagTrack, "Start", 2, RegistryValueKind.DWord); + Registry.SetValue(diagTrack, "Start", 4, RegistryValueKind.DWord); return Task.FromResult(true); } catch (Exception ex) @@ -49,7 +49,7 @@ namespace Settings.Privacy try { Registry.SetValue(dataCollection, "AllowTelemetry", 1, RegistryValueKind.DWord); - Registry.SetValue(diagTrack, "Start", 4, RegistryValueKind.DWord); + Registry.SetValue(diagTrack, "Start", 2, RegistryValueKind.DWord); return true; } diff --git a/src/MSCFixer/Features/System/MenuShowDelay.cs b/src/MSCFixer/Features/System/MenuShowDelay.cs new file mode 100644 index 0000000..d34f4bb --- /dev/null +++ b/src/MSCFixer/Features/System/MenuShowDelay.cs @@ -0,0 +1,64 @@ +using Microsoft.Win32; +using System; +using Crapfixer; +using System.Threading.Tasks; + +namespace Settings.System +{ + internal class MenuShowDelay : FeatureBase + { + private const string keyName = @"HKEY_CURRENT_USER\Control Panel\Desktop"; + private const string valueName = "MenuShowDelay"; + private const string recommendedValue = "10"; + + public override string GetFeatureDetails() + { + return $"{keyName} | Value: {valueName} | Recommended Value: \"{recommendedValue}\" (faster menu response)"; + } + + public override string ID() + { + return "Speed Up Menu Show Delay"; + } + + public override string Info() + { + return "Speeds up the appearance of menus and submenus by lowering the default delay. This improves the perceived responsiveness of the UI."; + } + + public override bool CheckFeature() + { + return Utils.StringEquals(keyName, valueName, recommendedValue); + } + + public override Task DoFeature() + { + try + { + Registry.SetValue(keyName, valueName, recommendedValue, RegistryValueKind.String); + return Task.FromResult(true); + } + catch (Exception ex) + { + Logger.Log("Code red in " + ex.Message, LogLevel.Error); + } + + return Task.FromResult(false); + } + + public override bool UndoFeature() + { + try + { + Registry.SetValue(keyName, valueName, "400", RegistryValueKind.String); // Default is 400 on Windows 11 + return true; + } + catch (Exception ex) + { + Logger.Log("Code red in " + ex.Message, LogLevel.Error); + } + + return false; + } + } +} diff --git a/src/MSCFixer/Features/System/NetworkThrottling.cs b/src/MSCFixer/Features/System/NetworkThrottling.cs new file mode 100644 index 0000000..47e294d --- /dev/null +++ b/src/MSCFixer/Features/System/NetworkThrottling.cs @@ -0,0 +1,64 @@ +using Microsoft.Win32; +using System; +using Crapfixer; +using System.Threading.Tasks; + +namespace Settings.System +{ + internal class NetworkThrottling : FeatureBase + { + private const string keyName = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile"; + private const string valueName = "NetworkThrottlingIndex"; + private const int recommendedValue = -1; // 0xFFFFFFFF + + public override string GetFeatureDetails() + { + return $"{keyName} | Value: {valueName} | Recommended Value: 0xFFFFFFFF (decimal: {uint.MaxValue})"; + } + + public override string ID() + { + return "Disable Network Throttling"; + } + + public override string Info() + { + return "Disables the Windows network throttling mechanism to potentially improve performance for streaming, gaming, or real-time applications."; + } + + public override bool CheckFeature() + { + return Utils.IntEquals(keyName, valueName, unchecked((int)0xFFFFFFFF)); + } + + public override Task DoFeature() + { + try + { + Registry.SetValue(keyName, valueName, unchecked((int)0xFFFFFFFF), RegistryValueKind.DWord); + return Task.FromResult(true); + } + catch (Exception ex) + { + Logger.Log("Code red in " + ex.Message, LogLevel.Error); + } + + return Task.FromResult(false); + } + + public override bool UndoFeature() + { + try + { + Registry.SetValue(keyName, valueName, 10, RegistryValueKind.DWord); // Default is 10 + return true; + } + catch (Exception ex) + { + Logger.Log("Code red in " + ex.Message, LogLevel.Error); + } + + return false; + } + } +} diff --git a/src/MSCFixer/Features/System/SpeedUpShutdown.cs b/src/MSCFixer/Features/System/SpeedUpShutdown.cs index 373614d..93ef822 100644 --- a/src/MSCFixer/Features/System/SpeedUpShutdown.cs +++ b/src/MSCFixer/Features/System/SpeedUpShutdown.cs @@ -18,7 +18,7 @@ namespace Settings.System public override string ID() { - return "Speed Up Shutdown Process"; + return "Speed Up Shutdown Time"; } public override string Info() diff --git a/src/MSCFixer/Features/System/SystemResponsiveness.cs b/src/MSCFixer/Features/System/SystemResponsiveness.cs new file mode 100644 index 0000000..93536e9 --- /dev/null +++ b/src/MSCFixer/Features/System/SystemResponsiveness.cs @@ -0,0 +1,64 @@ +using Microsoft.Win32; +using System; +using Crapfixer; +using System.Threading.Tasks; + +namespace Settings.System +{ + internal class SystemResponsiveness : FeatureBase + { + private const string keyName = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile"; + private const string valueName = "SystemResponsiveness"; + private const int recommendedValue = 10; + + public override string GetFeatureDetails() + { + return $"{keyName} | Value: {valueName} | Recommended Value: {recommendedValue}"; + } + + public override string ID() + { + return "Optimize System Responsiveness"; + } + + public override string Info() + { + return "Enhances system responsiveness by prioritizing CPU resources for foreground tasks, improving performance during active use."; + } + + public override bool CheckFeature() + { + return Utils.IntEquals(keyName, valueName, recommendedValue); + } + + public override Task DoFeature() + { + try + { + Registry.SetValue(keyName, valueName, recommendedValue, RegistryValueKind.DWord); + return Task.FromResult(true); + } + catch (Exception ex) + { + Logger.Log("Code red in " + ex.Message, LogLevel.Error); + } + + return Task.FromResult(false); + } + + public override bool UndoFeature() + { + try + { + Registry.SetValue(keyName, valueName, 20, RegistryValueKind.DWord); // Default is typically 20 on Windows 11 + return true; + } + catch (Exception ex) + { + Logger.Log("Code red in " + ex.Message, LogLevel.Error); + } + + return false; + } + } +} diff --git a/src/MSCFixer/NavigationManager.cs b/src/MSCFixer/NavigationManager.cs new file mode 100644 index 0000000..7630224 --- /dev/null +++ b/src/MSCFixer/NavigationManager.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Windows.Forms; + +public class NavigationManager +{ + private Stack navigationHistory = new Stack(); // Holds previous views + private Panel panelContainer; // Reference to the panel container where views are switched + private Control mainPanel; // Reference to the main panel (start page) + + public NavigationManager(Panel panel) + { + this.panelContainer = panel; + this.mainPanel = panel.Controls.Count > 0 ? panel.Controls[0] : null; + } + + /// + /// Checks if there are views in the navigation history to go back to. + /// + public bool CanGoBack() + { + return navigationHistory.Count > 0; + } + + /// + /// Adds the current control to the navigation history. + /// + private void AddToHistory() + { + if (panelContainer.Controls.Count > 0) + { + // Add the currently visible control to the history stack + navigationHistory.Push(panelContainer.Controls[0]); + } + } + + /// + /// Switches to a new view and adds the current view to the navigation history. + /// + /// The new view to display. + public void SwitchView(Control newView) + { + AddToHistory(); // Save the current view before switching + + // Clear the container and display the new view + panelContainer.Controls.Clear(); + panelContainer.Controls.Add(newView); + newView.Dock = DockStyle.Fill; + newView.BringToFront(); + } + + /// + /// Navigates back to the previous view in the history, if available. + /// + public void GoBack() + { + if (CanGoBack()) + { + // Pop the last view from the history stack and display it + Control previousView = navigationHistory.Pop(); + panelContainer.Controls.Clear(); + panelContainer.Controls.Add(previousView); + previousView.Dock = DockStyle.Fill; + previousView.BringToFront(); + } + } + + /// + /// Clears the navigation history. + /// + public void ClearHistory() + { + navigationHistory.Clear(); + } +} \ No newline at end of file