Search code examples
powershellsleepsuspend

How can I use a script to keep a remote computer from sleep mode?


We're trying to create a script that tells a list of computers to stay awake.

I've shopped around, and decided to go with one I believe I found here, but I can't get it to work on a remote system. I tried to invoke-command before, then -filepath to the script, and specified the computer name, but it does nothing. What am I doing wrong?

function workflow-Scrollock {
  Clear-Host
  Echo "Keep-alive with Scroll Lock..."
  $WShell = New-Object -com "Wscript.Shell"
  while ($true)
  {
    $WShell.sendkeys("{CAPSLOCK}")
    Start-Sleep -Milliseconds 2000
    $WShell.sendkeys("{CAPSLOCK}")
    Start-Sleep -Seconds 2
  }
}

I want to see the remote computer turn it's capslock on and off until I reboot it or stop the script.


Solution

  • As long as your primary goal is keeping the computer awake (and not flashing the Caps Lock key), this answer to a related question should help. Basically you just need to slam all that C# into an Add-Type with a few modifications, eg:

    Add-Type -TypeDefinition @"
        using System;
        using System.Net;
        using System.Runtime.InteropServices;
    
        public class PowerManager {
            POWER_REQUEST_CONTEXT _PowerRequestContext;
            IntPtr _PowerRequest; // HANDLE
    
            // Availability Request Functions
            [DllImport("kernel32.dll")]
            static extern IntPtr PowerCreateRequest(ref POWER_REQUEST_CONTEXT Context);
    
            [DllImport("kernel32.dll")]
            static extern bool PowerSetRequest(IntPtr PowerRequestHandle, PowerRequestType RequestType);
    
            [DllImport("kernel32.dll")]
            static extern bool PowerClearRequest(IntPtr PowerRequestHandle, PowerRequestType RequestType);
    
            [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
            internal static extern int CloseHandle(IntPtr hObject);
    
            // Availablity Request Enumerations and Constants
            enum PowerRequestType
            {
                PowerRequestDisplayRequired = 0,
                PowerRequestSystemRequired,
                PowerRequestAwayModeRequired,
                PowerRequestMaximum
            }
    
            const int POWER_REQUEST_CONTEXT_VERSION = 0;
            const int POWER_REQUEST_CONTEXT_SIMPLE_STRING = 0x1;
            const int POWER_REQUEST_CONTEXT_DETAILED_STRING = 0x2;
    
            // Availablity Request Structure
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            public struct POWER_REQUEST_CONTEXT
            {
                public UInt32 Version;
                public UInt32 Flags;
                [MarshalAs(UnmanagedType.LPWStr)]
                public string SimpleReasonString;
            }
    
            public void EnableConstantDisplayAndPower(bool enable, string reason)
            {
                if (enable)
                {
                    // Set up the diagnostic string
                    _PowerRequestContext.Version = POWER_REQUEST_CONTEXT_VERSION;
                    _PowerRequestContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
                    _PowerRequestContext.SimpleReasonString = reason; // your reason for changing the power settings
    
                    // Create the request, get a handle
                    _PowerRequest = PowerCreateRequest(ref _PowerRequestContext);
    
                    // Set the request
                    PowerSetRequest(_PowerRequest, PowerRequestType.PowerRequestSystemRequired);
                    PowerSetRequest(_PowerRequest, PowerRequestType.PowerRequestDisplayRequired);
                }
                else
                {
                    // Clear the request
                    PowerClearRequest(_PowerRequest, PowerRequestType.PowerRequestSystemRequired);
                    PowerClearRequest(_PowerRequest, PowerRequestType.PowerRequestDisplayRequired);
    
                    CloseHandle(_PowerRequest);
                }
            }
        }
    "@
    (New-Object PowerManager).EnableConstantDisplayAndPower($true, "Keeping the computer awake")
    Wait-Event
    

    I cleaned it up slightly to remove some unused things and to make it into a class, then added Wait-Event so that it will stay running.

    Now you just need to execute it remotely:

    Invoke-Command -AsJob -ComputerName remotecomputer -FilePath C:\Path\To\Set-KeepAwake.ps1
    

    You can prove that it's working by checking powercfg /requests:

    Invoke-Command -ComputerName remotecomputer -ScriptBlock { powercfg /requests }
    

    Which outputs:

    DISPLAY:
    None.
    
    SYSTEM:
    [PROCESS] \Device\HarddiskVolume1\Windows\System32\wsmprovhost.exe
    Keeping the computer awake
    
    AWAYMODE:
    None.