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
This commit is contained in:
Den Delimarsky 2022-02-12 10:42:14 -08:00 committed by GitHub
parent dc15a6cecc
commit 3df2c5fe6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 174 additions and 38 deletions

View File

@ -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);
/// <summary>
@ -85,7 +67,7 @@ namespace Awake.Core
/// </summary>
/// <param name="state">Single or multiple EXECUTION_STATE entries.</param>
/// <returns>true if successful, false if failed</returns>
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)

View File

@ -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;
}
}

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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;
}
}

View File

@ -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,
}
}

View File

@ -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)]

View File

@ -2,12 +2,12 @@
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<variable name="awakeversion" value="0.0.1" />
<variable name="buildid" value="ARBITER_01312022" />
<targets async="true">
<target name="logfile"
xsi:type="File"
fileName="${specialfolder:folder=LocalApplicationData}/Microsoft/PowerToys/Awake/Logs/${var:awakeversion}/log_${date:format=yyyy-MM-dd_HH}_DBG.txt"
fileName="${specialfolder:folder=LocalApplicationData}/Microsoft/PowerToys/Awake/Logs/${var:awakeversion}/applog_${date:format=yyyy-MM-dd_HH}_${var:buildid}.txt"
layout="[${longdate} ${level:uppercase=true} ${logger}] ${message}"
archiveEvery="Day"
archiveNumbering="Rolling"

View File

@ -12,9 +12,10 @@ using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Windows;
using Awake.Core;
using Awake.Core.Models;
using interop;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
@ -27,6 +28,14 @@ namespace Awake
{
internal class Program
{
// PowerToys Awake build codename. Used for exact logging
// that does not map to PowerToys broad versioning to pinpoint
// internal issues easier.
// Format of the build ID is: CODENAME_MMDDYYYY, where MMDDYYYY
// is representative of the date when the last change was made before
// the pull request is issued.
private static readonly string BuildId = "ARBITER_01312022";
private static Mutex? _mutex = null;
private static FileSystemWatcher? _watcher = null;
private static SettingsUtils? _settingsUtils = null;
@ -37,6 +46,7 @@ namespace Awake
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static ConsoleEventHandler _handler;
private static SystemPowerCapabilities _powerCapabilities;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private static ManualResetEvent _exitSignal = new ManualResetEvent(false);
@ -56,17 +66,23 @@ namespace Awake
_settingsUtils = new SettingsUtils();
_log.Info("Launching PowerToys Awake...");
_log.Info($"Launching {InternalConstants.AppName}...");
_log.Info(FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion);
_log.Info($"Build: {BuildId}");
_log.Info($"OS: {Environment.OSVersion}");
_log.Info($"OS Build: {APIHelper.GetOperatingSystemBuild()}");
// To make it easier to diagnose future issues, let's get the
// system power capabilities and aggregate them in the log.
NativeMethods.GetPwrCapabilities(out _powerCapabilities);
_log.Info(JsonSerializer.Serialize(_powerCapabilities));
_log.Info("Parsing parameters...");
Option<bool>? configOption = new Option<bool>(
Option<bool>? 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<bool>(() => false)
{
@ -76,7 +92,7 @@ namespace Awake
configOption.Required = false;
Option<bool>? displayOption = new Option<bool>(
Option<bool>? 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<uint>? timeOption = new Option<uint>(
Option<uint>? 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<int>? pidOption = new Option<int>(
Option<int>? 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<int>(() => 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);
});
}