RSS icon Home icon
  • How to disable Windows auto sleep in AC mode with code

    Posted on February 17th, 2023 admin No comments

    It can be annoying if you use your computer 24/7 and when you don’t interact with the desktop, your machine will go into sleep after a while. If you’re using the PC remotely, you cannot bring it back from sleep. You can also turn off sleep mode manually in Power & sleep under System preferences by changing the “When plugged in, PC goes to sleep after” setting to Never.

    Or even better, you can modify it with the PowerCfg utility by invoking the following command:

    powercfg /x -standby-timeout-ac 0

    This should be a simple set-and-forget option, but surprisingly, this setting can sometimes revert to its original value. I didn’t spend time trying to figure out why the value can change, instead I got my supervisor application watch and reset the value. Here’s how I figured out (debug) how to do it with API calls, and of course I’ll show you the working source code (C++, C#, Delphi).

    You can debug PowerCfg in Visual Studio. Simply load the exe just as a normal project: File -> Open -> Project/Solution. To add command-line arguments right click on the Solution (powercfg in bold), then select Properties. Hit F11 and start debugging. This is a relatively simple console program so it didn’t take so long to find the required system calls.

    In Main() it starts with parsing the command line options then it goes into the DoChange() function. Only two (or three) system calls are used to change the power config.

    First, it retrieves the current power scheme by calling the PowerGetActiveScheme function.

    
    
    DWORD PowerGetActiveScheme(
       [in, optional] HKEY UserRootPowerKey,
       [out] GUID **ActivePolicyGuid );

    UserRootPowerKey is set to null
    ActivePolicyGuid receives the GUID

    Then it searches for the “standby-timeout” argument and sets the required GUIDs.

    And then it calls PowerWriteACValueIndex on the Active scheme to change the standby timeout to zero.

    Those 64-bit registers points to the three GUIDs required by the function. The first two GUIDs are defined if you include <windows.h>.

    • R8 points to GUID_STANDBY_TIMEOUT (29f6c1db-86da-48c5-9fdb-f2b67b1f44da)
    • R9 points to GUID_SLEEP_SUBGROUP (238c9fa8-0aad-41ed-83f4-97be242c8f20)
    • RDX points to the Active power scheme

    The GUID for the power scheme is not defined, it seems specific to a given computer. But in the Registry I was able to find a reference to this GUID. The key GUID is defined as GUID_TYPICAL_POWER_SAVINGS and is also documented in Power Policy Settings. But that’s no so interesting, since we’re using the value returned from the previous PowerGetActiveScheme() call.

    
    
    DWORD PowerWriteACValueIndex(
      [in, optional] HKEY RootPowerKey,
      [in] const GUID *SchemeGuid,
      [in, optional] const GUID *SubGroupOfPowerSettingsGuid,
      [in, optional] const GUID *PowerSettingGuid,
      [in] DWORD AcValueIndex
    );

    RootPowerKey is set to null
    SchemeGuid is set by the previous PowerGetActiveScheme call
    SubGroupOfPowerSettingsGuid is set to GUID_SLEEP_SUBGROUP
    PowerSettingGuid is set to GUID_STANDBY_TIMEOUT
    AcValueIndex is set to zero

    Finally it calls PowerSetActiveScheme. This step seems redundant since the Active scheme is not altered. In the documentation’s Remarks section it states that “Changes to the settings for the active power scheme do not take effect until you call the PowerSetActiveScheme function”. On my Windows 10 machine, the setting was changed without calling this function, but let’s stick with the rules.

    
    
    DWORD PowerSetActiveScheme(
      [in, optional] HKEY UserRootPowerKey,
      [in] const GUID *SchemeGuid
    );

    UserRootPowerKey is null
    SchemeGuid still points to the Active power scheme

    After all these investigation, the solution is quite simple:

    C++ Version

    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <windows.h>
    #include <powrprof.h>
    #pragma comment(lib, "powrprof.lib")

    int main()
    {
      GUID *ptrActiveScheme;
      GUID guidSleep = GUID_SLEEP_SUBGROUP;
      GUID guidStandBy = GUID_STANDBY_TIMEOUT;

      if (PowerGetActiveScheme(nullptr, &ptrActiveScheme) == ERROR_SUCCESS)
      {
        if (PowerWriteACValueIndex(nullptr, ptrActiveScheme, &guidSleep, &guidStandBy, 0) == ERROR_SUCCESS)
          PowerSetActiveScheme(nullptr, ptrActiveScheme);
        LocalFree(ptrActiveScheme);
      }
    }

    C# Version

    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    using System;
    using System.Runtime.InteropServices;

    namespace TurnOffSleep
    {
      class Program
      {
        static Guid GUID_SLEEP_SUBGROUP = new Guid("238C9FA8-0AAD-41ED-83f4-97BE242C8F20");
        static Guid GUID_STANDBY_TIMEOUT = new Guid("29F6C1DB-86DA-48C5-9FDB-F2B67B1F44DA");
        [DllImport("powrprof.dll", SetLastError = true)]
        private static extern UInt32 PowerGetActiveScheme(
          IntPtr UserRootPowerKey,
          ref IntPtr ActivePolicyGuid
        );
        [DllImport("powrprof.dll", SetLastError = true)]
        private static extern UInt32 PowerWriteACValueIndex(
          IntPtr RootPowerKey,
          ref Guid SchemeGuid,
          ref Guid SubGroupOfPowerSettingsGuid,
          ref Guid PowerSettingGuid,
          UInt32 AcValueIndex
        );
        [DllImport("powrprof.dll", SetLastError = true)]
        private static extern UInt32 PowerSetActiveScheme(
          IntPtr UserRootPowerKey,
          ref Guid SchemeGuid
        );

        static void Main(string[] args)
        {
          IntPtr ptr = IntPtr.Zero;
          if (PowerGetActiveScheme(IntPtr.Zero, ref ptr) == 0)
          {
            Guid scheme = (Guid)Marshal.PtrToStructure(ptr, typeof(Guid));
            if (PowerWriteACValueIndex(IntPtr.Zero, ref scheme, ref GUID_SLEEP_SUBGROUP, ref GUID_STANDBY_TIMEOUT, 0) == 0)
              PowerSetActiveScheme(IntPtr.Zero, ref scheme);
            Marshal.FreeHGlobal(ptr);
          }
        }
      }
    }
    
    

    Delphi Version

    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    {$APPTYPE CONSOLE}
    program TurnOffSleep;

    uses
      Windows, SysUtils;

    const
      GUID_STANDBY_TIMEOUT: TGUID = (D1:$29F6C1DB; D2:$86DA; D3:$48C5; D4:($9F, $DB, $F2, $B6, $7B, $1F, $44, $DA));
      GUID_SLEEP_SUBGROUP : TGUID = (D1:$238C9FA8; D2:$0AAD; D3:$41ED; D4:($83, $F4, $97, $BE, $24, $2C, $8F, $20));
    type
      TPowerGetActiveScheme = function (UserRootPowerKey: Pointer; var ActivePolicyGuid: PGUID): DWORD; stdcall;
      TPowerSetActiveScheme = function (UserRootPowerKey: Pointer; SchemeGuid: PGUID): DWORD; stdcall;
      TPowerWriteACValueIndex = function (RootPowerKey: Pointer; SchemeGuid: PGUID; SubGroupOfPowerSettingsGuid: PGUID; PowerSettingGuid: PGUID; AcValueIndex: DWORD): DWORD; stdcall;

    var
      ptrActiveScheme: PGUID;
      guidSleep: TGUID;
      guidStandBy: TGUID;
      hPwrDll: THandle;
      PowerGetActiveScheme: TPowerGetActiveScheme;
      PowerSetActiveScheme: TPowerSetActiveScheme;
     PowerWriteACValueIndex: TPowerWriteACValueIndex;

    begin
      hPwrDll := LoadLibrary('powrprof.dll');
      PowerGetActiveScheme := GetProcAddress(hPwrDll, 'PowerGetActiveScheme');
      PowerSetActiveScheme := GetProcAddress(hPwrDll, 'PowerSetActiveScheme');
      PowerWriteACValueIndex := GetProcAddress(hPwrDll, 'PowerWriteACValueIndex');
      guidSleep := GUID_SLEEP_SUBGROUP;
      guidStandBy := GUID_STANDBY_TIMEOUT;

      if PowerGetActiveScheme(nil, &ptrActiveScheme) = ERROR_SUCCESS then
      begin
        if PowerWriteACValueIndex(nil, ptrActiveScheme, @guidSleep, @guidStandBy, 0) = ERROR_SUCCESS then
          PowerSetActiveScheme(nil, ptrActiveScheme);
        LocalFree(ptrActiveScheme);
      end;
      FreeLibrary(hPwrDll);
    end.

    You can change the DC power settings (computer is on battery power) in the very same way, just call the PowerWriteDCValueIndex function.

    
    
    DWORD PowerWriteDCValueIndex(
      [in, optional] HKEY RootPowerKey,
      [in] const GUID *SchemeGuid,
      [in, optional] const GUID *SubGroupOfPowerSettingsGuid,
      [in, optional] const GUID *PowerSettingGuid,
      [in] DWORD DcValueIndex
    );

     

    Leave a Reply

    Your email address will not be published. Required fields are marked *