質問

I need to programmatically detect whether my computer (Windows 7 / 8) supports wake timers. So far I have done the following:

Guid activePowerScheme = GetActivePowerSchemeGuid();
IntPtr ptrActiveGuid = IntPtr.Zero;
uint buffSize = 0;
uint res = PowerReadACValue(IntPtr.Zero, ref activePowerScheme, ref ApplicationConstants.SLEEPGUID, ref ApplicationConstants.WAKETIMERGUID, IntPtr.Zero, IntPtr.Zero, ref buffSize);

if (res == 0)
{
    IntPtr ptrName = IntPtr.Zero;
    try
    {
        ptrName = Marshal.AllocHGlobal((int)buffSize);
        res = PowerReadACValue(IntPtr.Zero, ref activePowerScheme, ref ApplicationConstants.SLEEPGUID, ref ApplicationConstants.WAKETIMERGUID, IntPtr.Zero, ptrName, ref buffSize);
        byte[] ba = new byte[buffSize];
        Marshal.Copy(ptrName, ba, 0, (int)buffSize);
        int retVal = BitConverter.ToInt32(ba, 0);

        if (retVal == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }        
    catch(Exception exp)
    {
        Logger.LogException(exp);
        return false;
    }
    finally
    {
        if (ptrName != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(ptrName);
        }
    }
}

return false;

This works most of the time, but when I reset my power plan settings, this doesn't work well (inconsistent). I also tried the following:

Guid currentPowerSchemeGuid = GetActivePowerSchemeGuid();

RegistryKey currentPowerSchemeKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + currentPowerSchemeGuid.ToString());
if (currentPowerSchemeKey != null)
{
    RegistryKey sleepRegKey = currentPowerSchemeKey.OpenSubKey(ApplicationConstants.SLEEPGUID.ToString());
    currentPowerSchemeKey.Close();
    if (sleepRegKey != null)
    {
        RegistryKey wakeTimerRegKey = sleepRegey.OpenSubKey(ApplicationConstants.WAKETIMERGUID.ToString());
        sleepRegKey.Close();
        if (wakeTimerRegKey != null)
        {
            wakeTimerRegKey.Close();
            currentPowerSchemeKey.Close();
            return true;
        }
        else
        {
            currentPowerSchemeKey.Close();
            return false;
        }
    }
    else
    {
        currentPowerSchemeKey.Close();
        return false;
    }
}
else
{
    return false;
}

This doesn't work on reset of power plan settings, the wake timer GUID registry key gets cleared. Is there a proper way I can detect if my system supports wake timers?

役に立ちましたか?

解決

According to arx, tried the following code and it works.

    public static bool IsWakeTimerSupported()
    {
        IntPtr timerHandle  = CreateWaitableTimer(IntPtr.Zero, true, "Wait Timer 1");
        uint retVal         = GetLastError();
        if (timerHandle != IntPtr.Zero)
        {
            CancelWaitableTimer(timerHandle);
            CloseHandle(timerHandle);
            timerHandle     = IntPtr.Zero;
        }

        //SUCCESS
        if (retVal == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

The CancelWaitableTimer(timerHandle) can be ignored as the MSDN documentation says to use CloseHandle.

EDIT:

public static bool IsWakeTimerSupported()
{
        IntPtr timerHandle  = CreateWaitableTimer(IntPtr.Zero, true, "Wait Timer 1");
        long interval       = 0;
        int retVal          = 0;
        if (timerHandle != IntPtr.Zero)
        {
            SetWaitableTimer(timerHandle, ref interval, 0, IntPtr.Zero, IntPtr.Zero, true);
            retVal = Marshal.GetLastWin32Error();
            WaitableTimer.CancelWaitableTimer(timerHandle);
            try
            {
                Win32.CloseHandle(timerHandle);
            }
            catch (Exception exp)
            {
                Logger.LogException(exp);
            }
            timerHandle = IntPtr.Zero;
        }

        //SUCCESS
        if (retVal == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
}

According to this article, http://blogs.msdn.com/b/adam_nathan/archive/2003/04/25/56643.aspx we shoud never use GetLastError through PInvoke.

他のヒント

Using the powrprof.dll library worked for me:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Namespace {

public static class PowerOptions {

    // src: https://msdn.microsoft.com/en-us/library/windows/desktop/hh448380%28v=vs.85%29.aspx
    private readonly static Guid HIGH_PERFORMANCE = new Guid("8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"); // aka MIN_POWER_SAVINGS
    private readonly static Guid BALANCED = new Guid("381b4222-f694-41f0-9685-ff5bb260df2e"); // aka TYPICAL_POWER_SAVINGS
    private readonly static Guid POWER_SAVER = new Guid("a1841308-3541-4fab-bc81-f71556f20b4a"); // aka MAX_POWER_SAVINGS
    private readonly static Guid ACDC_POWER_SOURCE = new Guid("5d3e9a59-e9D5-4b00-a6bd-ff34ff516548");

    private readonly static Guid SLEEP_SUBCATEGORY = new Guid("238C9FA8-0AAD-41ED-83F4-97BE242C8F20");
    private readonly static Guid WAKE_TIMERS = new Guid("bd3b718a-0680-4d9d-8ab2-e1d2b4ac806d");

    public static String GetCurrentPowerPlanFriendlyName() {
        IntPtr ptrActiveGuid = IntPtr.Zero;
        int ret = PowerGetActiveScheme(IntPtr.Zero, ref ptrActiveGuid);
        if (ret == 0) {
            uint buffSize = 0;
            ret = PowerReadFriendlyName(IntPtr.Zero, ptrActiveGuid, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref buffSize);
            if (ret == 0) {
                if (buffSize == 0)
                    return "";

                IntPtr ptrName = Marshal.AllocHGlobal((int) buffSize);
                ret = PowerReadFriendlyName(IntPtr.Zero, ptrActiveGuid, IntPtr.Zero, IntPtr.Zero, ptrName, ref buffSize);
                if (ret == 0) {
                    String name = Marshal.PtrToStringUni(ptrName);
                    Marshal.FreeHGlobal(ptrName);
                    return name;
                }
                Marshal.FreeHGlobal(ptrName);
            }
        }
        throw new Win32Exception(ret, "GetCurrentPowerPlanFriendlyName");
    }

    public static PowerStatus GetPowerStatus() {
        PowerStatus ps = new PowerStatus();
        if (!GetSystemPowerStatus(ref ps))
            throw new Win32Exception(Marshal.GetLastWin32Error(), "GetPowerStatus");
        return ps;
    }

    public static bool GetWakeTimersEnabled(PowerSource powerSource = PowerSource.Current, PowerPlan powerPlan = PowerPlan.Current) {
        int ret = 0;
        if (powerSource == PowerSource.Current) {
            PowerStatus ps = GetPowerStatus();
            if (ps.ACLineStatus == PowerLineStatus.Online)
                powerSource = PowerSource.PluggedIn;
            else
                powerSource = PowerSource.OnBattery;
        }

        if (ret == 0) {
            if (powerPlan == PowerPlan.Current) {
                IntPtr ptrPowerPlan = IntPtr.Zero;
                ret = PowerGetActiveScheme(IntPtr.Zero, ref ptrPowerPlan);

                if (ret == 0) {
                    uint value = 0;
                    if (powerSource == PowerSource.PluggedIn)
                        ret = PowerReadACValueIndex(IntPtr.Zero, ptrPowerPlan, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value);
                    else
                        ret = PowerReadDCValueIndex(IntPtr.Zero, ptrPowerPlan, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value);

                    if (ret == 0) {
                        return (value == 1);
                    }
                }
            }
            else {
                Guid guid = GetGuid(powerPlan);
                uint value = 0;
                if (powerSource == PowerSource.PluggedIn)
                    ret = PowerReadACValueIndex(IntPtr.Zero, guid, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value);
                else
                    ret = PowerReadDCValueIndex(IntPtr.Zero, guid, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value);

                if (ret == 0) {
                    return (value == 1);
                }
            }
        }
        throw new Win32Exception(ret, "GetWakeTimersEnabled");
    }

    public static Guid GetGuid(PowerPlan powerPlan) {
        if (powerPlan == PowerPlan.Balanced)
            return BALANCED;
        if (powerPlan == PowerPlan.HighPerformance)
            return HIGH_PERFORMANCE;
        if (powerPlan == PowerPlan.PowerSaver)
            return POWER_SAVER;
        throw new ArgumentException("Not a standard power plan: " + powerPlan);
    }

    [DllImport("powrprof.dll", SetLastError = true)]
    public static extern int PowerWriteACValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, uint AcValueIndex);

    [DllImport("powrprof.dll", SetLastError = true)]
    private static extern int PowerReadACValueIndex(IntPtr RootPowerKey, IntPtr SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex);
    [DllImport("powrprof.dll", SetLastError = true)]
    private static extern int PowerReadACValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex);

    [DllImport("powrprof.dll", SetLastError = true)]
    private static extern int PowerReadDCValueIndex(IntPtr RootPowerKey, IntPtr SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex);
    [DllImport("powrprof.dll", SetLastError = true)]
    private static extern int PowerReadDCValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex);

    [DllImport("powrprof.dll", SetLastError = true)]
    private static extern int PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr ActivePolicyGuid);

    [DllImport("powrprof.dll", SetLastError = true)]
    private static extern int PowerReadFriendlyName(IntPtr RootPowerKey, IntPtr SchemeGuid, IntPtr SubGroupOfPowerSettingsGuid, IntPtr PowerSettingGuid, IntPtr Buffer, ref uint BufferSize);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetSystemPowerStatus(ref PowerStatus lpSystemPowerStatus);
}

public enum PowerPlan {
    Current,
    HighPerformance,
    Balanced,
    PowerSaver,
}

public enum PowerSource {
    Current,
    OnBattery,
    PluggedIn
}

public struct PowerStatus {

    ///<summary>The AC power status.</summary>
    public PowerLineStatus ACLineStatus;

    ///<summary>The battery charge status.</summary>
    public PowerChargeStatus BatteryFlag;

    ///<summary>Returns a value between [0 to 100] or 255 if unknown.</summary>
    public byte BatteryLifePercent;

    ///<summary>Returns a value that indicates if the system is currently conserving power.</summary>
    public PowerSaveStatus SystemStatusFlag;

    ///<summary>Number of seconds of battery life remaining, or -1 if unknown.</summary>
    public int BatteryLifeTime;

    ///<summary>Number of seconds of batter life on a full charge, or -1 if unknown.</summary>
    public int BatteryFullLifeTime;
}

public enum PowerLineStatus : byte {
    Offline = 0,
    Online = 1,
    Unknown = 255,
}

[Flags]
public enum PowerChargeStatus : byte {
    High = 1,
    Low = 2,
    Critical = 4,
    Charging = 8,
    NoBattery = 128,
    Unknown = 255,
}

public enum PowerSaveStatus : byte {
    Off = 0,
    On = 1,
}

}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top