Search code examples
c#windowstimerwakeup

How to detect if a Windows system supports wake up timers


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?


Solution

  • 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.