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

Well, I want to move the mouse cursor every 4 minutes to prevent the screensaver from appearing (every second in the code above for testing). The code does really move the mouse every time one pixel up and then down immediately. The thing is, the screensaver (or idle mode of windows) is still appearing.

Where is my mistake?

starball
  • 20,030
  • 7
  • 43
  • 238
nivoe
  • 405
  • 1
  • 4
  • 11
  • Does a one pixel shift count as enough activity to stop the screen saver? – Mike G Apr 05 '13 at 14:04
  • Hmm, changed it to 100 pixels and it keeps going idle – nivoe Apr 05 '13 at 14:09
  • Tried to not go back after the move, leaving the cursor to move up every 10 seconds. Still I appear idle? – nivoe Apr 05 '13 at 14:18
  • 3
    There's a discussion (with no clear answer but some good information) here: http://stackoverflow.com/questions/463813/programmatically-prevent-windows-screensaver-from-starting - Basically saying that moving the mouse is not enough. Windows is looking for user input, not just mouse movement. `SendInput` looks promising, though I don't know how to call it from PowerShell. – Nate Hekman Apr 05 '13 at 14:21
  • Aha, ok, I see. Well, I guess I'll have to look deeper into that :) Thank you both – nivoe Apr 05 '13 at 14:29
  • Hey Nate. You're right, moving the mouse is not enough. It must be solved with a SendInput, such as http://dmitrysotnikov.wordpress.com/2009/06/29/prevent-desktop-lock-or-screensaver-with-powershell/ – nivoe Apr 05 '13 at 15:27
  • Awesome. For future users with the same question, you should write that as an answer and Accept it. – Nate Hekman Apr 05 '13 at 15:30
  • http://stackoverflow.com/a/36095443/388389 – npocmaka Mar 31 '17 at 07:41

10 Answers10

48

The solution from the blog Prevent desktop lock or screensaver with PowerShell is working for me. Here is the relevant script, which simply sends a single period to the shell:

param($minutes = 60)

$myshell = New-Object -com "Wscript.Shell"

for ($i = 0; $i -lt $minutes; $i++) {
  Start-Sleep -Seconds 60
  $myshell.sendkeys(".")
}
JPBlanc
  • 70,406
  • 17
  • 130
  • 175
38

I tried a mouse move solution too, and it likewise didn't work. This was my solution, to quickly toggle Scroll Lock every 4 minutes:

Update: Improved version.

#This script exists because the auto-lock/disconnect time period is way too short.

#*** Make sure to lock before you go AFK! ***

#==============================================================================

#https://stackoverflow.com/a/56636565/1599699
#https://gist.github.com/MatthewSteeples/ce7114b4d3488fc49b6a?permalink_comment_id=4590234#gistcomment-4590234
#https://ss64.com/vb/sendkeys.html
#https://devguru.com/content/technologies/wsh/wshshell-sendkeys.html

#==============================================================================

$host.UI.RawUI.WindowTitle = "OS Keep-Alive"
[Console]::SetWindowSize(50, 10)

$format = "dddd MM/dd/yy hh:mm:ss tt"
$start = $(Get-Date -Format $format)
$previous = "N/A"

$WShell = New-Object -com "Wscript.Shell"
while ($true)
{
  Clear-Host
  Echo "Keep-alive with Scroll Lock toggle..."
  Write-Host
  Write-Host "     Start:" $start
  Write-Host "  Previous:" $previous
  $previous = $(Get-Date -Format $format)
  Write-Host "    Latest:" $previous
  Write-Host
  Echo "*** Make sure to lock before you go AFK! ***"

  #==============================================================================
  
  #If you're getting a "null-valued" expression error, try "SCROLLLOCK" instead.
  $WShell.sendkeys("{SCROLLLOCK}")
  Start-Sleep -Milliseconds 100 #100 milliseconds == 1/10th seconds

  $WShell.sendkeys("{SCROLLLOCK}")
  Start-Sleep -Seconds 240 #240 seconds == 4 minutes * 60 seconds
}

I used Scroll Lock because that's one of the most useless keys on the keyboard. Also could be nice to see it briefly blink every now and then. This solution should work for just about everyone, I think.

Some people get success using $WShell.sendkeys("SCROLLLOCK") instead of $WShell.sendkeys("{SCROLLLOCK}")

See also:

Andrew
  • 5,839
  • 1
  • 51
  • 72
8

There is an analog solution to this also. There's an android app called "Timeout Blocker" that vibrates at a set interval and you put your mouse on it. https://play.google.com/store/apps/details?id=com.isomerprogramming.application.timeoutblocker&hl=en

Matt
  • 81
  • 1
  • 1
4
<# Stay Awake by Frank Poth 2019-04-16 #>

(Get-Host).UI.RawUI.WindowTitle = "Stay Awake"

[System.Console]::BufferWidth  = [System.Console]::WindowWidth  = 40
[System.Console]::BufferHeight = [System.Console]::WindowHeight = 10

$shell = New-Object -ComObject WScript.Shell

$start_time = Get-Date -UFormat %s <# Get the date in MS #>
$current_time = $start_time
$elapsed_time = 0

Write-Host "I am awake!"

Start-Sleep -Seconds 5

$count = 0

while($true) {

  $shell.sendkeys("{NUMLOCK}{NUMLOCK}") <# Fake some input! #>

  if ($count -eq 8) {

    $count = 0
    Clear-Host

  }

  if ($count -eq 0) {

    $current_time = Get-Date -UFormat %s
    $elapsed_time = $current_time - $start_time

    Write-Host "I've been awake for "([System.Math]::Round(($elapsed_time / 60), 2))" minutes!"

  } else { Write-Host "Must stay awake..." }

  $count ++

  Start-Sleep -Seconds 2.5

}

The part that matters is $shell.sendkeys("{NUMLOCK}{NUMLOCK}") This registers two presses on the numlock key and fools the shell into thinking input was entered. I wrote this today after searching through various scripts that didn't work for me. Hope it helps someone!

Frank
  • 2,050
  • 6
  • 22
  • 40
3

I created a PS script to check idle time and jiggle the mouse to prevent the screensaver.

There are two parameters you can control how it works.

$checkIntervalInSeconds : the interval in seconds to check if the idle time exceeds the limit

$preventIdleLimitInSeconds : the idle time limit in seconds. If the idle time exceeds the idle time limit, jiggle the mouse to prevent the screensaver

Here we go. Save the script in preventIdle.ps1. For preventing the 4-min screensaver, I set $checkIntervalInSeconds = 30 and $preventIdleLimitInSeconds = 180.

Add-Type @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

    public static class UserInput {

        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO {
            public uint cbSize;
            public int dwTime;
        }

        public static DateTime LastInput {
            get {
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }

        public static TimeSpan IdleTime {
            get {
                return DateTime.UtcNow.Subtract(LastInput);
            }
        }

        public static double IdleSeconds {
            get {
                return IdleTime.TotalSeconds;
            }
        }

        public static int LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@

Add-Type @'
using System;
using System.Runtime.InteropServices;

namespace MouseMover
{
    public class MouseSimulator
    {
        [DllImport("user32.dll", SetLastError = true)]
        static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetCursorPos(out POINT lpPoint);

        [StructLayout(LayoutKind.Sequential)]
        struct INPUT
        {
            public SendInputEventType type;
            public MouseKeybdhardwareInputUnion mkhi;
        }
        [StructLayout(LayoutKind.Explicit)]
        struct MouseKeybdhardwareInputUnion
        {
            [FieldOffset(0)]
            public MouseInputData mi;

            [FieldOffset(0)]
            public KEYBDINPUT ki;

            [FieldOffset(0)]
            public HARDWAREINPUT hi;
        }
        [StructLayout(LayoutKind.Sequential)]
        struct KEYBDINPUT
        {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }
        [StructLayout(LayoutKind.Sequential)]
        struct HARDWAREINPUT
        {
            public int uMsg;
            public short wParamL;
            public short wParamH;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            public POINT(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }
        }
        struct MouseInputData
        {
            public int dx;
            public int dy;
            public uint mouseData;
            public MouseEventFlags dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [Flags]
        enum MouseEventFlags : uint
        {
            MOUSEEVENTF_MOVE = 0x0001
        }
        enum SendInputEventType : int
        {
            InputMouse
        }
        public static void MoveMouseBy(int x, int y) {
            INPUT mouseInput = new INPUT();
            mouseInput.type = SendInputEventType.InputMouse;
            mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_MOVE;
            mouseInput.mkhi.mi.dx = x;
            mouseInput.mkhi.mi.dy = y;
            SendInput(1, ref mouseInput, Marshal.SizeOf(mouseInput));
        }
    }
}
'@

$checkIntervalInSeconds = 30
$preventIdleLimitInSeconds = 180

while($True) {
    if (([PInvoke.Win32.UserInput]::IdleSeconds -ge $preventIdleLimitInSeconds)) {
        [MouseMover.MouseSimulator]::MoveMouseBy(10,0)
        [MouseMover.MouseSimulator]::MoveMouseBy(-10,0)
    }
    Start-Sleep -Seconds $checkIntervalInSeconds
}

Then, open Windows PowerShell and run

powershell -ExecutionPolicy ByPass -File C:\SCRIPT-DIRECTORY-PATH\preventIdle.ps1
Timespace
  • 5,101
  • 7
  • 23
  • 32
2

I had a similar situation where a download needed to stay active overnight and required a key press that refreshed my connection. I also found that the mouse move does not work. However, using notepad and a send key function appears to have done the trick. I send a space instead of a "." because if there is a [yes/no] popup, it will automatically click the default response using the spacebar. Here is the code used.

param($minutes = 120)

$myShell = New-Object -com "Wscript.Shell"

for ($i = 0; $i -lt $minutes; $i++) {
  Start-Sleep -Seconds 30
  $myShell.sendkeys(" ")
}

This function will work for the designated 120 minutes (2 Hours), but can be modified for the timing desired by increasing or decreasing the seconds of the input, or increasing or decreasing the assigned value of the minutes parameter.

Just run the script in powershell ISE, or powershell, and open notepad. A space will be input at the specified interval for the desired length of time ($minutes).

Good Luck!

Bucket
  • 7,415
  • 9
  • 35
  • 45
2

Try this: (source: http://just-another-blog.net/programming/powershell-and-the-net-framework/)

Add-Type -AssemblyName System.Windows.Forms 

$position = [System.Windows.Forms.Cursor]::Position  
$position.X++  
[System.Windows.Forms.Cursor]::Position = $position 

    while(1) {  
    $position = [System.Windows.Forms.Cursor]::Position  
    $position.X++  
    [System.Windows.Forms.Cursor]::Position = $position  

    $time = Get-Date;  
    $shorterTimeString = $time.ToString("HH:mm:ss");  

    Write-Host $shorterTimeString "Mouse pointer has been moved 1 pixel to the right"  
    #Set your duration between each mouse move
    Start-Sleep -Seconds 150  
    }  
Jeremy F.
  • 1,778
  • 11
  • 51
  • 86
  • While this does move the mouse pointer, it doesn't prevent the Windows idle timeout unfortunately. – KERR Sep 06 '18 at 10:22
1

Below PowerShell script toggles scroll lock every minute, prints out current time, and clears console every 5 minutes (in case you want to keep the script going indefinitely).

$WShell = New-Object -com "Wscript.Shell"
cls

$count = 0
while ($true)
{
  $count = $count + 1
  if($count -eq 5) {
    cls
    $count=0
  }
  $WShell.sendkeys("{SCROLLLOCK}")
  Start-Sleep -Milliseconds 100
  Write-Output "Toggle Scroll at $(Get-Date -Format u)"
  $WShell.sendkeys("{SCROLLLOCK}")
  Start-Sleep -Seconds 60
}
Ancalagon
  • 11
  • 4
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 24 '22 at 08:29
1

I simply run alt+tab after every random second between 5 to 10 sec Because new tools even track pattern of any key press.

Add it inside a loop and you are done.

[System.Windows.Forms.SendKeys]::SendWait("%{TAB}")

$ran=(Get-Random -Minimum 5 -Maximum 10)
echo "sleep for $ran sec"
sleep $ran 
0

I've added a notification that you can easily enable / disable just setting its variable to $true or $false. Also the mouse cursor moves 1 px right and then 1 px left so it basically stays in the same place even after several iterations.

# Lines needed for the notification
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
Add-Type -AssemblyName System.Windows.Forms 
$isNotificationOn = $true

$secondsBetweenMouseMoves = 6
$Pos = [System.Windows.Forms.Cursor]::Position
$PosDelta = 1
$logFilename = "previousMouseMoverAction.txt"
$errorLogFilename = "mouseMoverLog.txt"

if (!(Test-Path "$PSScriptRoot\$logFilename")) {
   New-Item -path $PSScriptRoot -name $logFilename -type "file" -value "right"
   Write-Host "Warning: previousMouseMoverAction.txt missing, created a new one."
}

$previousPositionChangeAction = Get-Content -Path $PSScriptRoot\$logFilename

if ($previousPositionChangeAction -eq "left") {
    $PosDelta = 1
    Set-Content -Path $PSScriptRoot\$logFilename -Value 'right'
} else {
    $PosDelta = -1
    Set-Content -Path $PSScriptRoot\$logFilename -Value 'left'
}

for ($i = 0; $i -lt $secondsBetweenMouseMoves; $i++) {
    [System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point((($Pos.X) + $PosDelta) , $Pos.Y)
    if ($isNotificationOn) {
        # Sending a notification to the user
        $global:balloon = New-Object System.Windows.Forms.NotifyIcon
        $path = (Get-Process -id $pid).Path
        $balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) 
        $balloon.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]::Warning 
        $balloon.BalloonTipText = 'I have just moved your cheese...'
        $balloon.BalloonTipTitle = "Attention, $Env:USERNAME" 
        $balloon.Visible = $true 
        $balloon.ShowBalloonTip(3000)
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Pitto
  • 8,229
  • 3
  • 42
  • 51
  • I sinthetized keyboard and mouse management in this powershell script. https://gist.github.com/neo7BF/deda3afb61bcc033ce0893fbc3335b55 I noticed that only keyboard prevent lock. – Forcuti Alessandro Feb 25 '21 at 15:49