Partager via


Create a simple File Watcher to monitor folder changes: Windows Prefetcher

There’s a folder like c:\Windows\Prefetch on your machine, probably with a few hundred files and a few megabytes. If you search the web, you’ll find a few comments about Prefetch (enhanced by SuperFetch and ReadyBoost:  https://en.wikipedia.org/wiki/Prefetcher ).
Below is sample code you can run to monitor the activity of this folder. The program shows a window that displays any changes to the folder as they occur. Start a few programs on your computer and you’ll see some action.
The sample code uses the FileSystemWatcher  which makes it easy to write such a program.

When your computer starts to run a program, the program and its dependencies need to be loaded into primary memory for execution. The data being read is stored in some sort of slower memory like disk or SSD (see https://blogs.msdn.microsoft.com/calvin_hsia/2012/08/10/performance-of-memory-vs-disk/ )
For example, you start Outlook. The computer then may have to access hundreds or thousands of various pieces of data scattered on the slower memory.
Suppose you start Outlook every day. The prefetcher could have already fetched all that data into faster memory before its needed, so Outlook starts much faster. Similarly, rebooting a system causes lots of programs to load. A prefetcher can fetch the data before its actually required.
Additionally, the prefetched data can be retrieved in an optimum sequence: accounting for data locality can improve fetch times.

If you were working on the Windows team and were given a task to write a Prefetch feature that makes the computer seem faster to most users, how would you approach it? Hmm… a computer is very good at recording information. A folder is a very convenient place to put recorded information. You’d probably need some way to cap the size of the collected data so it doesn’t eat too much user space. You could record all slow memory accesses and how often the program was accessed. When the OS starts a program it could consult the Prefetch data to see if it’s already been “fetched” and thus seem faster to the user.

 

For example, I start and old and a new app Visual Foxpro and Paint 3D on my Windows 10 machine, and I see
[03:16:42:331],31,Changed VFP9.EXE-F6167E92.pf
[03:16:42:331],32,Changed VFP9.EXE-F6167E92.pf
[03:17:03:769],33,Created PAINTSTUDIO.VIEW.EXE-8DC0D32B.pf
[03:17:03:770],33,Changed PAINTSTUDIO.VIEW.EXE-8DC0D32B.pf

 

 

<code>

 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

/*
File->new->Project->C#->WPF App "PreFetchWatcher"
*/

namespace PrefetchWatcher
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        TextBox _txtStatus;
        public string FileFilter { get; set; }
        public bool ShowFileChanges { get; set; } = true;

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
        }

        private async void MainWindow_Loaded(object sender, RoutedEventArgs ee)
        {
            try
            {
                this.DataContext = this;
                this.Height = 600;
                this.Width = 1000;
                var sp = new StackPanel() { Orientation = Orientation.Vertical };
                var spControls = new StackPanel() { Orientation = Orientation.Horizontal };
                sp.Children.Add(spControls);

                spControls.Children.Add(new Label() { Content = "FileFilter", ToolTip = "if not empty, will filter file change events  (case insensitive)" });
                var txtfileFilter = new TextBox() { Width = 100, Height = 20, VerticalAlignment = VerticalAlignment.Top };
                txtfileFilter.SetBinding(TextBox.TextProperty, nameof(FileFilter));
                spControls.Children.Add(txtfileFilter);


                var chkShowFileChanges = new CheckBox()
                {
                    Content = "ShowFileChanges",
                    Height = 20,
                    VerticalAlignment = VerticalAlignment.Top,
                    Margin = new Thickness(10, 0, 10, 0)
                };
                chkShowFileChanges.SetBinding(CheckBox.IsCheckedProperty, nameof(ShowFileChanges));
                spControls.Children.Add(chkShowFileChanges);


                _txtStatus = new TextBox()
                {
                    IsReadOnly = true,
                    VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
                    HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
                    IsUndoEnabled = false,
                    FontFamily = new FontFamily("Courier New"),
                    FontSize = 10,
                    Height = 200,
                    MaxHeight = 200,
                    HorizontalContentAlignment = HorizontalAlignment.Left
                };
                sp.Children.Add(_txtStatus);
                this.Content = sp;
                var folderPreFetch = System.IO.Path.Combine(Environment.GetEnvironmentVariable("SystemRoot"), "Prefetch");
                AddStatusMsg($"Started watching {folderPreFetch}");
                await Task.Run(() =>
                {
                    var watcher = new FileSystemWatcher(folderPreFetch);
                    var hndler = new FileSystemEventHandler((o, e) =>
                    {
                        bool showit = true;
                        if (!string.IsNullOrEmpty(FileFilter))
                        {
                            if (e.Name.ToLower().IndexOf(FileFilter.Trim().ToLower()) < 0)
                            {
                                showit = false;
                            }
                        }
                        if (e.ChangeType == WatcherChangeTypes.Changed && !this.ShowFileChanges)
                        {
                            showit = false;
                        }
                        if (showit)
                        {
                            AddStatusMsg($"{e.ChangeType} {e.Name}");
                            //switch (e.ChangeType)
                            //{
                            //    case WatcherChangeTypes.Created:
                            //        break;
                            //    case WatcherChangeTypes.Changed:
                            //        break;
                            //    case WatcherChangeTypes.Deleted:
                            //        break;
                            //    case WatcherChangeTypes.Renamed:
                            //        break;
                            //}
                        }
                    });
                    watcher.Created += hndler;
                    watcher.Deleted += hndler;
                    watcher.Changed += hndler;
                    //watcher.Renamed += hndler;
                    watcher.EnableRaisingEvents = true;
                });

            }
            catch (Exception ex)
            {
                this.Content = ex.ToString();
            }
        }


        void AddStatusMsg(string msg, params object[] args)
        {
            if (_txtStatus != null)
            {
                // we want to read the threadid 
                //and time immediately on current thread
                var dt = string.Format("[{0}],{1,2},",
                    DateTime.Now.ToString("hh:mm:ss:fff"),
                    Thread.CurrentThread.ManagedThreadId);
                _txtStatus.Dispatcher.BeginInvoke(
                    new Action(() =>
                    {
                        // this action executes on main thread
                        var str = string.Format(dt + msg + "\r\n", args);
                        // note: this is a memory leak: can clear periodically
                        _txtStatus.AppendText(str);
                        _txtStatus.ScrollToEnd();
                    }));
            }
        }
    }
}

</code>