From b133439c91d638898431e844dba522b5f6ce8d31 Mon Sep 17 00:00:00 2001 From: Yizhou Zhu Date: Tue, 2 Aug 2022 23:25:45 -0700 Subject: [PATCH] Bypass throttle management if AC is connected --- EnergyStar/EnergyManager.cs | 6 +++- EnergyStar/EnergyStar.csproj | 4 +++ EnergyStar/Interop/Win32Api.cs | 60 ++++++++++++++++++++++++++++++++++ EnergyStar/Program.cs | 27 +++++++++++++++ 4 files changed, 96 insertions(+), 1 deletion(-) diff --git a/EnergyStar/EnergyManager.cs b/EnergyStar/EnergyManager.cs index 0b93002..dfb909b 100644 --- a/EnergyStar/EnergyManager.cs +++ b/EnergyStar/EnergyManager.cs @@ -47,6 +47,8 @@ public unsafe class EnergyManager // Speical handling needs for UWP to get the child window process public const string UWPFrameHostApp = "ApplicationFrameHost.exe"; + public static bool IsAcConnected { get; set; } = false; + private static uint pendingProcPid = 0; private static string pendingProcName = ""; @@ -100,6 +102,8 @@ private static string GetProcessNameFromHandle(IntPtr hProcess) public static unsafe void HandleForegroundEvent(IntPtr hwnd) { + if (IsAcConnected) return; + var windowThreadId = Win32Api.GetWindowThreadProcessId(hwnd, out uint procId); // This is invalid, likely a process is dead, or idk if (windowThreadId == 0 || procId == 0) return; @@ -180,7 +184,7 @@ public static void ThrottleAllUserBackgroundProcesses() if (proc.Id == pendingProcPid) continue; if (BypassProcessList.Contains($"{proc.ProcessName}.exe".ToLowerInvariant())) continue; var hProcess = Win32Api.OpenProcess((uint)Win32Api.ProcessAccessFlags.SetInformation, false, (uint) proc.Id); - ToggleEfficiencyMode(hProcess, true); + ToggleEfficiencyMode(hProcess, IsAcConnected ? false : true); Win32Api.CloseHandle(hProcess); } } diff --git a/EnergyStar/EnergyStar.csproj b/EnergyStar/EnergyStar.csproj index c1464b7..8c8c00f 100644 --- a/EnergyStar/EnergyStar.csproj +++ b/EnergyStar/EnergyStar.csproj @@ -10,6 +10,10 @@ Debug;Release;ReleaseInvisible + + + + diff --git a/EnergyStar/Interop/Win32Api.cs b/EnergyStar/Interop/Win32Api.cs index 9ef97b9..e9d6b77 100644 --- a/EnergyStar/Interop/Win32Api.cs +++ b/EnergyStar/Interop/Win32Api.cs @@ -6,6 +6,25 @@ namespace EnergyStar.Interop { internal class Win32Api { + public static Win32Api.SYSTEM_POWER_STATUS GetSystemPowerStatus() + { + IntPtr powerStatusPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + + try + { + if (Win32Api.GetSystemPowerStatus(powerStatusPtr)) + { + return Marshal.PtrToStructure(powerStatusPtr); + } + + return new Win32Api.SYSTEM_POWER_STATUS(); + } + finally + { + Marshal.FreeHGlobal(powerStatusPtr); + } + } + [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); @@ -33,6 +52,10 @@ public static extern bool SetProcessInformation([In] IntPtr hProcess, [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc callback, IntPtr lParam); + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetSystemPowerStatus(IntPtr lpSystemPowerStatus); + [Flags] public enum ProcessAccessFlags : uint { @@ -93,5 +116,42 @@ public struct PROCESS_POWER_THROTTLING_STATE public ProcessorPowerThrottlingFlags ControlMask; public ProcessorPowerThrottlingFlags StateMask; } + + [StructLayout(LayoutKind.Sequential)] + public struct SYSTEM_POWER_STATUS + { + public const Byte AC_LINE_STATUS_OFFLINE = 0; // AC adapter disconnected + public const Byte AC_LINE_STATUS_ONLINE = 1; // AC adapter connected + public const Byte AC_LINE_STATUS_UNKNOWN = 255; + + public const Byte BATTERY_FLAG_HIGH = 1; // the battery capacity is at more than 66 percent + public const Byte BATTERY_FLAG_LOW = 2; // the battery capacity is at less than 33 percent + public const Byte BATTERY_FLAG_CRITICAL = 4; // the battery capacity is at less than five percent + public const Byte BATTERY_FLAG_CHARGING = 8; // Charging + public const Byte BATTERY_FLAG_NO_SYSTEM_BATTERY = 128; // No system battery + public const Byte BATTERY_FLAG_UNKNOWN = 255; // Unable to read the battery flag information + + public const Byte BATTERY_LIFE_PERCENT_UNKNOWN = 255; + + public const Byte SYSTEM_STATUS_FLAG_BATTERY_SAVER_OFF = 0; // Battery saver is off. + public const Byte SYSTEM_STATUS_FLAG_BATTERY_SAVER_ON = 1; // Battery saver on. Save energy where possible. + + public Byte ACLineStatus; // The AC power status. + public Byte BatteryFlag; // The battery charge status. + public Byte BatteryLifePercent; // The percentage of full battery charge remaining. This member can be a value in the range 0 to 100, or 255 if status is unknown. + public Byte SystemStatusFlag; // The status of battery saver. + public UInt32 BatteryLifeTime; // The number of seconds of battery life remaining, or –1 if remaining seconds are unknown or if the device is connected to AC power. + public UInt32 BatteryFullLifeTime; // The number of seconds of battery life when at full charge, or –1 if full battery lifetime is unknown or if the device is connected to AC power. + + public SYSTEM_POWER_STATUS() + { + ACLineStatus = AC_LINE_STATUS_UNKNOWN; + BatteryFlag = BATTERY_FLAG_UNKNOWN; + BatteryLifePercent = BATTERY_LIFE_PERCENT_UNKNOWN; + SystemStatusFlag = 0; + BatteryLifeTime = 0; + BatteryFullLifeTime = 0; + } + } } } diff --git a/EnergyStar/Program.cs b/EnergyStar/Program.cs index 6f791a4..3696da1 100644 --- a/EnergyStar/Program.cs +++ b/EnergyStar/Program.cs @@ -1,4 +1,5 @@ using EnergyStar.Interop; +using Microsoft.Win32; namespace EnergyStar { @@ -24,6 +25,25 @@ static async void HouseKeepingThreadProc() } } + static void SystemEventsPowerModeChanged() + { + Win32Api.SYSTEM_POWER_STATUS powerStatus = Win32Api.GetSystemPowerStatus(); + + switch (powerStatus.ACLineStatus) + { + case Win32Api.SYSTEM_POWER_STATUS.AC_LINE_STATUS_OFFLINE: + EnergyManager.IsAcConnected = false; + break; + case Win32Api.SYSTEM_POWER_STATUS.AC_LINE_STATUS_ONLINE: + EnergyManager.IsAcConnected = true; + break; + default: + break; + } + + EnergyManager.ThrottleAllUserBackgroundProcesses(); + } + static void Main(string[] args) { // Well, this program only works for Windows Version starting with Cobalt... @@ -38,12 +58,19 @@ static void Main(string[] args) Environment.Exit(120); } + // Call SystemEventsPowerModeChanged() to init the power adapter status. + SystemEventsPowerModeChanged(); + HookManager.SubscribeToWindowEvents(); EnergyManager.ThrottleAllUserBackgroundProcesses(); var houseKeepingThread = new Thread(new ThreadStart(HouseKeepingThreadProc)); houseKeepingThread.Start(); +#pragma warning disable CA1416 // Validate platform compatibility + SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler((object sender, PowerModeChangedEventArgs e) => SystemEventsPowerModeChanged()); +#pragma warning restore CA1416 // Validate platform compatibility + while (true) { if (Event.GetMessage(out Win32WindowForegroundMessage msg, IntPtr.Zero, 0, 0))