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.

Saturday, April 23, 2022

Share large allocated memory across applications without duplication


In some client - server applications running on same box,  a large amount of data may need to be shared. For example, an image acquisition application sharing the image with its clients. This typically involves using file Mapping objects to share memory across processes. However sometimes this can be overbearing if only two processes are involved.  Instead ReadProcessMemory and WriteProcessMemory APIs can be used.
What this means is a Process A can share its array with Process B without doing  IPC except sharing its PID and address of the array. Debuggers exploit this..

In the demo below, Writer process fills an array and shares it with Reader process. The array is read by the reader process using  ReadProcessMemory api . Also the array is repopulated using WriteProcessMemory api and is available to Writer process.





Sequence

Reader process starts
Writer process starts

Writer Process: 
creates an array of 100000  in writer process
Writes 100000 bytes containing 1
signals Reader with process handle and array memory address
sleeps for 5 seconds

Reader Process: 
Opens array using writer process handle and array memory address
Reads 100000 bytes containing 1
Writes 100000 bytes containing 2

Writer Process: 
Reads 100000 bytes containing 2

Source and Binaries can be found here.


Saturday, April 9, 2022

Use System Cache memory for your applications


Windows OS uses Cache memory set aside for faster IO with disk and disk. For example when an user launches an application  for the first time, it's loaded into cache memory. Next time when user launches the same application it loads faster since it's loaded from the cache memory instead from disk.
The size of cache is determined during system startup.

The red box in the screenshot below shows size of the cached files on the system.

The system cache is highly scalable and memory management is done by the cache manager in conjunction with memory manager. The following video describes internals for system cache.



User applications can also take advantage of this such that data can be directly written to cache memory and read from there.    The trick is to set FILE_ATTRIBUTE_TEMPORARY option during file creation. This will instruct cache manager to keep data in cache memory rather than write to disk.

It's also important the file readers should not try to open the file in anyway. This will make the file
contents to be written to disk. The alternative is to create a duplicate handle and use it to access the file.
Using overlapped IO for read and write ensures proper management of the file pointer by readers and writers.

The list of files in the screenshot below are the actual cached files of  the data files from an acquisition.  The red box indicates that 0 bytes are committed to the disk for the all the cached data files. Similarly the cyan colored rectangle indicates the actual sizes of the selected data file.


Example:
In this example, 100000 bytes are written by the writer process to the system cache via temporary file  as in the first screenshot and the same is read from the system cache by the reader process as in the second screenshot. From the third screenshot we can notice that all it stays in system cache memory and nothing is committed to disk.






Sequence

Reader process starts
Writer process starts

Writer Process: 
creates test1.dat on system cache
Writes 100000 bytes containing 1
signals Reader with duplicate file handle
sleeps for 5 seconds

Reader Process: 
Opens test1.dat on system cache using duplicate file handle
Reads 100000 bytes containing 1
Writes 100000 bytes containing 2

Writer Process:
Reads 100000 bytes containing 2


Source and Binaries can be found here.