Saturday, April 30, 2022

Running Custom applications using task scheduler when the system is idle

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.log
rem 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
started 07-06-2022  4:19:21.85 
ended 07-06-2022 11:59:01

Source and Binaries can be found here.

No comments:

Post a Comment