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.
+

### Why need it?