Как определить, поддерживает ли система Windows таймеры пробуждения

Мне нужно программно определить, поддерживает ли мой компьютер (Windows 7/8) таймеры пробуждения. До сих пор я сделал следующее:

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;

Это работает в большинстве случаев, но когда я сбрасываю настройки плана питания, это не работает (непоследовательно). Я также пробовал следующее:

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

Это не работает при сбросе настроек плана питания, раздел реестра GUID таймера пробуждения очищается. Есть ли правильный способ определить, поддерживает ли моя система таймеры пробуждения?


person Aster Veigas    schedule 18.04.2014    source источник
comment
Попробуйте и посмотрите. Другими словами, используйте CreateWaitableTimer, чтобы создать таймер, и вызовите SetWaitableTimer, чтобы активировать таймер с fResume, установленным на TRUE (и временем ожидания, установленным на какой-то момент в отдаленном будущем). Если таймеры пробуждения не поддерживаются, функция завершится успешно, и GetLastError вернет ERROR_NOT_SUPPORTED. Отмените таймер с помощью CancelWaitableTimer.   -  person arx    schedule 19.04.2014
comment
Я попробовал ваше решение на планшете с Windows 8 (у которого только Windows 8), и SetWaitableTimer не возвращает ERROR_NOT_SUPPORTED. Когда я смотрю на параметры питания на планшете, опция таймеров пробуждения не видна.   -  person Aster Veigas    schedule 23.04.2014


Ответы (2)


Согласно arx, попробовал следующий код, и он работает.

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

CancelWaitableTimer(timerHandle) можно игнорировать, поскольку в документации MSDN говорится, что нужно использовать CloseHandle.

РЕДАКТИРОВАТЬ:

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

Согласно этой статье, http://blogs.msdn.com/b/adam_nathan/archive/2003/04/25/56643.aspx мы никогда не должны использовать GetLastError через PInvoke.

person Aster Veigas    schedule 19.04.2014
comment
На Windows 8.1, retVal == 0 несмотря на то, что текущие Allow wake timers отключены (как от батареи, так и от сети). - person Loathing; 27.01.2016

Использование библиотеки powrprof.dll сработало для меня:

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

}
person Loathing    schedule 27.01.2016