Skip to content

Instantly share code, notes, and snippets.

@jamesfreeman959
Last active November 20, 2024 19:26
Show Gist options
  • Save jamesfreeman959/231b068c3d1ed6557675f21c0e346a9c to your computer and use it in GitHub Desktop.
Save jamesfreeman959/231b068c3d1ed6557675f21c0e346a9c to your computer and use it in GitHub Desktop.
A very simple PowerShell script to keep a Windows PC awake and make lync think the user is active on the keyboard
# Useful references:
#
# https://superuser.com/questions/992511/emulate-a-keyboard-button-via-the-command-line
# https://ss64.com/vb/sendkeys.html
# https://social.technet.microsoft.com/Forums/windowsserver/en-US/96b339e2-e9da-4802-a66d-be619aeb21ac/execute-function-one-time-in-every-10-mins-in-windows-powershell?forum=winserverpowershell
# https://learn-powershell.net/2013/02/08/powershell-and-events-object-events/
#
# Future enhancements - use events rather than an infinite loop
$wsh = New-Object -ComObject WScript.Shell
while (1) {
# Send Shift+F15 - this is the least intrusive key combination I can think of and is also used as default by:
# http://www.zhornsoftware.co.uk/caffeine/
# Unfortunately the above triggers a malware alert on Sophos so I needed to find a native solution - hence this script...
$wsh.SendKeys('+{F15}')
Start-Sleep -seconds 59
}
@mnotgninnep
Copy link

Thank you for this. Please put the line "$wsh = New-Object -ComObject WScript.Shell" before the while loop. You don't need to call it every time it loops.

@jamesfreeman959
Copy link
Author

Good catch!

Thank you for this. Please put the line "$wsh = New-Object -ComObject WScript.Shell" before the while loop. You don't need to call it every time it loops.

I've updated the code. I've been playing with moving the mouse instead of pressing a key as even the F15 keypress results in some odd artefacts in Linux terminal sessions. However having mixed results with this. If you want to experiment this with, try the following inside the loop:

$Pos = [System.Windows.Forms.Cursor]::Position
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point((($Pos.X) + 1), $Pos.Y)

Over time this would cause the cursor to creep towards the edge of the screen one pixel at a time, so you might want to move it, then, then move it back a pixel. However I am not sure this is keeping my system alive so please have a play if you're interested!

@OraDotNetDev
Copy link

OraDotNetDev commented Apr 12, 2022

This StackoverOverflow Question tried the same logic calling [Windows.Forms.Cursor], without success, they ended up using Python instead.

Good catch!

Thank you for this. Please put the line "$wsh = New-Object -ComObject WScript.Shell" before the while loop. You don't need to call it every time it loops.

I've updated the code. I've been playing with moving the mouse instead of pressing a key as even the F15 keypress results in some odd artefacts in Linux terminal sessions. However having mixed results with this. If you want to experiment this with, try the following inside the loop:

$Pos = [System.Windows.Forms.Cursor]::Position
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point((($Pos.X) + 1), $Pos.Y)

Over time this would cause the cursor to creep towards the edge of the screen one pixel at a time, so you might want to move it, then, then move it back a pixel. However I am not sure this is keeping my system alive so please have a play if you're interested!

@ice423cube
Copy link

ice423cube commented Aug 29, 2022

@jamesfreeman959 How do you actually run this in a bat file? Does it work in windows 10?

I put this in a keepawake.bat file

powershell.exe -windowstyle hidden -file C:\keepawake.ps1 -Until 17:30

It doesn't seem to work though.

or is there a way to run it in PowerShell itself 😃

@ice423cube
Copy link

ice423cube commented Aug 29, 2022

@jamesfreeman959 FYI. If anyone else comes a cross this and needs to know how to use it 😃. All you need to do is copy the above code to your computer and put in a "keepawake.ps1" file. If you open it in nope pad you can paste in the code. Then create a "keepawake.bat" file on your computer and open in notepad and add the following "PowerShell -file C:\keepawake.ps1". It will leave a command window open. If you ever want to stop it all you need to do is open the command window and hit the control key and the c key at the same time "ctrl+c". That kills the command. It is that simple 😃. Hope that helps.

@jamesfreeman959
Copy link
Author

Thanks for the update - I'm sure many will find that helpful! One little tip I picked up - I've had Windows machines where Group Policy prevents PowerShell scripts from being run. Yet if you open the ISE, copy the code in (do not save it - this triggers the policy - it has to run directly in the ISE) - it'll run quite happily. Naturally you have the PowerShell ISE open whilst it's running but I find that's a small price to pay.

@YouSpoonyBard
Copy link

@jamesfreeman959 You can also take the ps1 file, right click> run with PowerShell. It opens up a PowerShell window, and you can close the window when you're done with the program.

@tsuke35
Copy link

tsuke35 commented Sep 23, 2022

If you don't want to save anything and need to bypass policy checks, you can just paste the code as a one-liner and run it in a powershell window (so no need for ISE)

$wsh = New-Object -ComObject WScript.Shell; while (1) {$wsh.SendKeys('+{F15}'); Start-Sleep -seconds 59}

and next time you open the shell, just tap arrow up on the keyboard and you can continue where you left off ;)

@cleansteve
Copy link

cleansteve commented Jan 9, 2023

To add a simple time limit to run I changed the while condition to compare current time vs target time. I just wanted to leave this here in case someone finds it useful. I'm fairly new to Powershell but I assume it's defaulting to my system date format. Be sure to modify that if you use a different format as it's currently US "MM-dd-yyyy".

Microsoft's Get-Date Doc
Comparison Operators Doc

$wsh = New-Object -ComObject WScript.Shell
   $a = Get-Date # Current time
   $b = Get-Date "01-09-2023  10:40:00" # Target time
   while ($a -lt $b) {
     $wsh.SendKeys('+{F15}')
     Start-Sleep -seconds 59
     $a = Get-Date
  }

Hope it helps someone!

@tsuke35
Copy link

tsuke35 commented Feb 2, 2023

To add a simple time limit to run I changed the while condition to compare current time vs target time. I just wanted to leave this here in case someone finds it useful. I'm fairly new to Powershell but I assume it's defaulting to my system date format. Be sure to modify that if you use a different format as it's currently US "MM-dd-yyyy".

Microsoft's Get-Date Doc Comparison Operators Doc

$wsh = New-Object -ComObject WScript.Shell
   $a = Get-Date # Current time
   $b = Get-Date "01-09-2023  10:40:00" # Target time
   while ($a -lt $b) {
     $wsh.SendKeys('+{F15}')
     Start-Sleep -seconds 59
     $a = Get-Date
  }

Hope it helps someone!

Good addition with the timer! You could make it little more easily managable by replacing the $b datetime with a bit more flexible solution by using .AddMinutes() method.. This way you only need to change the amount of minutes for each using occasion, instead of the whole target datetime.

$wsh = New-Object -ComObject WScript.Shell
$a = Get-Date # Current time
$b = (Get-Date).AddMinutes(120) # Target time

while ($a -lt $b) {
    $wsh.SendKeys('+{F15}')
    Start-Sleep -seconds 59
    $a = Get-Date
}

@0scvr
Copy link

0scvr commented Apr 13, 2023

I'll leave my modified version here in case it may be useful to someone else:

param([int]$minutes=60)  # input param, 60 minutes if no value provided

$wsh = New-Object -ComObject WScript.Shell
$a = Get-Date # Current time
$b = $a.AddMinutes($minutes) # Target time

while ($a -lt $b) {
# Send Shift+F15    
$wsh.SendKeys('+{F15}')
    Start-Sleep -seconds 240  # sleep for 4 minutes
    $a = Get-Date
}

Use it with: powershell.exe -file keepawake.ps1 -minutes 20

@ldhelms
Copy link

ldhelms commented Jun 22, 2023

I do the following inside the while loop... This RANDOMLY sleeps between 30 and 200 seconds (3.33 mins). If you have some sort of AI type activity tracker installed you should change it to shorter intervals to look human-ish (something like 1 - 5 seconds)

   $WShell.sendkeys("{SCROLLLOCK}")
   Start-Sleep -Milliseconds 100
   $WShell.sendkeys("{SCROLLLOCK}")

   $RNDSleep = Get-Random -Minimum 30 -Maximum 200
   $MINS = RNDSleep / 60
   Write-Host $NOW $RNDSleep "("$MINS" mins )"

   Start-Sleep -Seconds $RNDSleep

@TheBeerGenius
Copy link

Hello there; I am not sure if it was the most recent Windows 11 update that was forced, but I have been exposed to this [new to me] "Constrained Language Mode" within PS. Here is the Keepalive script I normally run:

image

The above script (and any script that tries to leverage the "New-Object -ComObject WScript.Shell" command (or series of commands)) is met with the following error dialog (I will redact certain elements with an asterisk (*).

--- Begin Error Output ---

New-Object : Cannot create type. Only core types are supported in this language mode.
At C:\Users*REDACTED*\OneDrive - PATHREDACTED\Documents\PowerShell\Alive.ps1:7 char:11

  • $WShell = New-Object -com "Wscript.Shell"
  •       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : PermissionDenied: (:) [New-Object], PSNotSupportedException
    • FullyQualifiedErrorId : CannotCreateComTypeConstrainedLanguage,Microsoft.PowerShell.Commands.NewObjectCommand

You cannot call a method on a null-valued expression.
At C:\Users*REDACTED*\OneDrive - PATHREDACTED\Documents\PowerShell\Alive.ps1:15 char:3

  • $WShell.sendkeys("{SCROLLLOCK}")
  • + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
    
    

You cannot call a method on a null-valued expression.
At C:\Users*REDACTED*\OneDrive - PATHREDACTED\Documents\PowerShell\Alive.ps1:19 char:3

  • $WShell.sendkeys("{SCROLLLOCK}")
  • + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull 
    
    

--- End Error Output ---

I can see that there's the whole permission denied bit, but that's interesting [at least to me] because previous permissions issues were cleared up by running the following:

Set-ExecutionPolicy -Scope CurrentUser Unrestricted

So, that Execution Policy is still in effect (verified by running Get-ExecutionPolicy; still set to Unrestricted). Which brings me to the root of my question; was this Constrained Language Mode automatically pushed by Microsoft for Windows 11, i.e., is the Language Mode being unconstrained a vulnerability [that has now been modified as to not be exploited]?

Background -- this keep alive PS Script was useful for kiosks in customer lobbies or in some cases where a presentation deck is run in a cyclical manner all day (in a common area or in an auditorium). It is also useful for operational type situations, such as air operations, where a particular status board needs to remain up and visible without a screen saver turning on or a display going to sleep.

I'd appreciate any guidance or even workarounds on how to make keep alive type PS scripts function in this new [to me] Constrained Language paradigm.

Thank you in advance,
The Beer Genius

@ps29
Copy link

ps29 commented Sep 27, 2023

This works great without admin privilege in work computer on windows 10. Thanks :)

@TheBeerGenius
Copy link

This works great without admin privilege in work computer on windows 10. Thanks :)

Indeed... and it did work on Windows 11 without admin privilege until about a week ago [when an update of some sort was pushed that changed/added the Constrained Language mode].

((Commenting to bump in order to solicit possible replies))

@Skhmt
Copy link

Skhmt commented Apr 1, 2024

This works great without admin privilege in work computer on windows 10. Thanks :)

Indeed... and it did work on Windows 11 without admin privilege until about a week ago [when an update of some sort was pushed that changed/added the Constrained Language mode].

((Commenting to bump in order to solicit possible replies))

Try this (it must be in a .bat file):

@if (@a==@a) @end /*

@echo off

set SendKeys=CScript //nologo //E:JScript "%~F0"

set starttime=%TIME%

:loop

cls
set /a rng = %RANDOM% * 60 / 32768 + 30
echo Sending [Shift]+[F15]
echo Start:  %starttime%
echo Latest: %TIME%
%SendKeys% "+{F15}"
timeout /t %rng% /nobreak

goto loop

*/

var WshShell = WScript.CreateObject("WScript.Shell");
WshShell.SendKeys(WScript.Arguments(0));

@stringTrimmer
Copy link

I realize the original use case was to keep the Windows computer awake AND for another app (lync) to see keyboard activity. However for anyone who just needs the 1st part (to prevent Windows from sleeping and optionally keep the screen on) another solution was posted by @dend on his blog. You can also use the Awake utility from PowerToys which was created by the same author.

My use case: to keep the Windows machine hosting OpenSSH server awake when there is an active ssh connection using a pwsh session (seems like a bug in Windows OpenSSH tbh). I didn't need the screen to stay on and also didn't seem to require using a background thread as Den originally did (maybe something changed in .NET or PowerShell versions since his post, idk). So I just put this in my pwsh $PROFILE:

if (Test-Path env:SSH_CLIENT) { # only do this for a remote ssh user's session, but if that's not your thing remove this conditional
  $Signature=@"
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern void SetThreadExecutionState(uint esFlags);
  "@

    $ES_SYSTEM_REQUIRED = [uint32]"0x00000001"
    $ES_CONTINUOUS = [uint32]"0x80000000"
    $stes = Add-Type -MemberDefinition $Signature -Name System -Namespace Win32 -PassThru
    $stes::SetThreadExecutionState($ES_CONTINUOUS -bor $ES_SYSTEM_REQUIRED)
    Write-Host "Keeping computer awake"
}

If you need the screen to stay on change the script to use the appropriate flags with SetThreadExecutionState.

Thank you @dend

@dend
Copy link

dend commented Apr 18, 2024

@stringTrimmer thank you for the shout-out! 🥳

@jheinrichs79
Copy link

I added some extra features and built an EXE using some of your code.
Random keep alive time
Dynamic time to run (application will ask you if you didn't enter it at run time)
Add a Pause at completion (optional)
Included a compiled version

https://github.com/jheinrichs79/Public/blob/main/Powershell/Utilities/Start-KeepAwake/
https://github.com/jheinrichs79/Public/blob/main/Powershell/Utilities/Start-KeepAwake/Start-KeepAwake.ps1
https://github.com/jheinrichs79/Public/blob/main/Powershell/Utilities/Start-KeepAwake/Start-KeepAwake.exe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment