Search code examples
powershellwindows-10sleep

How to stop a windows 10 machine from sleeping/hibernating down when running a powershell process?


I have a powershell process that reads records from a remote server and copies them into a local database. When it runs, it might run for 8-12 hours.

How do I prevent the computer from shutting down (or going into sleep/hibernate mode) during this time? I know I can adjust the 'Power and sleep settings' to set the computer to never sleep, but that's not what I'm looking for - I do want it to go to sleep when the process isn't running.

I know that sleep/hibernate is suspended if a netflix or youtube video is running, I'd like the computer to do the same when a powershell process is running.

The powershell process runs in a command window on the desktop - I'm happy for the screen saver to activate, but what I don't want to happen is for me to wake the machine after 8 hours and discover that the process only ran for 10 minutes before the computer went to sleep!


Solution

  • With some extra effort, you can achieve the desired behavior with the standard powercfg.exe utility, by using a custom, always-on power scheme that is created on demand and temporarily activated for the duration of your script run:

    Note:

    • Look for comment # YOUR CODE GOES HERE below.

    • For a .NET / Windows API-based alternative, see this answer.

    # Define the properties of a custom power scheme, to be created on demand.
    $schemeGuid = 'e03c2dc5-fac9-4f5d-9948-0a2fb9009d67' # randomly created with New-Guid
    $schemeName = 'Always on'
    $schemeDescr = 'Custom power scheme to keep the system awake indefinitely.'
    
    # Helper function that ensures that the most recent powercfg.exe call succeeded.
    function assert-ok { if ($LASTEXITCODE -ne 0) { throw } }
    
    # Determine the currently active power scheme, so it can be restored at the end.
    $prevGuid = (powercfg -getactivescheme) -replace '^.+([-0-9a-f]{36}).+$', '$1'
    assert-ok
    
    # Temporarily activate a custom always-on power scheme; create it on demand.
    try {
    
      # Try to change to the custom scheme.
      powercfg -setactive $schemeGuid 2>$null
      if ($LASTEXITCODE -ne 0) { # Changing failed -> create the scheme on demand.
        # Clone the 'High performance' scheme.
        $null = powercfg -duplicatescheme SCHEME_MIN $schemeGuid
        assert-ok
        # Change its name and description.
        $null = powercfg -changename $schemeGuid $schemeName $schemeDescr
        # Activate it
        $null = powercfg -setactive $schemeGuid
        assert-ok
        # Change all settings to be always on.
        # Note: 
        #   * Remove 'monitor-timeout-ac', 'monitor-timeout-dc' if it's OK
        #     for the *display* to go to sleep.
        #   * If you make changes here, you'll have to run powercfg -delete $schemeGuid 
        #     or delete the 'Always on' scheme via the GUI for changes to take effect.
        #   * On an AC-only machine (desktop, server) the *-ac settings aren't needed.
        $settings = 'monitor-timeout-ac', 'monitor-timeout-dc', 'disk-timeout-ac', 'disk-timeout-dc', 'standby-timeout-ac', 'standby-timeout-dc', 'hibernate-timeout-ac', 'hibernate-timeout-dc'
        foreach ($setting in $settings) {
          powercfg -change $setting 0 # 0 == Never
          assert-ok
        }
      }
      
      # YOUR CODE GOES HERE.
      # In this sample, wait for the user to press Enter before exiting.
      # Before that, the 'Always on' power scheme should remain in
      # effect, and the machine shouldn't go to sleep.
      pause
    
    } finally { # Executes even when the script is aborted with Ctrl-C.
      # Reactivate the previously active power scheme.
      powercfg -setactive $prevGuid
    }
    

    You could create a wrapper script from the above, to which you pass the path of the script to execute.


    If you don't mind modifying the currently active scheme, you can use the approach shown in Kerr's answer, using per-setting powercfg -change <setting> <value-in-minutes> calls (/x / -x is an alias of /change / -change), using one of the following <setting> names in each call; passing 0 as <value-in-minutes> represents never:

    • monitor-timeout-ac
    • monitor-timeout-dc
    • disk-timeout-ac
    • disk-timeout-dc
    • standby-timeout-ac
    • standby-timeout-dc
    • hibernate-timeout-ac
    • hibernate-timeout-dc

    Note, however, that such changes are persistent, so you may want to restore the original values later, which takes extra effort.