Sunday, July 17, 2022

RVV Quotemate - A simple trading system for equity markets


There are plenty of free web portals for doing research to pick a stock . Example, Yahoo finance, Google Finance, finviz, trading view etc. These provide valuable information such as  real-time quotes, charts, news, institution actions etc. However, it requires hopping to different web browser tabs or windows,

RVV Quotemate attempts to provide a balance by providing a split view where top view provides real time quotes, alerts etc for the portfolio and bottom view displays the chosen web portal for the selected stock. 

RVV Quotemate  has following features.:

  •  Define multiple portfolios
  • Configure real time quotes source 
  • Configure timeout for refreshing Quotes and news
  • Configure a wide range of information to display
  • Configure alarms based on a wide range of parameters
  • Print portfolios
  • Refresh quotes and News on demand
  • define URLs that display a wide range of information for a selected stock
  • Browse , Refresh, move and back and forth web pages






Implementation

Workflow
Create a portfolio
  1. Click on "new portfolio" icon
  2. Enter a name for the portfolio
  3. Enter symbols in the following format <exchange>,<symbol><SPACE><exchange>,<symbol><SPACE> 
e.g, NASDAQ,AAPL NYSE,NOW   
Special symbols: NYSE,^DJI NASDAQ,^IXIC INDEXSP,^GSPC 

    4. Finally click Save icon to save,
















Configure auto refresh settings






 






Quote and news data can be refreshed  automatically if Auto Refresh is checked. Change Quote data refresh timeout and News data refresh timeout. The current settings are 5 and 10 minutes respectively. Alternatively Quote and news data can be refreshed manually.

Enable Triggers automatically checks the quote and news data and alerts if conditions are matched as specified in the triggers.

Configure columns

Various information can be displayed. The columns to display or hide can be done using right and left signals. Similarly the position of a column can be changed by moving up and down signals.









Column

Remark

Exchange

Stock exchange NYSE, NASDAQ

Symbol

Equity symbol

Alaram

Current  active Alarm

High Alaram and  Low Alaram

High and Low trading price to trigger alarm

Last Trade

Last trading price of the stock

Change, change 1-5

Last changes of the trading price of the stock

Open

Open value of the trading price of the stock

Volume

Current volume

Prv Close 1-5

Last 5 closing prices of the  stock

Day Range

Day's range

52Wk Range

52 weeks range

Ask

Current ask price

Bid

Current bid price

Bought

Quantity bought

Paid

Price paid per stock

Value

Total value

 Setup Triggers

Four categories  of  Triggers can be set. 
1. Price based
2. Change based
3. Intra change based.
4. News based

Price based

























This trigger is symbol specific  for the selected scrip from the dropdown list. it's useful when focusing on a few stocks. 
Purchase Info - Enter  quantity and price information. This total value is calculated and displayed in the value column.
Price Alert - Enter price value to alert if the price of the scrip goes above the upper limit or  goes below lower limit. For e.g., AAPL ticker price  goes above 130 or goes below 110. Upper limit alert is shown as $P+ and lower limit alert is shown as $P-.

Change based
























These triggers are symbol agnostic and monitored for all the stocks in the selected portfolio.
Two types of triggers can be defined.
Change Alert
This alert is based on current price scrip  compared to its last closing price .
$ change - if the $ price difference between current value of the stock and last closing price goes above the upper limit or goes below the lower limit.
For example, if  TSLA gained 40 points since last close. similarly if  BKNG lost 10 points since last close.  Upper limit alert is shown as $C+ and lower limit alert is shown as $C-.
% change - same above  except percentage change is considered. Therefore if the % price difference between current value of the stock and last closing price goes above the upper limit or goes below the lower limit.
For example, if  SHOP gained 1% since last close. similarly if  ZOOM lost 2%  since last close.  Upper limit alert is shown as %C+ and lower limit alert is shown as %C-.

Intra Change Alert
This alert is based on current price of the scrip  compared to its previous price during the current session.
$ Intra change - if the $ price of the stock rose by upper limit within upper limit timeout.
For example, if  SHOP gained 5 points with in last 5 mins. similarly if  NVDA lost 6 points with in last 5 minutes.  Upper limit alert is shown as $I+ and lower limit alert is shown as $I-.
$ change - same above  except percentage change of the stock price is considered. Therefore if the price difference between current value of the stock rose above the upper limit or fell  below the lower limit within upper  limit timeout or lower limit timeout.
For example, SHOP gained 2 % of its value within last 5 minutes. similarly if ZOOM lost 1%  of its value within last 3 minutes.  Upper limit alert is shown as %I+ and lower limit alert is shown as %I-.

News Alert
News alert is is triggered if the time difference between latest news published and the last news accessed is more than 10 minutes.
For example, if the last GOOG news accessed was at 3:30 pm and if the new news is available at 3:41 pm, the news alarm is raised. The next alarm is raised if the new news arrives after 10 minutes i.e., after 3:51pm.

Clear News Data clears the cached time of the last published news if checked.

Manage Bookmarks
In the public domain, there are a lot of highly useful websites that offer free services such as charting, analysis etc. The usefulness of the system can be enhanced by browsing such websites without switching to a browser.  Simply select  the bookmark from the toolbar and click adjoining "open bookmark " button. The second split window will display it's contents.

To define more bookmarks, 
1. Click on New bookmark icon
2. Enter a name
3. Update url address. 
4. click Save button

While defining URL, use <Symbol()> to pass the ticker selected in the top window.
0 => exchange 1 => ticker.
Example, if AMZN is selected, <Symbol(0:1)> would pass NASDAQ:AMZN or , <Symbol(1)>  would pass AMZN.
Optionally -SCROLL=n can be used to scroll n lines after the page is loaded.






















In addition, navigation arrows on tool bar can be used to go back and forth. Refresh button an be used to refresh. 

Generate Report
Using the print command, reports can be generated as shown below.





















Source and Binaries can be found here.

Tuesday, June 28, 2022

Setting and Retrieving IPv6 Configuration

Unlike IPv4, configuring and retrieving IPv6 information on a PC can be daunting. Depending on the configuration, multiple IP address might be returned by an IPv6 host.

IPV6Configurator tries to address this.

Details
The Adapters lists all available ethernet adapters.

Retrieving
Currently configured Ip addresses for DHCP/Manual for IPv4 and IPv6 are displayed

Setting
Netsh is used for configuring IP addresses.
In case of DHCP, only IP address is needed.

In case of manual configuration,  An IP address is needed. For IPv4, Subnet  and Gateway (optional) configuration are applicable. For IPv6  Prefix Length and Gateway(optional) configuration is applicable. Also "Update Router Discovery" disables the IPv6 host from sending nearest router discovery packets to the network.

Source and Binaries can be found here.

Sunday, June 5, 2022

Supporting Chinese, Japanese and Korean (CJK) languages in MBCS C++ Applications

There are plenty of  MBCS based legacy MFC applications out there that require CJK language support. Migrating to Unicode is not an option in these cases.

All the resources such as menus, dialogs, string tables used by the application are defined in the .rc file. 
However this rc file will not be a part of  the application. It will be part of  separate resource only DLL known as satellite Dll, There will be one satellite Dll for each  language supported by the application.  The main application just loads the satellite dlls for the current code page and avails the resources such as  menus, dialogs, string tables.

Steps for setting language
1. For MBCS applications, the correct code page i.e,  Japanese, chinese traditional or simplified, should be first set in the control panel.as shown below. The system should be restarted.

2.The .RC file must be encoded in the correct format as shown in the table below:
Japanese                        ShiftJIS
Chinese Traditional       Big5
Chinese Simplified        GB312

3. Language specific changes should be made in the RC file. An example for Japanese is shown below.

4. Use Unicode Win32 APIs for rendering.
Sometimes some special characters such as subscript and superscript  are not rendered correctly as they are not available in the codepage. In such cases Unicode Win32  apis can be used in the subclassed controls such as radio buttons.

Source and Binaries can be found here.

Tuesday, May 24, 2022

Learning some useful Windows OS Concepts

Windows  Operating system provides multiple technologies to develop distributed, scalable  Network based applications. Some not so common technologies and their usage is discussed below.

Overlapped IO 

  • Provide fastest asynchronous capability known to OS, enabling concurrent reads/writes by multiple threads. This comes free with .NET managed thread pool. 
    • Overlapped IO is useful when IOs are executed in parallel. For example, when an data file is assembled from strips received from UDP packets.
    • Operations such as ReadFile / WriteFile use OVERLAPPED structure parameter.
    • To perform asynchronous overlapped IO, files are opened in the CreateFile API with FILE_FLAG_OVERLAPPED flag.
    • In case of async, ReadFile / WriteFile calls might return immediately.
    • GetOverlappedResult API can be used to check status of the IO operation.

    IO Completion Port

    • IOCompletionPorts Provide fastest asynchronous IO capability known to OS, enabling concurrent reads/writes by multiple threads. 
      • IOCompletionPorts are useful to process results of multiple async IOs executed in parallel on multiple handles(file, socket, pipes etc) in a centralized place. For example, web server such as IIS.
      • To use IOCP functionality, first an IOCP needs to be created using CreateIOCompletionPort Api with an Invalid handle.
      • A thread pool needs to be created where multiple threads waits on the IOCP in QueuedCompletionStatus API to process completed IOs.
      • The handles on which async IOs are performed should be registered with the above IOCP. An unique Completion key can be also supplied.
      • Upon completion of asynchronous overlapped IOs on the above handles, one of the threads from the thread pool is released to process the results.
      • .Net library provides a separate IOCP thread pool. Classes such as FileStream internally use this to support async operations.
      • A good Winsock based example in C++ can be found here

      File Mapping backed by NTFS sparse file

      • Sparse data consists of data chunks containing 0s.
      • In a file system these occupy valuable disk space without adding much value.
      • NTFS introduces a new file type called sparse file.
      • The big advantage of sparse file is that when it contains sparse data, it’s not committed to disk as seen in the screenshot below.


      OS keeps track of these sparse data segments within in its internal data structures. When sparse data is
       read, OS returns an array of 0s instead of physically reading the file.
      • When real data is written to a sparse file, OS will update its internal data structures to reflect changes.
      • One of the disadvantages of shared memory is that either huge blocks of memory needs to be committed upfront or requires complicated programming to commit memory on the fly for the previously reserved address space.
      • NTFS sparse file backed memory mapped file enables accessing of a large address space without committing of disk space.
      • The OS transparently commits as much memory written when an application writes into the address space.
      • When a non written address space is accessed, faults are not raised; instead a block of 0s is returned.
      Usage Example
      • During acquisition data files generated vary in numbers and size. 
      • In other words, the total size of  data files generated can vary from 100MB to 1.5GB.
      • The storage is allocated in the shared memory results in underutilization since most of the committed pages contain sparse data thereby affecting overall performance of the system as well.
      • As an alternative if the storage is allocated in a memory mapped file, the disk space will be committed upfront. Again most pages will have sparse data.
      • A NTFS Sparse file backed MMF file eliminates both of these shortcomings since sparse data is never committed to disk.
      • As seen in the screenshot above, this difference can be seen.

      Source and Binaries can be found here.

      Wednesday, May 4, 2022

      Ultra fast compression and decompression Win32 APIs for realtime applications



      The NTFS file system internally uses Ultra fast Realtime data compression and decompression.
      Starting with Win10, it’s used internally by OS as shown below.

      The same APIs RtlCompressBuffer and RtlDecompressBuffer can be used by user applications as well.
      More info in the MSDN page.

      The tables below show data collected for first 10 data files during an acquisition run.
      Up to 38% of savings can be achieved over a standard 1.8 MB data file. Compression timings as low as 16 milliseconds and expansion timings as low as 15 milliseconds are clocked per data file.


      In an another example below,  a 5 MB DICOM image was compressed to almost 40% in 38 ms and decompressed in 8ms.

      TestApp  IM_0047

      Compression [fast]
      uncompressed size:      5509714 compressed size:        3378829 time in ms:38

      Decompression [fast]
      compressed size:        3378829 uncompressed size:      5509714 time in ms:8


      Compression [slow]
      uncompressed size:      5509714 compressed size:        3203440 time in ms:6235

      Decompression [slow]
      compressed size:        3203440 uncompressed size:      5509714 time in ms:8




      Source and Binaries can be found here.

      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.

      Thursday, March 31, 2022

      Simple Managed Inter Process Communication(IPC) Framework



      If the need is just to do a cross process communication within the box, it'd be bit heavy to use frameworks such as WCF as it comes with a learning curve and complex setup. However  home grown solutions such as messaged based implementation suffer from flexibility and heavy maintenance.
       The simpleIPC framework tries to strike right balance with the interface based programming coupled with zero setup.

      IPC Overview
      A lightweight Inter Process Communication (IPC) across process within the same PC in .Net can be implemented using named event object or Window kernel object and shared memory.
      • Interface based programming
      • Duplex communication support
      • Servers are identified by a unique string to which clients can generate a proxy to communicate. Some of the features:
      • Servers/ Clients generate Stubs/ Proxy based on an interface for communication using reflection.
      • As the proxy is based on the RealProxy object, intellisense is also supported in the Visual Studio IDE editor. Easy to debug, just a single file of code.
      • To access a managed server from an unmanaged client, reversePinvoke can be used.
      As shown in  the diagram above. IPC consists of a Client, Proxy, Stub and Server as described below

      • Client uses proxy based on RealProxy to make API call
      • Proxy serializes the input data using binary formatter to shared memory and signals stub
      • Stub deserializes the input data using binary formatter from shared memory and makes the call on the server using reflection
      • Stub serializes the results to shared memory and signals Proxy
      • Proxy deserializes the results from shared memory and returns it to the client
      Implementation
      Comes in two flavors windows kernel object based or named event kernel object based.
      Windows based uses a window kernel object for implementing the server


      As shown above multiple Windows are hosted in a thread. Each thread hosts a server. 
      • A Window based container can supports multiple servers per thread
      • A window based server cannot work across window stations
      Named object based uses a named event object for implementing the server.
      • A Named event object based container supports only one server
      • A named event object based server can work across window stations
      Example
      The interface  ICallInterface shown below is implemented by the server and ICallbackInterface is implemented by the client.
      namespace Example
      {
          [Serializable]
          public class regdata
          {
              public string name;
              public string regid;
          }
      
          public interface ICallInterface
          {
              string current { get; }
              string register(string name, string cbservername, out int ticket);
          }
      
          public interface ICallbackInterface
          {
              void update(int ticket, regdata data);
          }
      
      }


      Windows Simple IPC  Demo

      Server Process
      Simple IPC Server name for ICallback : winserver


      Client Process
      Simple IPC server name for ICallbackInterface : winclient




      Named Object Demo

      Server Process
      Simple IPC Server name for ICallback : namederver


      Client Process
      Simple IPC server name for ICallbackInterface :  namedclient


      Operation

      Server process starts
      client process starts

      Client:
      creates proxy for server
      calls register() on server using proxy

      Server:
      executes method register() 
      server returns success with Ticket #100

      Client:
      results are received from proxy : success, Ticket:100

      Server:
      creates proxy for client implementing callback interface
      calls update() method on the proxy
      Client:
      executes update() method

      Source and Binaries can be found here.


      Tuesday, March 8, 2022

      Unattended System monitoring with ProcessMonitor and UI automation

      ProcessMonitor from Microsoft is hugely popular and widely used to monitor events such as registry or file related updates. 
      If Process monitor is used to track an event  (e.g., a registry change) system wide, in an unattended scenario, some customization will be needed since ProcessMonitor  creates a large amount of log files in a short span of time i.e., around 5gb in 30 minutes. 
      The plan is to check Process monitor log files every 30 minutes for the event, take a snapshot and clear the logs. Using a task scheduler task and UIA this can be accomplished as discussed below.

      Background
      It was noticed that  on a particular windows 10 machine, the windows scaling factor changed from 100% to 150% randomly over a period of 1 week. The plan was to track changes made to the registry entry hkcu\control panel\desktop\logpixels.

      Implementation
      The solution should be generic so that it can be deployed seamlessly on different PCs.
      When done manually, following steps are executed.
      Steps
      1. First time, folders for the pm logs and captured events are created. This is done by importing  the configurations from ProcMonConfiguration.pmc.
      2. After that, when a specific user logs in, the unprocessed  logs are scanned for the event and cleared
      3. Every 30 mins logs are scanned for the event, logs are saved if found and then logs are cleared

      Step 1
       Init.cmd will do the initial setup.

      Add a filter  for registry entry as shown below:


      Change file backing as shown below.  A dedicated folder in required.


      A scheduler task startpm is created that runs on logon of specific user to  run startpm.cmd described below.
      A scheduler task savepmlog  is created that runs startpm.cmd for a specific user every 30 minutes to check for the event , take a screenshot and  clear the logs.


      Step 2
      startpm task is executed on logon of a specific user. This tasks runs savepmlog.cmd.

      Step 3
      savepmlog  task is executed every 30 mins after logon of a specific user. This tasks runs savepmlog.cmd.

      Init.cmd
      This script will do initial setup as discussed above.
             
      @echo on
      rd /s /q c:\temp\procmon
      md c:\temp\procmon\capture
      md c:\temp\procmon\pmlogs
      schtasks /create  /f /sc onlogon /tn startpm /it /ru rvvya /tr "%~dp0\savepmlog.cmd"
      schtasks /create  /f /sc minute /mo 30  /tn savepmlog /it /ru rvvya /tr "%~dp0\savepmlog.cmd"
      start "" "%~dp0\..\bin\Procmon64.exe" /accepteula /terminate
      start "" "%~dp0\..\bin\Procmon64.exe" /quiet /loadconfig "%~dp0\ProcmonConfiguration.pmc"
             
       
      savepm.cmd
      This script will check logs for the event and saves it along with a screenshot if found and then clears all logs.
             
      @echo on
      setlocal enabledelayedexpansion
      del C:\temp\procmon\capture\snapshot.bmp>nul
      del C:\temp\procmon\capture\logfile.csv>nul
      
      tasklist /fi "imagename eq Procmon64.exe" | find /i "Procmon64.exe">nul
      if !errorlevel! equ 0 start "" /wait "%~dp0\..\bin\Procmon64.exe" /Terminate
      start "" "%~dp0\..\bin\Procmon64.exe" /quiet /openlog C:\temp\procmon\pmlogs\uiatest.PML
      echo saving....
      start "" /min /wait "%~dp0\..\bin\savelog.exe"
      call :rename_logfile
      goto :eof
      
      :rename_logfile
      set logfile=logfile_%date%_%time%
      set logfile=%logfile::=_%
      set logfile=%logfile:/=_%
      find /c /v ""  C:\temp\procmon\capture\logfile.CSV | find /i ".CSV: 1">nul
      if !errorlevel! equ 0 (
      del C:\temp\procmon\capture\snapshot.bmp>nul
      del C:\temp\procmon\capture\logfile.csv>nul
      echo %date% %time% not found >> C:\temp\procmon\capture\results.log
      ) else (
      move C:\temp\procmon\capture\snapshot.bmp "C:\temp\procmon\capture\%logfile%".bmp
      move C:\temp\procmon\capture\logfile.csv "C:\temp\procmon\capture\%logfile%".csv
      echo %date% %time% found >> C:\temp\procmon\capture\results.log
      )
      exit /b
            
       
      Savelog.exe
      This is driven by UIA. This executable is launched by savepmlog.cmd. It takes a snapshot, saves pmlog files and then clears them. 

      Deployment
      The solution is deployed as shown below:
      \bin
      ui automation executables and the libraries and processminitor executable.
      \scripts
      contains task scheduler scripts described below and setup script

      Demo
      The following movie shows actual operation
      1. Regedit is started to add an event
      2. savepmlog task is triggered to check and record.




      Output 
      The  output files are available in C:\temp\procmon\capture folder.
      snapshot (logfile_11-06-2022_15_26_36.34.bmp)


      saved log file (logfile_11-06-2022_15_26_36.34.csv)
      "Time of Day","Process Name","PID","Operation","Path","Result","Detail"
      "15:26:06.7849475","regedit.exe","10344","RegQueryValue","HKCU\Control Panel\Desktop\LogPixels","SUCCESS","Type: REG_DWORD, Length: 4, Data: 150"

      results.log
      11-06-2022 14:32:30.01 not found 
      11-06-2022 15:00:22.52 not found 
      11-06-2022 15:26:36.36 found 

      Source and Binaries can be found here.