Many times it is necessary to run background tasks when the system is idle.
For example, perform background encryption / decryption using manage-bde tool on volumes containing data which could not be completed during installation.
In such cases the task scheduler can be used to schedule a task under the system account, upon OnIdle condition (when the system becomes idle with no user inter action or no background process running), after a delay say 1 minute. This task may launch a batch script start_idle_task.cmd. As this runs under the system account, it'll run even none is logged in.
When the system goes out of idle say due to an user action, this task is immediately terminated if it's still running.
In the some cases, when the on idle task is terminated, it's desirable to take an action such as run another task. As windows OS provides no direct way to achieve this, it can be implemented by tracking the process termination of start_idle_task.cmd using WMI class Win32_ProcessStopTrace in a different process and launch the new task.
Example:
Create a batch file start_idle_task.cmd as below. This batch file will be started when system becomes idle and trigger ProcessWatcher.exe when it's terminated by user action.
rem processwatcher will trigger when idle_task.cmd is terminated due to user action
start D:\Github\TechBlog\IdleTask\Scripts\ProcessWatcher.exe echo started %date% %time% >> c:\temp\idletask.log
echo started %date% %time% >> c:\temp\idletask.logrem run any command to start when system is idle
rem start manage-bde -resume d:
rem wait for the task scheduler to kill
pause
Create a task scheduler task that will be launched when system becomes idle. This task will run under system account and run start_idle_task.cmd.
schtasks /create /f /sc onidle /i 1 /tn idletaskstart /tr "D:\Github\TechBlog\IdleTask\Scripts\start_idle_task.cmd" /ru system
ProcessWatcher.exe
Create a winform project. Remove Form1 and add following lines in Program.cs. This will wait for termination of start_idle_task.cmd by the system. Then execute customaction() and terminate.
static class Program
{
static ManagementEventWatcher processStopEvent;
static void customaction()
{
System.IO.File.AppendAllText(@"c:\temp\processwatcher.txt", "ended " + System.DateTime.Now.ToString() + "\n");
}
static void processStopEvent_EventArrived(object sender, EventArrivedEventArgs e)
{
try
{
customaction();
processStopEvent.Stop();
processStopEvent.EventArrived -= processStopEvent_EventArrived;
Process.GetCurrentProcess().Kill();
}
catch
{
}
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
int parentpid = 0;
using (var query = new ManagementObjectSearcher(
"SELECT * " +
"FROM Win32_Process " +
"WHERE ProcessId=" + System.Diagnostics.Process.GetCurrentProcess().Id))
{
parentpid = query
.Get()
.OfType<ManagementObject>()
.Select(p => System.Diagnostics.Process.GetProcessById((int)(uint)p["ParentProcessId"]))
.FirstOrDefault().Id;
};
processStopEvent = new ManagementEventWatcher("SELECT * FROM Win32_ProcessStopTrace where ProcessID="+ parentpid);
processStopEvent.EventArrived += processStopEvent_EventArrived;
processStopEvent.Start();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run();
}
}
}
Output
c:\temp\idletask.log