From 953222f9c458ce53cfff0e2b5fa3127d9bf79970 Mon Sep 17 00:00:00 2001 From: AnonAmit <134582556+AnonAmit@users.noreply.github.com> Date: Thu, 9 Apr 2026 07:34:01 +0530 Subject: [PATCH] Add SYSTEM relaunch via PsExec --- AsusFanControl/AsusFanControl.csproj | 3 +- AsusFanControl/Program.cs | 53 ++++++++- AsusFanControl/SystemRelaunch.cs | 128 +++++++++++++++++++++ AsusFanControlGUI/AsusFanControlGUI.csproj | 10 +- AsusFanControlGUI/Form1.cs | 16 ++- AsusFanControlGUI/Program.cs | 24 +++- README.md | 5 + 7 files changed, 233 insertions(+), 6 deletions(-) create mode 100644 AsusFanControl/SystemRelaunch.cs diff --git a/AsusFanControl/AsusFanControl.csproj b/AsusFanControl/AsusFanControl.csproj index 4f06f7f..d2ccd36 100644 --- a/AsusFanControl/AsusFanControl.csproj +++ b/AsusFanControl/AsusFanControl.csproj @@ -66,6 +66,7 @@ + @@ -77,4 +78,4 @@ - \ No newline at end of file + diff --git a/AsusFanControl/Program.cs b/AsusFanControl/Program.cs index dbdc50c..2463ae9 100644 --- a/AsusFanControl/Program.cs +++ b/AsusFanControl/Program.cs @@ -1,5 +1,9 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + namespace AsusFanControl { internal static class Program @@ -10,7 +14,29 @@ internal static class Program [STAThread] static int Main(string[] args) { - if (args.Length < 1) + args = args ?? new string[0]; + + var filteredArgs = new List(); + bool isPsExecRelaunch = false; + bool autoRelaunch = false; + + foreach (var a in args) + { + if (string.Equals(a, SystemRelaunch.PsExecRelaunchFlag, StringComparison.OrdinalIgnoreCase)) + { + isPsExecRelaunch = true; + continue; + } + if (string.Equals(a, SystemRelaunch.AutoRelaunchFlag, StringComparison.OrdinalIgnoreCase)) + { + autoRelaunch = true; + continue; + } + + filteredArgs.Add(a); + } + + if (filteredArgs.Count < 1) { Console.WriteLine("Usage: AsusFanControl "); Console.WriteLine("\t--get-fan-speeds"); @@ -19,12 +45,35 @@ static int Main(string[] args) Console.WriteLine("\t--get-fan-speed=fanId (comma separated)"); Console.WriteLine("\t--set-fan-speed=fanId:0-100 (comma separated, percent value, 0 for turning off test mode)"); Console.WriteLine("\t--get-cpu-temp"); + Console.WriteLine("\t--relaunch-system (optional, auto-relaunch via PsExec if needed)"); return 1; } + if (!isPsExecRelaunch && !SystemRelaunch.IsRunningAsSystem() && !SystemRelaunch.CanAccessFanDriver()) + { + if (autoRelaunch && SystemRelaunch.TryRelaunchAsSystem(filteredArgs.ToArray())) + return 0; + + Console.WriteLine("Fan driver access requires running as SYSTEM (LocalSystem) on some newer ASUS drivers."); + + var psExecPath = SystemRelaunch.FindPsExecPath(); + if (!string.IsNullOrWhiteSpace(psExecPath)) + { + var sessionId = Process.GetCurrentProcess().SessionId; + Console.WriteLine("Relaunch example:"); + Console.WriteLine(string.Format(" {0} -accepteula -d -s -i {1} AsusFanControl.exe {2}", Path.GetFileName(psExecPath), sessionId, string.Join(" ", filteredArgs.ToArray()))); + } + else + { + Console.WriteLine("Place PsExec64.exe / PsExec.exe next to AsusFanControl.exe, or use AsusFanControlGUI (it can self-relaunch)."); + } + + return 2; + } + var asusControl = new AsusControl(); - foreach (var arg in args) + foreach (var arg in filteredArgs) { if (arg.StartsWith("--get-fan-speeds")) { diff --git a/AsusFanControl/SystemRelaunch.cs b/AsusFanControl/SystemRelaunch.cs new file mode 100644 index 0000000..6074d0c --- /dev/null +++ b/AsusFanControl/SystemRelaunch.cs @@ -0,0 +1,128 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Security.Principal; +using AsusSystemAnalysis; + +namespace AsusFanControl +{ + public static class SystemRelaunch + { + public const string PsExecRelaunchFlag = "--psexec-relaunch"; + public const string AutoRelaunchFlag = "--relaunch-system"; + + public static bool IsRunningAsSystem() + { + try + { + using (var identity = WindowsIdentity.GetCurrent()) + { + return identity != null && identity.IsSystem; + } + } + catch + { + return false; + } + } + + public static bool CanAccessFanDriver() + { + try + { + var dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AsusWinIO64.dll"); + if (!File.Exists(dllPath)) + return false; + + AsusWinIO64.InitializeWinIo(); + var fanCount = AsusWinIO64.HealthyTable_FanCounts(); + AsusWinIO64.ShutdownWinIo(); + + return fanCount > 0; + } + catch + { + try { AsusWinIO64.ShutdownWinIo(); } catch { } + return false; + } + } + + public static string FindPsExecPath() + { + var baseDir = AppDomain.CurrentDomain.BaseDirectory; + var candidates = new[] + { + "PsExec64.exe", + "PsExec.exe", + "psexec64.exe", + "psexec.exe", + "PsExec64", + "PsExec", + "psexec64", + "psexec" + }; + + foreach (var candidate in candidates) + { + var path = Path.Combine(baseDir, candidate); + if (File.Exists(path)) return path; + } + + return null; + } + + public static bool TryRelaunchAsSystem(string[] args) + { + if (!Environment.UserInteractive) return false; + if (IsRunningAsSystem()) return false; + + if (args != null && args.Any(a => string.Equals(a, PsExecRelaunchFlag, StringComparison.OrdinalIgnoreCase))) + return false; + + var psExecPath = FindPsExecPath(); + if (psExecPath == null) return false; + + string exePath; + try { exePath = Process.GetCurrentProcess().MainModule.FileName; } + catch { return false; } + + if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath)) + return false; + + try + { + var forwardArgs = (args ?? new string[0]) + .Where(a => !string.Equals(a, PsExecRelaunchFlag, StringComparison.OrdinalIgnoreCase)) + .Concat(new[] { PsExecRelaunchFlag }) + .ToArray(); + + var forwarded = string.Join(" ", forwardArgs.Select(QuoteArg)); + var sessionId = Process.GetCurrentProcess().SessionId; + + var psi = new ProcessStartInfo + { + FileName = psExecPath, + Arguments = string.Format("-accepteula -d -s -i {0} \"{1}\" {2}", sessionId, exePath, forwarded), + UseShellExecute = true, + Verb = "runas", + WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory + }; + + Process.Start(psi); + return true; + } + catch + { + return false; + } + } + + private static string QuoteArg(string arg) + { + if (string.IsNullOrEmpty(arg)) return "\"\""; + if (!arg.Contains(" ") && !arg.Contains("\"")) return arg; + return "\"" + arg.Replace("\"", "\\\"") + "\""; + } + } +} diff --git a/AsusFanControlGUI/AsusFanControlGUI.csproj b/AsusFanControlGUI/AsusFanControlGUI.csproj index d1527a6..fa16ba5 100644 --- a/AsusFanControlGUI/AsusFanControlGUI.csproj +++ b/AsusFanControlGUI/AsusFanControlGUI.csproj @@ -9,6 +9,7 @@ AsusFanControlGUI AsusFanControlGUI v4.7.2 + true 512 true true @@ -65,6 +66,10 @@ + + $(NuGetPackageRoot)system.resources.extensions\8.0.0\lib\net462\System.Resources.Extensions.dll + True + @@ -112,5 +117,8 @@ + + + - \ No newline at end of file + diff --git a/AsusFanControlGUI/Form1.cs b/AsusFanControlGUI/Form1.cs index 1678305..8065c63 100644 --- a/AsusFanControlGUI/Form1.cs +++ b/AsusFanControlGUI/Form1.cs @@ -6,7 +6,7 @@ namespace AsusFanControlGUI { public partial class Form1 : Form { - AsusControl asusControl = new AsusControl(); + AsusControl asusControl; int fanSpeed = 0; Timer timer; NotifyIcon trayIcon; @@ -14,6 +14,7 @@ public partial class Form1 : Form public Form1() { InitializeComponent(); + asusControl = new AsusControl(); AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit); toolStripMenuItemTurnOffControlOnExit.Checked = Properties.Settings.Default.turnOffControlOnExit; @@ -31,6 +32,19 @@ private void OnProcessExit(object sender, EventArgs e) private void Form1_Load(object sender, EventArgs e) { + var fanCount = asusControl.HealthyTable_FanCounts(); + if (fanCount <= 0) + { + MessageBox.Show( + "Unable to access ASUS fan driver.\n\n" + + "If you are on a newer ASUS System Control Interface driver, run this app as SYSTEM (LocalSystem).\n" + + "Place PsExec64.exe / PsExec.exe next to AsusFanControlGUI.exe and start again.", + "Asus Fan Control", + MessageBoxButtons.OK, + MessageBoxIcon.Warning + ); + } + timerRefreshStats(); } diff --git a/AsusFanControlGUI/Program.cs b/AsusFanControlGUI/Program.cs index 7dd3541..4013d71 100644 --- a/AsusFanControlGUI/Program.cs +++ b/AsusFanControlGUI/Program.cs @@ -1,5 +1,6 @@ using System; using System.Windows.Forms; +using AsusFanControl; namespace AsusFanControlGUI { @@ -9,8 +10,29 @@ internal static class Program /// The main entry point for the application. /// [STAThread] - static void Main() + static void Main(string[] args) { + args = args ?? new string[0]; + + bool isPsExecRelaunch = false; + foreach (var a in args) + if (string.Equals(a, SystemRelaunch.PsExecRelaunchFlag, StringComparison.OrdinalIgnoreCase)) + isPsExecRelaunch = true; + + if (!isPsExecRelaunch && Environment.UserInteractive && !SystemRelaunch.IsRunningAsSystem() && !SystemRelaunch.CanAccessFanDriver()) + { + if (SystemRelaunch.TryRelaunchAsSystem(args)) + return; + + MessageBox.Show( + "Fan driver access requires running as SYSTEM (LocalSystem) on some newer ASUS drivers.\n\n" + + "Place PsExec64.exe / PsExec.exe next to AsusFanControlGUI.exe to enable auto-relaunch.", + "Asus Fan Control", + MessageBoxButtons.OK, + MessageBoxIcon.Warning + ); + } + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); diff --git a/README.md b/README.md index c132c78..26ed40f 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,15 @@ Go to [releases](../../releases) --get-fan-speed=fanId (comma separated) --set-fan-speed=fanId:0-100 (comma separated, percent value, 0 for turning off test mode) --get-cpu-temp + --relaunch-system (optional, auto-relaunch via PsExec if needed) GUI: `AsusFanControlGUI.exe` +> [!NOTE] +> **SYSTEM requirement (newer ASUS drivers):** On some ASUS System Control Interface driver versions (3.1.41.0+), low-level fan access is restricted to **LocalSystem**. +> If fan control doesn't work, place `PsExec64.exe` or `PsExec.exe` next to `AsusFanControlGUI.exe` and start the GUI again — it will relaunch itself as SYSTEM automatically. + ![AsusFanControlGUI](https://github.com/Karmel0x/AsusFanControl/assets/25367564/fe197ad0-7079-4d51-ae78-177cb6369e96) ### Why need it?