using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; using Vanara.Collections; using Vanara.Extensions; using Vanara.InteropServices; using Vanara.PInvoke; using static Vanara.PInvoke.Kernel32; using static Vanara.PInvoke.PowrProf; using static Vanara.PInvoke.User32; namespace Vanara.Diagnostics { /// Indicates the status of the battery. public enum BatteryStatus { /// The battery or battery controller is not present. NotPresent, /// The battery is discharging. Discharging, /// The battery is idle. Idle, /// The battery is charging. Charging } /// Specifies the status of battery saver. public enum EnergySaverStatus { /// Battery saver is off permanently or the device is plugged in. Disabled, /// Battery saver is off now, but ready to turn on automatically. Off, /// Battery saver is on. Save energy where possible. On } /// Specifies the power capabilities of a device. [Flags] public enum PowerCapabilities { /// There is a system power button. PowerButtonPresent = 1 << 0, /// There is a system sleep button. SleepButtonPresent = 1 << 1, /// There is a lid switch. LidPresent = 1 << 2, /// The operating system supports sleep state S1. SystemS1 = 1 << 3, /// The operating system supports sleep state S2. SystemS2 = 1 << 4, /// The operating system supports sleep state S3. SystemS3 = 1 << 5, /// The operating system supports sleep state S4 (hibernation). SystemS4 = 1 << 6, /// The operating system supports power off state S5 (soft off). SystemS5 = 1 << 7, /// The system hibernation file is present. HiberFilePresent = 1 << 8, /// The system supports wake capabilities. FullWake = 1 << 9, /// The system supports video display dimming capabilities. VideoDimPresent = 1 << 10, /// The system supports APM BIOS power management features. ApmPresent = 1 << 11, /// There is an uninterruptible power supply (UPS). UpsPresent = 1 << 12, /// The system supports thermal zones. ThermalControl = 1 << 13, /// The system supports processor throttling. ProcessorThrottle = 1 << 14, /// The system supports the hybrid sleep state. FastSystemS4 = 1 << 15, /// /// The system supports fast startup (aka: hiberboot, hybrid boot, or hybrid shutdown) which is a setting that helps your PC start /// up faster after shutdown. /// Hiberboot = 1 << 16, /// /// The platform has support for ACPI wake alarm devices. For more details on wake alarm devices, please see the ACPI specification /// section 9.18. /// WakeAlarmPresent = 1 << 17, /// The system supports the S0 low power idle model. AoAc = 1 << 18, /// The system supports allowing the removal of power to fixed disk devices. DiskSpinDown = 1 << 19, /// AoAcConnectivitySupported = 1 << 20, /// There are one or more batteries in the system. SystemBatteriesPresent = 1 << 21, /// The system batteries are short-term. Short-term batteries are used in uninterruptible power supplies (UPS). BatteriesAreShortTerm = 1 << 22 } /// Represents the device's power supply status. public enum PowerSupplyStatus { /// The device has no power supply. NotPresent, /// The device has an inadequate power supply. Inadequate, /// The device has an adequate power supply. Adequate } /// /// Provides access to information about a device's battery and power supply status and configuration. This extends the capabilities /// Windows.System.Power.PowerManager to include more detail, schemes and devices. /// public static class PowerManager { private static readonly SystemPowerEventHandler Instance; static PowerManager() { Instance = new SystemPowerEventHandler(); } /// /// Occurs when the system is entering or exiting away mode. The Data property is a DWORD that indicates the current away mode state. /// /// 0x0 - The computer is exiting away mode. /// 0x1 - The computer is entering away mode. /// /// public static event EventHandler> AwayModeChanged { add => Instance.AddEvent(GUID_SYSTEM_AWAYMODE, value); remove => Instance.RemoveEvent(GUID_SYSTEM_AWAYMODE, value); } /// /// Occurs when the remaining battery capacity has changed. The granularity varies from system to system but the finest granularity /// is 1 percent. The Data member is a DWORD that indicates the current battery capacity remaining as a percentage from 0 through 100. /// public static event EventHandler> BatteryCapacityChanged { add => Instance.AddEvent(GUID_BATTERY_PERCENTAGE_REMAINING, value); remove => Instance.RemoveEvent(GUID_BATTERY_PERCENTAGE_REMAINING, value); } /// /// /// Occurs when the battery saver has been turned off or on in response to changing power conditions. This notification is useful /// for components that participate in energy conservation. These applications should register for this notification and save power /// when battery saver is on. /// /// The Data member is a DWORD that indicates battery saver state. /// /// 0x0 - Battery saver is off. /// 0x1 - Battery saver is on. Save energy where possible. /// /// public static event EventHandler> BatterySaverStatusChanged { add => Instance.AddEvent(GUID_POWER_SAVING_STATUS, value); remove => Instance.RemoveEvent(GUID_POWER_SAVING_STATUS, value); } /// /// Occurs when the current monitor's display state has changed. /// /// Windows 7, Windows Server 2008 R2, Windows Vista and Windows Server 2008: This notification is available /// starting with Windows 8 and Windows Server 2012. /// /// The Data member is a DWORD with one of the following values. /// /// 0x0 - The display is off. /// 0x1 - The display is on. /// 0x2 - The display is dimmed. /// /// public static event EventHandler> CurrentMonitorDisplayStateChanged { add => Instance.AddEvent(GUID_CONSOLE_DISPLAY_STATE, value); remove => Instance.RemoveEvent(GUID_CONSOLE_DISPLAY_STATE, value); } /// /// /// The user status associated with any session has changed. This represents the combined status of user presence across all local /// and remote sessions on the system. /// /// /// This notification is sent only services and other programs running in session 0. User-mode applications should register for /// GUID_SESSION_USER_PRESENCE instead. /// /// /// Windows 7, Windows Server 2008 R2, Windows Vista and Windows Server 2008: This notification is available /// starting with Windows 8 and Windows Server 2012. /// /// The Data member is a DWORD with one of the following values. /// /// PowerUserPresent (0) - The user is present in any local or remote session on the system. /// PowerUserInactive (2) - The user is not present in any local or remote session on the system. /// /// public static event EventHandler> GlobalUserStatusChanged { add => Instance.AddEvent(GUID_GLOBAL_USER_PRESENCE, value); remove => Instance.RemoveEvent(GUID_GLOBAL_USER_PRESENCE, value); } /// /// /// The active power scheme personality has changed. All power schemes map to one of these personalities. The Data member is a GUID /// that indicates the new active power scheme personality. /// /// /// GUID_MIN_POWER_SAVINGS (8C5E7FDA-E8BF-4A96-9A85-A6E23A8C635C) /// High Performance - The scheme is designed to deliver maximum performance at the expense of power consumption savings. /// /// GUID_MAX_POWER_SAVINGS (A1841308-3541-4FAB-BC81-F71556F20B4A) /// /// Power Saver - The scheme is designed to deliver maximum power consumption savings at the expense of system performance and responsiveness. /// /// /// GUID_TYPICAL_POWER_SAVINGS (381B4222-F694-41F0-9685-FF5BB260DF2E) /// Automatic - The scheme is designed to automatically balance performance and power consumption savings. /// /// /// public static event EventHandler> PowerSchemePersonalityChanged { add => Instance.AddEvent(GUID_POWERSCHEME_PERSONALITY, value); remove => Instance.RemoveEvent(GUID_POWERSCHEME_PERSONALITY, value); } /// /// Occurs when there is a change in the power status of the computer, such as a switch from battery power to A/C. The system also /// broadcasts this event when remaining battery power slips below the threshold specified by the user or if the battery power /// changes by a specified percentage. /// public static event EventHandler PowerStatusChanged; /// /// /// The primary system monitor has been powered on or off. This notification is useful for components that actively render content /// to the display device, such as media visualization. These applications should register for this notification and stop rendering /// graphics content when the monitor is off to reduce system power consumption. The Data member is a DWORD that indicates the /// current monitor state. /// /// /// 0x0 - The monitor is off. /// 0x1 - The monitor is on. /// /// /// Windows 8 and Windows Server 2012: New applications should use GUID_CONSOLE_DISPLAY_STATE instead of this notification. /// /// public static event EventHandler> PrimarySystemMonitorPowerChanged { add => Instance.AddEvent(GUID_MONITOR_POWER_ON, value); remove => Instance.RemoveEvent(GUID_MONITOR_POWER_ON, value); } /// /// Occurs when the system is resuming from sleep or hibernation. This event is delivered every time the system resumes and does not /// indicate whether a user is present. /// public static event EventHandler ResumedAfterSleep; /// Occurs when the system has resumed operation after being suspended. public static event EventHandler ResumedAfterSuspend; /// /// Occurs when the display associated with the application's session has been powered on or off. /// /// Windows 7, Windows Server 2008 R2, Windows Vista and Windows Server 2008: This notification is available /// starting with Windows 8 and Windows Server 2012. /// /// /// This notification is sent only to user-mode applications.Services and other programs running in session 0 do not receive this notification. /// /// The Data member is a DWORD with one of the following values. /// /// 0x0 - The display is off. /// 0x1 - The display is on. /// 0x2 - The display is dimmed. /// /// public static event EventHandler> SessionDisplayStatusChanged { add => Instance.AddEvent(GUID_SESSION_DISPLAY_STATUS, value); remove => Instance.RemoveEvent(GUID_SESSION_DISPLAY_STATUS, value); } /// /// The user status associated with the application's session has changed. /// /// Windows 7, Windows Server 2008 R2, Windows Vista and Windows Server 2008: This notification is available /// starting with Windows 8 and Windows Server 2012. /// /// /// This notification is sent only to user-mode applications running in an interactive session. Services and other programs running /// in session 0 should register for GUID_GLOBAL_USER_PRESENCE. The Data member is a DWORD with one of the /// following values. /// /// /// PowerUserPresent(0) - The user is providing input to the session. /// PowerUserInactive (2) - The user activity timeout has elapsed with no interaction from the user. /// /// public static event EventHandler> SessionUserStatusChanged { add => Instance.AddEvent(GUID_SESSION_USER_PRESENCE, value); remove => Instance.RemoveEvent(GUID_SESSION_USER_PRESENCE, value); } /// Occurs when the computer is about to enter a suspended state. public static event EventHandler Suspending; /// /// Occurs when the system is busy. This indicates that the system will not be moving into an idle state in the near future and that /// the current time is a good time for components to perform background or idle tasks that would otherwise prevent the computer /// from entering an idle state. /// /// There is no notification when the system is able to move into an idle state.The idle background task notification does not /// indicate whether a user is present at the computer.The Data member has no information and can be ignored. /// /// public static event EventHandler> SystemIsBusy { add => Instance.AddEvent(GUID_IDLE_BACKGROUND_TASK, value); remove => Instance.RemoveEvent(GUID_IDLE_BACKGROUND_TASK, value); } /// /// /// Occurs when the system power source has changed. The Data member is a DWORD with values from the SYSTEM_POWER_CONDITION /// enumeration that indicates the current power source. /// /// /// 0 - Indicates the system is being powered by an AC power source. /// 1 - Indicates the system is being powered by a DC power source. /// /// 2 - Indicates the system is being powered by a short-term DC power source. For example, this would be the case if the system is /// being powered by a short-term battery supply in a backing UPS system. When this value is received, the consumer should make /// preparations for either a system hibernate or system shutdown. /// /// /// public static event EventHandler> SystemPowerSourceChanged { add => Instance.AddEvent(GUID_ACDC_POWER_SOURCE, value); remove => Instance.RemoveEvent(GUID_ACDC_POWER_SOURCE, value); } internal static event EventHandler LowBattery; internal static event CancelEventHandler QueryStandby; internal static event CancelEventHandler QuerySuspend; internal static event EventHandler ResumedAfterFailure; internal static event EventHandler ResumedAfterStandby; internal static event EventHandler StandbyFailed; internal static event EventHandler StandingBy; internal static event EventHandler SuspendFailed; /// Gets the device's battery status. /// Returns a value. public static BatteryStatus BatteryStatus => GetStatus().BatteryFlag switch { BATTERY_STATUS.BATTERY_CHARGING => BatteryStatus.Charging, BATTERY_STATUS.BATTERY_NONE => BatteryStatus.NotPresent, _ => GetStatus().ACLineStatus == AC_STATUS.AC_OFFLINE ? BatteryStatus.Discharging : BatteryStatus.Idle, }; /// Gets flags indicating the system's power capabilities. /// Returns a value. public static PowerCapabilities DeviceCapabilities { get { SYSTEM_POWER_CAPABILITIES c = GetCapabilities(); PowerCapabilities ret = 0; if (c.PowerButtonPresent) ret |= PowerCapabilities.PowerButtonPresent; if (c.SleepButtonPresent) ret |= PowerCapabilities.SleepButtonPresent; if (c.LidPresent) ret |= PowerCapabilities.LidPresent; if (c.SystemS1) ret |= PowerCapabilities.SystemS1; if (c.SystemS2) ret |= PowerCapabilities.SystemS2; if (c.SystemS3) ret |= PowerCapabilities.SystemS3; if (c.SystemS4) ret |= PowerCapabilities.SystemS4; if (c.SystemS5) ret |= PowerCapabilities.SystemS5; if (c.HiberFilePresent) ret |= PowerCapabilities.HiberFilePresent; if (c.FullWake) ret |= PowerCapabilities.FullWake; if (c.VideoDimPresent) ret |= PowerCapabilities.VideoDimPresent; if (c.ApmPresent) ret |= PowerCapabilities.ApmPresent; if (c.UpsPresent) ret |= PowerCapabilities.UpsPresent; if (c.ThermalControl) ret |= PowerCapabilities.ThermalControl; if (c.ProcessorThrottle) ret |= PowerCapabilities.ProcessorThrottle; if (c.FastSystemS4) ret |= PowerCapabilities.FastSystemS4; if (c.Hiberboot) ret |= PowerCapabilities.Hiberboot; if (c.WakeAlarmPresent) ret |= PowerCapabilities.WakeAlarmPresent; if (c.AoAc) ret |= PowerCapabilities.AoAc; if (c.DiskSpinDown) ret |= PowerCapabilities.DiskSpinDown; if (c.AoAcConnectivitySupported) ret |= PowerCapabilities.AoAcConnectivitySupported; if (c.SystemBatteriesPresent) ret |= PowerCapabilities.SystemBatteriesPresent; if (c.BatteriesAreShortTerm) ret |= PowerCapabilities.BatteriesAreShortTerm; return ret; } } /// Gets the device's battery saver status, indicating when to save energy. /// Returns a value. public static EnergySaverStatus EnergySaverStatus { get { SYSTEM_POWER_STATUS s = GetStatus(); return s.ACLineStatus == AC_STATUS.AC_ONLINE || s.BatteryFlag == BATTERY_STATUS.BATTERY_NONE ? EnergySaverStatus.Disabled : (s.SystemStatusFlag == 0 ? EnergySaverStatus.Off : EnergySaverStatus.On); } } /// Gets a value indicating whether the system is on AC power (plugged in). /// if on AC power; otherwise, . public static bool OnACPower => GetStatus().ACLineStatus == AC_STATUS.AC_ONLINE; /// Indicates the OEM's preferred power management profile for this device. public static POWER_PLATFORM_ROLE PlatformRole => PInvokeClient.Windows8.IsPlatformSupported() ? PowerDeterminePlatformRoleEx(PowerPlatformRoleVersion.POWER_PLATFORM_ROLE_V2) : PowerDeterminePlatformRole(); /// A collection of the powered devices on the system. Filters on this list can be set as properties. /// Returns a value. public static PoweredDeviceCollection PoweredDevices { get; } = new PoweredDeviceCollection(); /// Gets the device's power supply status. /// Returns a value. public static PowerSupplyStatus PowerSupplyStatus => GetStatus().ACLineStatus switch { AC_STATUS.AC_ONLINE => PowerSupplyStatus.Adequate, AC_STATUS.AC_LINE_BACKUP_POWER => PowerSupplyStatus.Inadequate, _ => PowerSupplyStatus.NotPresent, }; /// Gets the total percentage of charge remaining from all batteries connected to the device. /// Returns a int? value from 0 to 100, or if the status is unknown. public static int? RemainingChargePercent { get { var p = GetStatus().BatteryLifePercent; return p == 255 ? (int?)null : p; } } /// Gets the total runtime remaining from all batteries connected to the device. /// /// The total runtime remaining from all batteries connected to the device, or if the status is unknown or /// connected to AC power. /// public static TimeSpan? RemainingDischargeTime { get { var s = GetStatus().BatteryLifeTime; return s == uint.MaxValue ? (TimeSpan?)null : TimeSpan.FromSeconds(s); } } /// Gets the collection of all power schemes on this device. /// Returns a value. public static PowerSchemeCollection Schemes { get; } = new PowerSchemeCollection(); internal static string NameForGuid(Guid guid) => typeof(PowrProf).GetFields(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(i => i.FieldType == typeof(Guid) && guid.Equals(i.GetValue(null)))?.Name; private static SYSTEM_POWER_CAPABILITIES GetCapabilities() => GetPwrCapabilities(out SYSTEM_POWER_CAPABILITIES c) ? c : default; private static SYSTEM_POWER_STATUS GetStatus() => GetSystemPowerStatus(out SYSTEM_POWER_STATUS s) ? s : default; private class SystemPowerEventHandler : SystemEventHandler { private readonly Dictionary regs = new Dictionary(); protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { lock (regs) { foreach (SafeHPOWERSETTINGNOTIFY reg in regs.Values) reg.Dispose(); regs.Clear(); } } } protected override bool MessageFilter(HWND hwnd, uint msg, IntPtr wParam, IntPtr lParam, out IntPtr lReturn) { System.Diagnostics.Debug.WriteLine($"Message={msg}"); lReturn = default; switch (msg) { case (uint)WindowMessage.WM_POWERBROADCAST: var pbt = (PowerBroadcastType)wParam.ToInt32(); System.Diagnostics.Debug.WriteLine($"WM_POWERBROADCAST : {pbt}"); switch (pbt) { case PowerBroadcastType.PBT_APMQUERYSUSPEND: var qscancel = new CancelEventArgs(); QuerySuspend?.Invoke(null, qscancel); lReturn = qscancel.Cancel ? BROADCAST_QUERY_DENY : new IntPtr(-1); return true; case PowerBroadcastType.PBT_APMQUERYSTANDBY: var qsbcancel = new CancelEventArgs(); QueryStandby?.Invoke(null, qsbcancel); lReturn = qsbcancel.Cancel ? BROADCAST_QUERY_DENY : new IntPtr(-1); return true; case PowerBroadcastType.PBT_APMQUERYSUSPENDFAILED: SuspendFailed?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMQUERYSTANDBYFAILED: StandbyFailed?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMSUSPEND: Suspending?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMSTANDBY: StandingBy?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMRESUMECRITICAL: ResumedAfterFailure?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMRESUMESUSPEND: ResumedAfterSuspend?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMRESUMESTANDBY: ResumedAfterStandby?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMBATTERYLOW: LowBattery?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMPOWERSTATUSCHANGE: PowerStatusChanged?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_APMOEMEVENT: break; case PowerBroadcastType.PBT_APMRESUMEAUTOMATIC: ResumedAfterSleep?.Invoke(null, EventArgs.Empty); break; case PowerBroadcastType.PBT_POWERSETTINGCHANGE: POWERBROADCAST_SETTING setting = lParam.ToStructure(); switch (setting.PowerSetting) { case var g when g == GUID_IDLE_BACKGROUND_TASK: RaiseEvent(g); break; case var g when g == GUID_POWERSCHEME_PERSONALITY: RaiseEvent(g, setting); break; case var g when g == GUID_ACDC_POWER_SOURCE: RaiseEvent(g, setting); break; //case var g when g == GUID_CONSOLE_DISPLAY_STATE: //case var g when g == GUID_BATTERY_PERCENTAGE_REMAINING: //case var g when g == GUID_GLOBAL_USER_PRESENCE: //case var g when g == GUID_MONITOR_POWER_ON: //case var g when g == GUID_POWER_SAVING_STATUS: //case var g when g == GUID_SESSION_DISPLAY_STATUS: //case var g when g == GUID_SESSION_USER_PRESENCE: //case var g when g == GUID_SYSTEM_AWAYMODE: default: RaiseEvent(setting.PowerSetting, setting); break; } break; default: break; } break; } return false; } protected override void OnEventAdd(Guid key) { lock (regs) regs.Add(key, RegisterNotification(key)); } protected override void OnEventRemove(Guid key) { lock (regs) { if (regs.TryGetValue(key, out SafeHPOWERSETTINGNOTIFY reg)) { reg.Dispose(); regs.Remove(key); } } } private void RaiseEvent(Guid key) { if (HasKey(key)) RaiseEvent(key, null, new PowerEventArgs(null)); } private void RaiseEvent(Guid key, in POWERBROADCAST_SETTING pbs) { object value = typeof(T) switch { var t when t == typeof(uint) => BitConverter.ToUInt32(pbs.Data, 0), var t when t == typeof(Guid) => new Guid(pbs.Data), _ => throw new InvalidOperationException("Unrecognized invoke type.") }; if (HasKey(key)) RaiseEvent(key, null, new PowerEventArgs((T)value)); } private SafeHPOWERSETTINGNOTIFY RegisterNotification(Guid key) => Win32Error.ThrowLastErrorIfInvalid(RegisterPowerSettingNotification((IntPtr)MessageWindowHandle, key, User32.DEVICE_NOTIFY.DEVICE_NOTIFY_WINDOW_HANDLE)); } } /// Represents a device on the system that has power requirements. public class PoweredDevice { private readonly bool hw; internal PoweredDevice(string id, bool isHW) { Id = id; hw = isHW; } /// Gets or sets the identifier for the device. /// The identifier. public string Id { get; protected set; } /// Gets or sets a value indicating whether the device is wake enabled. /// if wake enabled; otherwise, . public bool WakeEnabled { get => PoweredDeviceCollection.GetEnumValues((hw ? PDQUERY.DEVICEPOWER_HARDWAREID : 0) | PDQUERY.DEVICEPOWER_FILTER_WAKEENABLED).Any(s => s == Id); set => DevicePowerSetDeviceState(Id, value ? PDSET.DEVICEPOWER_SET_WAKEENABLED : PDSET.DEVICEPOWER_CLEAR_WAKEENABLED); } /// public override string ToString() => Id; } /// Retrieves the list, optionally filtered, of the powered devices on the system. /// public class PoweredDeviceCollection : VirtualDictionary { private static Finalizer finalizer; internal PoweredDeviceCollection() : base(true) { } /// public override ICollection Keys => GetEnumValues(PDQUERY).ToList(); /// Gets or sets a value indicating whether to display only devices that are currently present in the system. /// to retrieve only present devices; otherwise, . public bool OnlyPresentDevices { get; set; } /// Gets or sets a value indicating whether to display only devices that are wake-enabled. /// if to retrieve only wake enabled devices; otherwise, . public bool OnlyWakeEnabledDevices { get; set; } /// Gets or sets a value indicating whether to display the hardware identifier rather than a friendly name. /// /// to display the hardware identifier; otherwise, to display the friendly name. /// public bool UseHardwareId { get; set; } /// public override ICollection Values => GetEnumValues(PDQUERY).Select(s => new PoweredDevice(s, UseHardwareId)).ToList(); /// protected override IEnumerable> Items => GetEnumValues(PDQUERY).Select(s => new KeyValuePair(s, new PoweredDevice(s, UseHardwareId))); private PDQUERY PDQUERY => (UseHardwareId ? PDQUERY.DEVICEPOWER_HARDWAREID : 0) | (OnlyPresentDevices ? PDQUERY.DEVICEPOWER_FILTER_DEVICES_PRESENT : 0) | (OnlyWakeEnabledDevices ? PDQUERY.DEVICEPOWER_FILTER_WAKEENABLED : 0); /// public override bool TryGetValue(string key, out PoweredDevice value) { var ret = GetEnumValues(UseHardwareId ? PDQUERY.DEVICEPOWER_HARDWAREID : 0).Contains(key); value = ret ? new PoweredDevice(key, UseHardwareId) : null; return ret; } internal static IEnumerable GetEnumValues(PDQUERY filter = 0, PDCAP flags = 0) { const uint bufSz = 1024; ValidateOpened(); var sz = bufSz; var sb = new StringBuilder((int)sz / 2, (int)sz / 2); for (var i = 0U; ; i++) { sb.Clear(); if (DevicePowerEnumDevices(i, filter, flags, sb, ref sz)) yield return sb.ToString(); else break; } } private static void ValidateOpened() { if (finalizer is null) finalizer = new Finalizer(); } private class Finalizer { internal Finalizer() => DevicePowerOpen(); ~Finalizer() => DevicePowerClose(); } } /// Event arguments for power events. /// The data type. /// public class PowerEventArgs : EventArgs { /// Initializes a new instance of the class. /// The data value. public PowerEventArgs(T value) => Data = value; /// Gets the data associated with the power event. /// The data. public T Data { get; } } /// Represents a system power scheme (power plan). public class PowerScheme : IEquatable, IEquatable { /// The well-known, system defined Balance (or Typical) power scheme with fairly aggressive power savings measures. public static readonly PowerScheme Balanced = new PowerScheme(GUID_TYPICAL_POWER_SAVINGS); /// The well-known, system defined High Performance (or Minimum) power scheme with almost no power savings measures. public static readonly PowerScheme HighPerformance = new PowerScheme(GUID_MIN_POWER_SAVINGS); /// /// The well-known, system defined Power Saver (or Max) power scheme with very aggressive power savings measures to help stretch /// battery life. /// public static readonly PowerScheme PowerSaver = new PowerScheme(GUID_MAX_POWER_SAVINGS); internal PowerScheme(Guid g) { Guid = g; Groups = new PowerSchemeGroupCollection(g); } /// Gets the active power scheme for the system. /// Returns a value for the active scheme. public static PowerScheme Active => PowerGetActiveScheme(out Guid g).Succeeded ? new PowerScheme(g) : throw new SystemException(); /// Gets the variable name of the GUID defined in the Windows SDK for this scheme. /// The API based variable name. public string ApiName => PowerManager.NameForGuid(Guid); /// Gets or sets the description of the scheme. /// The description. public string Description { get => PowerReadDescription(Guid); set => PowerWriteDescription(Guid, null, null, value).ThrowIfFailed(); } /// Gets the subgroups defined for this scheme. /// Returns a value. public PowerSchemeGroupCollection Groups { get; } /// Gets or sets a value indicating whether this scheme is the active scheme for the system. /// if this instance is active; otherwise, . public bool IsActive { get => PowerGetActiveScheme(out Guid g).Succeeded && Guid.Equals(g); set => PowerSetActiveScheme(default, Guid).ThrowIfFailed(); } /// Gets or sets the friendly name of the scheme. /// The friendly name. public string Name { get => PowerReadFriendlyName(Guid); set => PowerWriteFriendlyName(Guid, null, null, value).ThrowIfFailed(); } /// Gets the underlying GUID for the scheme. /// Returns a value. protected internal Guid Guid { get; } /// Returns a duplicates this scheme that can be edited and set as the new active scheme. /// An editable duplicate of the current scheme. public PowerScheme Duplicate() { PowerDuplicateScheme(default, Guid, out SafeLocalHandle h).ThrowIfFailed(); return new PowerScheme(h.ToStructure()); } /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// if the specified is equal to this instance; otherwise, . public bool Equals(Guid other) => Guid.Equals(other); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// if the specified is equal to this instance; otherwise, . public bool Equals(PowerScheme other) => !(other is null) && Equals(other.Guid); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// if the specified is equal to this instance; otherwise, . public override bool Equals(object obj) => obj is Guid g ? Equals(g) : obj is PowerScheme s && Equals(s); /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => Guid.GetHashCode(); /// public override string ToString() => Name + (IsActive ? " (Active)" : ""); } /// Represents a collection of all the power schemes available on the system. /// public class PowerSchemeCollection : VirtualDictionary { internal PowerSchemeCollection() : base(!CanUserWritePwrScheme()) { } /// public override ICollection Keys => PowerEnumerate(null, null).ToList(); /// Imports a power scheme from a file. /// The path to a power scheme backup file created by PowerCfg.Exe /Export. /// A derived from the file. public static PowerScheme ImportFromFile(string importFilePath) { PowerImportPowerScheme(default, importFilePath, out SafeLocalHandle memG).ThrowIfFailed(); return new PowerScheme(memG.ToStructure()); } /// /// Replaces the default power schemes with the current user's power schemes. This allows an administrator to change the default /// power schemes for the system. Replacing the default schemes enables users to use the Restore Defaults option in the /// Control Panel Power Options application to restore customized power scheme defaults instead of the original Windows power /// scheme defaults. /// /// The caller must be a member of the local Administrators group. public static void ReplaceDefaultPowerSchemes() => PowerReplaceDefaultPowerSchemes().ThrowIfFailed(); /// /// Replaces the power schemes for the system with default power schemes. All current power schemes and settings are deleted and /// replaced with the default system power schemes. /// /// The caller must be a member of the local Administrators group. public static void RestoreDefaultPowerSchemes() => PowerRestoreDefaultPowerSchemes().ThrowIfFailed(); /// public override bool Remove(Guid key) => PowerDeleteScheme(default, key).Succeeded; /// public override bool TryGetValue(Guid key, out PowerScheme value) { value = new PowerScheme(key); return true; } // public static POWER_POLICY ActivePowerPolicy => GetCurrentPowerPolicies(out var _, out var p) ? p : default; public static // GLOBAL_POWER_POLICY GlobalPowerPolicy => GetCurrentPowerPolicies(out var p, out var _) ? p : default; } /// Represents a subgroup of a system power scheme (power plan). public class PowerSchemeGroup { /// The scheme's guid. protected Guid scheme; /// The scheme's subgroup's guid. protected Guid subgroup; internal PowerSchemeGroup(Guid scheme, Guid group) { this.scheme = scheme; subgroup = group; Settings = new PowerSchemeSettingCollection(scheme, subgroup); } /// Gets the variable name of the GUID defined in the Windows SDK for this subgroup. /// The API based variable name. public string ApiName => PowerManager.NameForGuid(subgroup); /// Gets or sets the description of the subgroup. /// The description. public string Description { get => PowerReadDescription(scheme, subgroup); set => PowerWriteDescription(scheme, subgroup, null, value).ThrowIfFailed(); } /// Gets or sets the friendly name of the subgroup. /// The friendly name. public string Name { get => PowerReadFriendlyName(scheme, subgroup); set => PowerWriteFriendlyName(scheme, subgroup, null, value).ThrowIfFailed(); } /// Gets the settings defined for this subgroup. /// Returns a value. public PowerSchemeSettingCollection Settings { get; } /// public override string ToString() => Name; } /// Represents a collection of all the subgroups available under a power scheme on the system. public class PowerSchemeGroupCollection : VirtualDictionary { /// The scheme's guid. protected Guid scheme; internal PowerSchemeGroupCollection(Guid scheme) : base(false) => this.scheme = scheme; /// public override ICollection Keys => PowerEnumerate(scheme, null).Concat(new[] { NO_SUBGROUP_GUID }).ToList(); /// public override bool TryGetValue(Guid key, out PowerSchemeGroup value) { value = new PowerSchemeGroup(scheme, key); return true; } } /// Represents a setting on a subgroup. public class PowerSchemeSetting { /// The scheme's guid. protected Guid scheme; /// The scheme's setting guid. protected Guid setting; /// The scheme's subgroup guid. protected Guid subgroup; internal PowerSchemeSetting(Guid scheme, Guid group, Guid setting) { this.scheme = scheme; subgroup = group; this.setting = setting; } private delegate Win32Error IdxWriter(HKEY rootSystemPowerKey, in Guid schemePersonalityGuid, in Guid subGroupOfPowerSettingsGuid, in Guid powerSettingGuid, uint index); /// Retrieves the AC power value for the specified power setting. /// Returns the data value. public object ACValue { get { var sz = 0U; PowerReadACValue(default, scheme, subgroup, setting, out _, IntPtr.Zero, ref sz).ThrowIfFailed(); using var mem = new SafeHGlobalHandle((int)sz); PowerReadACValue(default, scheme, subgroup, setting, out REG_VALUE_TYPE regType, mem, ref sz).ThrowIfFailed(); return regType.GetValue(mem, sz); } } /// Gets or sets the default AC index of the specified power setting. /// The default AC index. public int ACValueDefaultIndex { get => PowerReadACDefaultIndex(default, scheme, subgroup, setting, out var i).Succeeded ? (int)i : -1; set => WriteIndex(PowerWriteACDefaultIndex, value); } /// Gets or sets the AC index of the specified power setting. /// The AC index. public int ACValueIndex { get => PowerReadACValueIndex(default, scheme, subgroup, setting, out var i).Succeeded ? (int)i : -1; set => WriteIndex(PowerWriteACValueIndex, value); } /// Gets the variable name of the GUID defined in the Windows SDK for this setting. /// The API based variable name. public string ApiName => PowerManager.NameForGuid(setting); /// Retrieves the DC power value for the specified power setting. /// Returns the data value. public object DCValue { get { var sz = 0U; PowerReadDCValue(default, scheme, subgroup, setting, out _, IntPtr.Zero, ref sz).ThrowIfFailed(); using var mem = new SafeHGlobalHandle((int)sz); PowerReadDCValue(default, scheme, subgroup, setting, out REG_VALUE_TYPE regType, mem, ref sz).ThrowIfFailed(); return regType.GetValue(mem, sz); } } /// Gets or sets the default DC index of the specified power setting. /// The default DC index. public int DCValueDefaultIndex { get => PowerReadDCDefaultIndex(default, scheme, subgroup, setting, out var i).Succeeded ? (int)i : -1; set => WriteIndex(PowerWriteDCDefaultIndex, value); } /// Gets or sets the DC index of the specified power setting. /// The DC index. public int DCValueIndex { get => PowerReadDCValueIndex(default, scheme, subgroup, setting, out var i).Succeeded ? (int)i : -1; set => WriteIndex(PowerWriteDCValueIndex, value); } /// Gets or sets the description of the setting. /// The description. public string Description { get => PowerReadDescription(scheme, subgroup, setting); set { PowerWriteDescription(scheme, subgroup, setting, value).ThrowIfFailed(); PowerSetActiveScheme(default, scheme).ThrowIfFailed(); } } /// Gets a value indicating whether a range is defined. /// if a range is defined; otherwise, . public bool IsRange => PowerIsSettingRangeDefined(subgroup, setting); /// Gets or sets the friendly name of the setting. /// The friendly name. public string Name { get => PowerReadFriendlyName(scheme, subgroup, setting); set { PowerWriteFriendlyName(scheme, subgroup, setting, value).ThrowIfFailed(); PowerSetActiveScheme(default, scheme).ThrowIfFailed(); } } /// Gets the possible values for this setting. /// Returns a value. public IEnumerable<(object value, string name, string description)> PossibleValues { get { if (IsRange) yield break; for (var i = 0U; ; i++) { Win32Error err = FunctionHelper.CallMethodWithStrBuf((StringBuilder sb, ref uint ssz) => PowerReadPossibleFriendlyName(default, subgroup, setting, i, sb, ref ssz), out var name, (ssz, r) => ssz > 0); if (err.Failed) break; err = FunctionHelper.CallMethodWithStrBuf((StringBuilder sb, ref uint ssz) => PowerReadPossibleDescription(default, subgroup, setting, i, sb, ref ssz), out var desc, (ssz, r) => ssz > 0); if (err.Failed) break; var sz = 0U; object obj = null; err = PowerReadPossibleValue(default, subgroup, setting, out REG_VALUE_TYPE regType, i, IntPtr.Zero, ref sz); if (err.Succeeded) { using var mem = new SafeHGlobalHandle((int)sz); err = PowerReadPossibleValue(default, subgroup, setting, out regType, i, mem, ref sz); if (err.Failed) break; obj = regType.GetValue(mem, sz); } yield return (obj, name, desc); } } } /// Gets or sets the range information for this setting. /// The range detail (min/max value, increment and unit specifier. /// Setting is not a range value. public (uint min, uint max, uint increment, string unitsSpecifier) Range { get { try { if (IsRange) { PowerReadValueMin(default, subgroup, setting, out var min).ThrowIfFailed(); PowerReadValueMax(default, subgroup, setting, out var max).ThrowIfFailed(); PowerReadValueIncrement(default, subgroup, setting, out var incr).ThrowIfFailed(); FunctionHelper.CallMethodWithStrBuf((StringBuilder sb, ref uint sz) => PowerReadValueUnitsSpecifier(default, subgroup, setting, sb, ref sz), out var spec, (sz, r) => sz > 0).ThrowIfFailed(); return (min, max, incr, spec); } } catch { } return (0, 0, 0, null); } set { if (!IsRange) throw new InvalidOperationException("Setting is not a range value."); PowerWriteValueMin(default, subgroup, setting, value.min).ThrowIfFailed(); PowerWriteValueMax(default, subgroup, setting, value.max).ThrowIfFailed(); PowerWriteValueIncrement(default, subgroup, setting, value.increment).ThrowIfFailed(); PowerWriteValueUnitsSpecifier(default, subgroup, setting, value.unitsSpecifier, (uint)((value.unitsSpecifier?.Length + 1) * 2 ?? 0)).ThrowIfFailed(); PowerSetActiveScheme(default, scheme).ThrowIfFailed(); } } private void WriteIndex(IdxWriter func, int value) { func(default, scheme, subgroup, setting, unchecked((uint)value)).ThrowIfFailed(); PowerSetActiveScheme(default, scheme).ThrowIfFailed(); } } /// Represents a collection of all settings for a subgroup and power scheme on the system. public class PowerSchemeSettingCollection : VirtualDictionary { /// The scheme's guid. protected Guid scheme; /// The scheme's subgroup guid. protected Guid subgroup; internal PowerSchemeSettingCollection(Guid scheme, Guid subgroup) : base(false) { this.scheme = scheme; this.subgroup = subgroup; } /// public override ICollection Keys => PowerEnumerate(scheme, subgroup).ToList(); /// public override bool TryGetValue(Guid key, out PowerSchemeSetting value) { value = new PowerSchemeSetting(scheme, subgroup, key); return true; } } }