0

I'm trying to run code https://stackoverflow.com/users/251123/andy-arismendi posted in Get idle time of machine. It returns results as I would expect when run locally, but when I try to execute on a remote computer I get strange results: it's telling me idle time of over 13 hours -- but I just walked out of this user's office and know the idle time should be in the magnitude of seconds.

Here's the exact code I'm running - slightly tweaked from the original:

# via https://stackoverflow.com/questions/15845508/get-idle-time-of-machine

# c:\admin\pstools\psexec.exe \\COMPUTERNAME powershell.exe -command Enable-PSRemoting

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 int LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@


for ( $i = 0; $i -lt 10; $i++ ) {
    $Last = [PInvoke.Win32.UserInput]::LastInput
    $Idle = [PInvoke.Win32.UserInput]::IdleTime
    $LastStr = $Last.ToLocalTime().ToString("MM/dd/yyyy hh:mm tt")
    Write-Host ("`nTest " + $i)
    Write-Host ("   Last user keyboard/mouse input: " + $LastStr)
    Write-Host ("   Idle for " + $Idle.Days + " days, " + $Idle.Hours + " hours, " + $Idle.Minutes + " minutes, " + $Idle.Seconds + " seconds.")
    Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 10)
}

I'm running it remotely like this:

Invoke-Command -ComputerName COMPUTERNAME -FilePath \\path\to\scripts\TESTING-IdleTime.ps1

Both the machine I'm running the command on, and the remote computer, are Windows 10 Pro 1709.

Example output:

Test 0
Last user keyboard/mouse input: 01/10/2018 11:50 PM
Idle for 0 days, 13 hours, 16 minutes, 13 seconds.

I just tried it on a computer I know nobody is currently logged into, and it's reporting the same time -- the approximate time since the last reboot of the system.

Any idea what might be going on?

Brian
  • 161
  • 1
  • 9
  • I encapsulated your code into 2 functions and ran a local test with the following results: `PS C:\Users\Pythagoras> C:\Users\Pythagoras\Desktop\get-idletime.ps1 Test 0 Last user keyboard/mouse input: 01/12/2018 07:51 PM Idle for 0 days, 0 hours, 0 minutes, 0 seconds. Test 1 Last user keyboard/mouse input: 01/12/2018 07:51 PM Idle for 0 days, 0 hours, 0 minutes, 8 seconds.` Then stopped so it certainly works locally. Have you tried adding `[cmdletBinding()]` and making `$ComputerName` a parameter instead of using psexec? – trebleCode Jan 13 '18 at 00:53
  • I'm not using psexec -- I'm using Invoke-Command I will look into [cmdletBinding()]; I'm not familiar with that. – Brian Jan 16 '18 at 15:52
  • Brian, here are some relevant links to get you started: https://4sysops.com/archives/powershell-advanced-functions-the-cmdletbinding-and-parameter-attribute/ https://blogs.technet.microsoft.com/heyscriptingguy/2012/07/07/weekend-scripter-cmdletbinding-attribute-simplifies-powershell-functions/ https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-5.1 – trebleCode Jan 17 '18 at 16:45

1 Answers1

0

The script needs to be run in the user context.

You can achieve that for example by using the Task Scheduler and setting "When running the task, use the following account:" to the user account of which you need the idle time (or a group like "users", that also seems to do the job).

It is also important to set it to "Run only when user is logged on". Any other setting will have the same result you already observed, showing the computer uptime.

You could then adapt the script to log the idle time periodically to a file or report it somewhere else. With some more work you could even deploy and start the scheduled task remotely if needed.

japi
  • 101
  • 1
  • 3