Skip to content

Commit

Permalink
Bypass throttle management if AC is connected
Browse files Browse the repository at this point in the history
  • Loading branch information
Yizhou Zhu committed Aug 20, 2022
1 parent 1887228 commit b133439
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 1 deletion.
6 changes: 5 additions & 1 deletion EnergyStar/EnergyManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
Expand Down
4 changes: 4 additions & 0 deletions EnergyStar/EnergyStar.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
<Configurations>Debug;Release;ReleaseInvisible</Configurations>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Win32.SystemEvents" Version="6.0.1" />
</ItemGroup>

<ItemGroup>
<Folder Include="Config\" />
</ItemGroup>
Expand Down
60 changes: 60 additions & 0 deletions EnergyStar/Interop/Win32Api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ namespace EnergyStar.Interop
{
internal class Win32Api
{
public static Win32Api.SYSTEM_POWER_STATUS GetSystemPowerStatus()
{
IntPtr powerStatusPtr = Marshal.AllocHGlobal(Marshal.SizeOf<Win32Api.SYSTEM_POWER_STATUS>());

try
{
if (Win32Api.GetSystemPowerStatus(powerStatusPtr))
{
return Marshal.PtrToStructure<Win32Api.SYSTEM_POWER_STATUS>(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);

Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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;
}
}
}
}
27 changes: 27 additions & 0 deletions EnergyStar/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using EnergyStar.Interop;
using Microsoft.Win32;

namespace EnergyStar
{
Expand All @@ -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...
Expand All @@ -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))
Expand Down

0 comments on commit b133439

Please sign in to comment.