From 3df2c5fe6a84514bff25adfc0e31a1de84c2c906 Mon Sep 17 00:00:00 2001 From: Den Delimarsky <1389609+dend@users.noreply.github.com> Date: Sat, 12 Feb 2022 10:42:14 -0800 Subject: [PATCH] PowerToys Awake - Improved Logging/Minor Bug Fixes (#15875) * Some code cleanup * Making sure that the native wrapper lives in Awake.Core * Adding power state logging, as well as termination entries. * Better logging. * Typos and logging improvements * Remove dependency --- src/modules/awake/Awake/Core/APIHelper.cs | 30 ++------ .../Core/Models/BatteryReportingScale.cs | 12 ++++ .../awake/Awake/Core/Models/ControlType.cs | 16 +++++ .../awake/Awake/Core/Models/ExecutionState.cs | 17 +++++ .../Core/Models/SystemPowerCapabilities.cs | 70 +++++++++++++++++++ .../Awake/Core/Models/SystemPowerState.cs | 20 ++++++ .../awake/Awake/{ => Core}/NativeMethods.cs | 6 +- src/modules/awake/Awake/NLog.config | 4 +- src/modules/awake/Awake/Program.cs | 37 +++++++--- 9 files changed, 174 insertions(+), 38 deletions(-) create mode 100644 src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs create mode 100644 src/modules/awake/Awake/Core/Models/ControlType.cs create mode 100644 src/modules/awake/Awake/Core/Models/ExecutionState.cs create mode 100644 src/modules/awake/Awake/Core/Models/SystemPowerCapabilities.cs create mode 100644 src/modules/awake/Awake/Core/Models/SystemPowerState.cs rename src/modules/awake/Awake/{ => Core}/NativeMethods.cs (83%) diff --git a/src/modules/awake/Awake/Core/APIHelper.cs b/src/modules/awake/Awake/Core/APIHelper.cs index 4f6d7ba8fa..49bc7a34cf 100644 --- a/src/modules/awake/Awake/Core/APIHelper.cs +++ b/src/modules/awake/Awake/Core/APIHelper.cs @@ -7,30 +7,12 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Awake.Core.Models; using Microsoft.Win32; using NLog; namespace Awake.Core { - [Flags] - public enum EXECUTION_STATE : uint - { - ES_AWAYMODE_REQUIRED = 0x00000040, - ES_CONTINUOUS = 0x80000000, - ES_DISPLAY_REQUIRED = 0x00000002, - ES_SYSTEM_REQUIRED = 0x00000001, - } - - // See: https://docs.microsoft.com/windows/console/handlerroutine - public enum ControlType - { - CTRL_C_EVENT = 0, - CTRL_BREAK_EVENT = 1, - CTRL_CLOSE_EVENT = 2, - CTRL_LOGOFF_EVENT = 5, - CTRL_SHUTDOWN_EVENT = 6, - } - public delegate bool ConsoleEventHandler(ControlType ctrlType); /// @@ -85,7 +67,7 @@ namespace Awake.Core /// /// Single or multiple EXECUTION_STATE entries. /// true if successful, false if failed - private static bool SetAwakeState(EXECUTION_STATE state) + private static bool SetAwakeState(ExecutionState state) { try { @@ -168,11 +150,11 @@ namespace Awake.Core bool success; if (keepDisplayOn) { - success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_CONTINUOUS); } else { - success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS); } try @@ -209,11 +191,11 @@ namespace Awake.Core { if (keepDisplayOn) { - success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_CONTINUOUS); } else { - success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + success = SetAwakeState(ExecutionState.ES_SYSTEM_REQUIRED | ExecutionState.ES_CONTINUOUS); } if (success) diff --git a/src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs b/src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs new file mode 100644 index 0000000000..1520662e47 --- /dev/null +++ b/src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Awake.Core.Models +{ + public struct BatteryReportingScale + { + public uint Granularity; + public uint Capacity; + } +} diff --git a/src/modules/awake/Awake/Core/Models/ControlType.cs b/src/modules/awake/Awake/Core/Models/ControlType.cs new file mode 100644 index 0000000000..5f05345cb0 --- /dev/null +++ b/src/modules/awake/Awake/Core/Models/ControlType.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Awake.Core.Models +{ + // See: https://docs.microsoft.com/windows/console/handlerroutine + public enum ControlType + { + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT = 1, + CTRL_CLOSE_EVENT = 2, + CTRL_LOGOFF_EVENT = 5, + CTRL_SHUTDOWN_EVENT = 6, + } +} diff --git a/src/modules/awake/Awake/Core/Models/ExecutionState.cs b/src/modules/awake/Awake/Core/Models/ExecutionState.cs new file mode 100644 index 0000000000..3c5ab849f1 --- /dev/null +++ b/src/modules/awake/Awake/Core/Models/ExecutionState.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Awake.Core.Models +{ + [Flags] + public enum ExecutionState : uint + { + ES_AWAYMODE_REQUIRED = 0x00000040, + ES_CONTINUOUS = 0x80000000, + ES_DISPLAY_REQUIRED = 0x00000002, + ES_SYSTEM_REQUIRED = 0x00000001, + } +} diff --git a/src/modules/awake/Awake/Core/Models/SystemPowerCapabilities.cs b/src/modules/awake/Awake/Core/Models/SystemPowerCapabilities.cs new file mode 100644 index 0000000000..c803941130 --- /dev/null +++ b/src/modules/awake/Awake/Core/Models/SystemPowerCapabilities.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace Awake.Core.Models +{ + public struct SystemPowerCapabilities + { + [MarshalAs(UnmanagedType.U1)] + public bool PowerButtonPresent; + [MarshalAs(UnmanagedType.U1)] + public bool SleepButtonPresent; + [MarshalAs(UnmanagedType.U1)] + public bool LidPresent; + [MarshalAs(UnmanagedType.U1)] + public bool SystemS1; + [MarshalAs(UnmanagedType.U1)] + public bool SystemS2; + [MarshalAs(UnmanagedType.U1)] + public bool SystemS3; + [MarshalAs(UnmanagedType.U1)] + public bool SystemS4; + [MarshalAs(UnmanagedType.U1)] + public bool SystemS5; + [MarshalAs(UnmanagedType.U1)] + public bool HiberFilePresent; + [MarshalAs(UnmanagedType.U1)] + public bool FullWake; + [MarshalAs(UnmanagedType.U1)] + public bool VideoDimPresent; + [MarshalAs(UnmanagedType.U1)] + public bool ApmPresent; + [MarshalAs(UnmanagedType.U1)] + public bool UpsPresent; + [MarshalAs(UnmanagedType.U1)] + public bool ThermalControl; + [MarshalAs(UnmanagedType.U1)] + public bool ProcessorThrottle; + public byte ProcessorMinThrottle; + public byte ProcessorMaxThrottle; + [MarshalAs(UnmanagedType.U1)] + public bool FastSystemS4; + [MarshalAs(UnmanagedType.U1)] + public bool Hiberboot; + [MarshalAs(UnmanagedType.U1)] + public bool WakeAlarmPresent; + [MarshalAs(UnmanagedType.U1)] + public bool AoAc; + [MarshalAs(UnmanagedType.U1)] + public bool DiskSpinDown; + public byte HiberFileType; + [MarshalAs(UnmanagedType.U1)] + public bool AoAcConnectivitySupported; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + private readonly byte[] spare3; + [MarshalAs(UnmanagedType.U1)] + public bool SystemBatteriesPresent; + [MarshalAs(UnmanagedType.U1)] + public bool BatteriesAreShortTerm; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public BatteryReportingScale[] BatteryScale; + public SystemPowerState AcOnLineWake; + public SystemPowerState SoftLidWake; + public SystemPowerState RtcWake; + public SystemPowerState MinDeviceWakeState; + public SystemPowerState DefaultLowLatencyWake; + } +} diff --git a/src/modules/awake/Awake/Core/Models/SystemPowerState.cs b/src/modules/awake/Awake/Core/Models/SystemPowerState.cs new file mode 100644 index 0000000000..337333612f --- /dev/null +++ b/src/modules/awake/Awake/Core/Models/SystemPowerState.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Awake.Core.Models +{ + // Maps to the OS power state. + // See documentation: https://docs.microsoft.com/windows/win32/power/system-power-states + public enum SystemPowerState + { + PowerSystemUnspecified = 0, + PowerSystemWorking = 1, + PowerSystemSleeping1 = 2, + PowerSystemSleeping2 = 3, + PowerSystemSleeping3 = 4, + PowerSystemHibernate = 5, + PowerSystemShutdown = 6, + PowerSystemMaximum = 7, + } +} diff --git a/src/modules/awake/Awake/NativeMethods.cs b/src/modules/awake/Awake/Core/NativeMethods.cs similarity index 83% rename from src/modules/awake/Awake/NativeMethods.cs rename to src/modules/awake/Awake/Core/NativeMethods.cs index 2f0bc0d107..4b80608f5b 100644 --- a/src/modules/awake/Awake/NativeMethods.cs +++ b/src/modules/awake/Awake/Core/NativeMethods.cs @@ -5,16 +5,20 @@ using System; using System.IO; using System.Runtime.InteropServices; +using Awake.Core.Models; namespace Awake.Core { internal static class NativeMethods { + [DllImport("PowrProf.dll", SetLastError = true)] + internal static extern bool GetPwrCapabilities(out SystemPowerCapabilities lpSystemPowerCapabilities); + [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool SetConsoleCtrlHandler(ConsoleEventHandler handler, bool add); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - internal static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags); + internal static extern ExecutionState SetThreadExecutionState(ExecutionState esFlags); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/modules/awake/Awake/NLog.config b/src/modules/awake/Awake/NLog.config index 5d19f72d81..f58ab302e5 100644 --- a/src/modules/awake/Awake/NLog.config +++ b/src/modules/awake/Awake/NLog.config @@ -2,12 +2,12 @@ - + ? configOption = new Option( + Option? configOption = new ( aliases: new[] { "--use-pt-config", "-c" }, getDefaultValue: () => false, - description: "Specifies whether PowerToys Awake will be using the PowerToys configuration file for managing the state.") + description: $"Specifies whether {InternalConstants.AppName} will be using the PowerToys configuration file for managing the state.") { Argument = new Argument(() => false) { @@ -76,7 +92,7 @@ namespace Awake configOption.Required = false; - Option? displayOption = new Option( + Option? displayOption = new ( aliases: new[] { "--display-on", "-d" }, getDefaultValue: () => true, description: "Determines whether the display should be kept awake.") @@ -89,7 +105,7 @@ namespace Awake displayOption.Required = false; - Option? timeOption = new Option( + Option? timeOption = new ( aliases: new[] { "--time-limit", "-t" }, getDefaultValue: () => 0, description: "Determines the interval, in seconds, during which the computer is kept awake.") @@ -102,10 +118,10 @@ namespace Awake timeOption.Required = false; - Option? pidOption = new Option( + Option? pidOption = new ( aliases: new[] { "--pid", "-p" }, getDefaultValue: () => 0, - description: "Bind the execution of PowerToys Awake to another process.") + description: $"Bind the execution of {InternalConstants.AppName} to another process.") { Argument = new Argument(() => 0) { @@ -135,9 +151,7 @@ namespace Awake private static bool ExitHandler(ControlType ctrlType) { _log.Info($"Exited through handler with control type: {ctrlType}"); - Exit("Exiting from the internal termination handler.", Environment.ExitCode); - return false; } @@ -251,7 +265,8 @@ namespace Awake { RunnerHelper.WaitForPowerToysRunner(pid, () => { - Exit("Terminating from PowerToys binding hook.", 0, true); + _log.Info($"Triggered PID-based exit handler for PID {pid}."); + Exit("Terminating from process binding hook.", 0, true); }); }