2

Recently, I have been trying to find a method of completely automating my backup system, which uses a 3rd-party program called "AOMEI Backupper Standard". The automated sequence I am trying to get the computer to perform consists of:

  1. Powering on the PC slightly before noon each day and initialising a set of two incremental backups at noon, which will run sequentially until completion. The total time that these two backups take to execute is currently around 2 hours 55 mins (I have a lot of data to read, even with a striped volume), though this time will likely increase as I accumulate more data in the future.
  2. At completion, the PC needs to verify whether someone has been using the computer for any significant period of time (say 5 mins or more to ignore quick usages or random disturbances), during a time period (say 1 hour) before the 2nd backup completed. Alternatively, instead of verifying if there has been use for more than 5 mins, the PC could simply detect any use longer than 1 minute (to eliminate random errors with the keyboard or mouse being nudged or moving slightly) and then there could be another script for resetting the usage flag if the user wants the PC to ignore their previous usage during the 1 hour time period.
  3. If there has not been significant user activity in that time period, then the PC must automatically hibernate at the end of the 2nd backup (this completes the automated process for if I am not at home/using the PC). Otherwise, the PC must abort the hibernation if it detects this user activity, so that it doesn't suddenly power off whilst I am using it, or if I have been using it recently.

I can handle the setting of the start of the timing period using a batch file by taking the previous time that the two backups took to complete, adding that on to midday, and then subtracting 1 hour. This will allow the start of the timing period to become later relative to noon as the total backup time increases with more data being stored.

Therefore, I am looking for a way to detect user activity on the PC during the time period by either checking mouse and/or keyboard input, or by some other means. This detection method would also need to take into account if the "ES_DISPLAY_REQUIRED" flag had been set, or similar, so that the PC would not be considered idle if it was being used to watch a movie where there is no user input for a long time.

One idea I had for detection other than directly checking M&K, was to somehow utilise the Windows idle flag/state, but modify it to ignore resource consumption as a factor in determining idle state. This is because during the backup, the CPU and IO percentage idle time will easily be less than the 80% threshold set by Microsoft (see this page from Microsoft for more details) for Windows 8 (although I assume this also applies for Windows 10 systems, which is what I am currently using as my OS), and so the system would never become "idle" even if there were no mouse or keyboard input/user presence during this period.

Ideally, I would like to be able to do this detection using a batch file/Windows command line (cmd), but I can appreciate that this may not be possible for this type of task. Therefore, if you have any suggestions for other programming languages which would integrate/work well with Windows and would be able to accomplish this task, I would be happy to hear them (I have some basic experience with HTML/CSS and JavaScript, as well as a moderate level of experience with Visual BASIC and Command Line/Batch Files). Sorry if this was a little too verbose, but I wanted to prevent any confusion about my problem.

Thanks in advance for your time.

EDIT #1: I have generalised the title to move away from my original choice of using batch files only, and I have added a tag for C# as I understand that it is similar to VB and can be useful for tasks in Windows.

EDIT #2: After doing some more research, I have come to the realisation that PowerShell may be another much more powerful option than batch files, as it has much more advanced capabilities than cmd. For now, I will try to do some research into learning PowerShell and looking over related questions such as this one, as it seems like what I'm trying to achieve could be done without a lot of complicated code using this language.

EDIT #3: It seems that my generalisation over multiple languages due to me not knowing which would solve my problem wasn't appreciated, and so I am now re-focusing the question onto only PowerShell. This is because the problem is apparently "not C# related" and I am almost certain now that it cannot be accomplished using cmd, as there simply isn't a command for detecting mouse or keyboard input.

Community
  • 1
  • 1
ScottyN91
  • 51
  • 1
  • 6
  • 1
    Try using Windows Task Scheduler to add a batch file that runs `On idle` (trigger page) and `Wait for idle` of say 5 minutes (conditions page). You'll have to figure out how to utilize that yourself, I didn't try it. – wOxxOm Sep 04 '16 at 08:53
  • Hi wOxxOm, I have just tried your suggestion, and as I thought might happen, the task does not trigger when the PC is under load. For example, when I ran a small test backup, the CPU load was around 20%-30% throughout and the task did not trigger. However, if I did not run the backup and simply left the computer alone for a few minutes, then the task did trigger, because I had set it to trigger after 1 minute of idle time and wait for this idle time to occur for a maximum of 10 minutes. Do you have any suggestions about how I could change this behaviour so that resource consumption is ignored? – ScottyN91 Sep 04 '16 at 12:34
  • @CodyG Would it be possible for you or anyone (else reading this) to explain why the post is too broad or how it could be improved? I have tried to focus more on PowerShell now, but I'm not sure if this will solve the problem that caused the post to be put on hold? – ScottyN91 Sep 05 '16 at 17:58

1 Answers1

3

From this answer, you can do this in PowerShell pretty simply. First you add a type to access the Win32_UserInput class then later you can read idea time.

Simply trigger your action when the idle time meets your requirements and you're done.

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; } } } } '@

And an example usage:

for ( $i = 0; $i -lt 10; $i++ ) { Write-Host ("Last input " + [PInvoke.Win32.UserInput]::LastInput) Write-Host ("Idle for " + [PInvoke.Win32.UserInput]::IdleTime) Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 5) }

P.S. The above code in human-readable form:

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;
                }
            }
        }
    }
'@

ForEach ($i in 0..9) {
    Write-Host ("Last input " + [PInvoke.Win32.UserInput]::LastInput)
    Write-Host ("Idle for " + [PInvoke.Win32.UserInput]::IdleTime)
    Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 5)
}
Community
  • 1
  • 1
FoxDeploy
  • 12,569
  • 2
  • 33
  • 48
  • Hi FoxDeploy, I shall have to spend some time looking over the code you provided, as this is my first time using PowerShell, so I'd like to learn about how this code works. However, in the meantime, when running/inspecting PowerShell code, is it best to use the normal Windows PowerShell application, or should I use the "ISE" version instead? – ScottyN91 Sep 04 '16 at 18:32
  • Never mind, I ended up finding the answer [here](https://deployhappiness.com/what-version-of-powershell-should-i-use/) anyway. – ScottyN91 Sep 04 '16 at 21:32