Monday, August 13, 2018

Windows 10 and Idle Timeout

I had built a simple session management software with a C# login screen that saves the data to a google sheet, and I used the Windows task manager for controlling the launching of the session management login screen and the idle timeout. In windows 7 the idle timeout was more/less 15 minutes. For in-depth information about the windows idle state

https://docs.microsoft.com/en-us/windows/desktop/taskschd/task-idle-conditions


As defined in the Task Idle Condition

"In Windows 7, the Task Scheduler verifies that the computer is in an idle state every 15 minutes. Task Scheduler checks for an idle state using two criteria: user absence, and a lack of resource consumption. The user is considered absent if there is no keyboard or mouse input during this period of time. The computer is considered idle if all the processors and all the disks were idle for more than 90% of the last detection interval. (An exception would be for any presentation type application that sets the ES_DISPLAY_REQUIRED flag. This flag forces Task Schedule to not consider the system as being idle, regardless of user activity or resource consumption.)

In Windows 7, Task Scheduler considers a processor as idle even when low priority threads (thread priority < normal) execute.

In Windows 7, when the Task Scheduler detects that the computer is idle, the service waits only for user input to mark the end of the idle state."


The changes in the Task Scheduler in Windows 8/10

In Windows 8, Task Scheduler performs the same general user absence and resource consumption checks. However, Task Scheduler relies on the operating system power subsystem to detect user presence. By default, the user is considered absent after four minutes of no keyboard or mouse input. The resource consumption verification time is shortened to 10 minute intervals when the user is present. When the user is away, the verification time is shortened to 30 second intervals. Task Scheduler makes additional resource consumption checks for the following events:

  • User presence state changed
  • AC/DC power source changed
  • Battery level changed (only when on batteries)

When any of the events above happens, Task Scheduler tests the computer for idleness since the last verification time. In practice, this means that Task Scheduler may declare the system as idle immediately after user absence is detected, if the other conditions have been met since the last verification time.

This is a big problem for the session management system we had. It went from having a 15 minute idle check to 30 seconds to 5 minutes (varies as per above technical info)

So I wrote a program in C# that had checked the last time a user had used the system. This thinking was also flawed because like the Windows 8/10 idle check it was based on the last user input. What I wanted was a full 15 minutes after the idle program was launched. So I used this bit of code.

(DateTime.UtcNow - Process.GetCurrentProcess().StartTime.ToUniversalTime()).TotalMilliseconds

This is different because I had the program doing the idle reboot based on the following:

IdleTime = System.Environment.TickCount - LastInput.dwTime;

In my testing I would have the task scheduler launch the app but because I was checking for LastInput.dwTime I would have used up between 200000 and 400000 milliseconds, even though the app was launched on idle from the task scheduler. This makes total scene since I am reading the system event.  However I still want to use this code because I am using it to exit the application if the idle timeout launches the application for the countdown.  When the user comes back and moves the mouse or presses the keyboard it closes (exits) the application.

The major part of the code is as follows


using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Runtime.InteropServices;

using System.Windows;

using System.Diagnostics;

using System.Windows.Forms;


namespace EndSession

{


public partial class EndSession : Form

{

//initialize variables
//Popup Counter. This ensures we only see the message windows that popups in the last 2 minutes once.


int popupCounter = 0;



[DllImport("user32.dll")]

public static extern Boolean GetLastInputInfo(ref tagLASTINPUTINFO plii);




public struct tagLASTINPUTINFO

{

public uint cbSize;

public Int32 dwTime;

}


public EndSession()

{

InitializeComponent();

//Hide the windows for the program so no one can see it running.  Turn off for debugging

this.WindowState = FormWindowState.Minimized;

this.ShowInTaskbar = false;


}



private void timer1_Tick(object sender, EventArgs e)

{

tagLASTINPUTINFO LastInput = new tagLASTINPUTINFO();

Int32 IdleTime;

LastInput.cbSize = (uint)Marshal.SizeOf(LastInput);

LastInput.dwTime = 0;

if (GetLastInputInfo(ref LastInput))

{

IdleTime = System.Environment.TickCount - LastInput.dwTime;


//DEBUGGING Text Field

// label1.Text = IdleTime + " ms " + rowCounter + "run time:" + "last input" + LastInput.dwTime + "App start" + ((DateTime.UtcNow - Process.GetCurrentProcess().StartTime.ToUniversalTime()).TotalMilliseconds);

// label1.Text = idleStart +" "+ idleEnd;


}


IdleTime = System.Environment.TickCount - LastInput.dwTime;



//Check the idle time. If less then 100ms close the application.  Based on the last time there was user input
//The Reason for 100ms is if it is set to 0, the program doesn't always catch the user input. If it is set to high the program closes right away.


if (IdleTime <= 100)

{

//If the user had moved the mouse or hit the keyboard close the program because IdleTime is less the 100 milliseconds.
Application.Exit();

}


//This is our time check. If we were to use the system idle time the program would close at the time the

//user stopped the keyboard/mouse input We are starting the idletime based on when our app lauches

if ((DateTime.UtcNow - Process.GetCurrentProcess().StartTime.ToUniversalTime()).TotalMilliseconds > 780000)


{

if (popupCounter == 0)

{

//increment our popup counter and show our message box. Force it to main focus over all other windows

//MessageBoxOptions 0x40000

popupCounter++;

DialogResult AutoResult = MessageBox.Show("This System will reboot in 2 minutes if it is left idle", "End Session Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning,

MessageBoxDefaultButton.Button1, (MessageBoxOptions)0x40000);

}


}

//If it is 900000ms or 15 minutes and greater since the launch or the app, force close all programs and restart.

if ((DateTime.UtcNow - Process.GetCurrentProcess().StartTime.ToUniversalTime()).TotalMilliseconds > 900000)

{

// Force close and reboot the system it is over 15 minutes or 900000 milliseconds

System.Diagnostics.Process.Start("shutdown.exe", "-r -f -t 0");

}

}


}

}


You can get the code from my GitHub Repository

How to fix CURL call imporitng an RSS feed on a site blocking CURL calls

There is a 3rd party service provider that my organization uses called bibliocommons.  They have these nice book carousels.  However the car...