Recipe12.23.Watching the Filesystem for Specific Changes to One or More Files or Directories


Recipe 12.23. Watching the Filesystem for Specific Changes to One or More Files or Directories

Problem

You want to be notified when a file and/or directory is created, modified, or deleted. In addition, you might need to be notified of any of these actions for a group of files and/or directories. This can aid in alerting your application when a file, such as a logfile, grows to a certain size, after which it must be truncated.

Solution

To be notified when an action takes place in the filesystem, you need to employ the FileSystemWatcher class. The TestWatcher method shown in Example 12-12 sets up a FileSystemWatcher object to watch the entire C:\ drive for any changes. The changes are limited to any file with the extension .txt. At the end of this method, the events are wired up for each one of the changes listed in the NotifyFilter property.

Example 12-12. Using the FileSystemWatcher class

 public static void TestWatcher( ) {     using (FileSystemWatcher fsw = new FileSystemWatcher( ))     {         fsw.Path = @"c:\";         fsw.Filter = @"*.txt";         fsw.IncludeSubdirectories = true;         fsw.NotifyFilter = NotifyFilters.FileName |             NotifyFilters.Attributes  |             NotifyFilters.LastAccess  |             NotifyFilters.LastWrite   |             NotifyFilters.Security    |             NotifyFilters.Size        |             NotifyFilters.CreationTime|             NotifyFilters.DirectoryName;         fsw.Changed += new FileSystemEventHandler(OnChanged);         fsw.Created += new FileSystemEventHandler(OnCreated);         fsw.Deleted += new FileSystemEventHandler(OnDeleted);         fsw.Renamed += new RenamedEventHandler(OnRenamed);         fsw.Error += new ErrorEventHandler(OnError);         fsw.EnableRaisingEvents = true;         string file = @"c:\myfile.txt";         string newfile = @"c:\mynewfile.txt";         using (FileStream stream = File.Create(file))         {             // Use stream var…             byte[] bytes = new byte[5] {32,33,34,35,36};             stream.Write(bytes, 0, bytes.Length);         }         using (FileStream stream = File.Create(newfile))         {             // Use stream var…             byte[] bytes = new byte[5] {32,33,34,35,36};             stream.Write(bytes, 0, bytes.Length);         }         File.Delete(file);         File.Delete(newfile);         // Wait to allow the event handlers to catch up         // to the events raised by the filesystem.         Thread.Sleep(1000);     } } 

The following code implements the event handlers to handle the events raised by the FileSystemWatcher object, which we created and initialized in the TestWatcher method:

 public static void OnChanged(object source, FileSystemEventArgs e) {     Console.WriteLine("File " + e.FullPath + " --> " + e.ChangeType); } public static void OnDeleted(object source, FileSystemEventArgs e) {     Console.WriteLine("File " + e.FullPath + " --> " + e.ChangeType); } public static void OnCreated(object source, FileSystemEventArgs e) {     Console.WriteLine("File " + e.FullPath + " --> " + e.ChangeType); } public static void OnRenamed(object source, RenamedEventArgs e) {     Console.WriteLine("File " + e.OldFullPath + " (renamed to)--> " + e.FullPath); } public static void OnError(object source, ErrorEventArgs e) {     Console.WriteLine("Error " + e.ToString( )); } 

The output of the TestWatcher method is shown here:

 File c:\myfile.txt --> Created File c:\myfile.txt --> Changed File c:\mynewfile.txt --> Created File c:\mynewfile.txt --> Changed File c:\myfile.txt --> Deleted 

Discussion

Watching for changes in the filesystem centers around the FileSystemWatcher class. This class can watch for filesystem changes on the local machine, a networked drive, and even a remote machine. The limitations of watching files on a remote machine are that the watching machine must be running versions of Windows starting from Windows NT 4.0 through 2000, XP, Server 2003, and Windows Vista. The one caveat for Windows NT 4.0 is that a Windows NT 4.0 machine cannot watch another remote Windows NT 4.0 machine.

The FileSystemWatcher object cannot watch directories or files on a CD or DVD drive (including rewritables) in the current versions of the Framework. This limitation might be revisited in a future version. This object does watch files regardless of whether their hidden property is set.

To start watching a filesystem, we need to create an instance of the FileSystem-Watcher class. After creating the FileSystemWatcher object, we can set its properties in order to focus our efforts in watching a filesystem. Table 12-10 examines the various properties that can be set on this object.

Table 12-9. Properties that can be set on the FileSystemWatcher object

Property name

Description

Path

A path to a directory to watch. The following are some examples of valid values for this property:

 @"C:\temp" @"C:\Program Files" @"C:\Progra~1" @"..\..\temp" @"\\MyServer\temp" @"." @"" 

Note that if a directory is specified, changes to it, such as deleting it or changing its attributes, are not watched. Only changes within the directory are watched. Assigning an empty string forces the current directory to be watched.

IncludeSubdirectories

Set to true to monitor all subdirectories as well, or false to watch only the specified directory.

Filter

Specifies a specific subset of files to watch. The following are some examples of valid values for this property:

 // Watch only .exe files @"*.exe" // Watch all files @"*" // Watch all files @"" // Watch all files beginning // with the letter 'a' @"a*" // Watch all files with the // name "test" and // having a three-letter // extension starting // with the letter 'd' @"test.d??" 

NotifyFilter

One or more NotifyFilters enumeration values. This enumeration is marked with the FlagsAttribute, so each enumeration value can be ORed together using the | operator. By default, this property is set to FileName, DirectoryName, and LastWrite. The members of the NotifyFilters enumeration are shown in Table 12-11.

EnableRaisingEvents

When this property is set to true, the FileSystemWatcherobject starts watching the filesystem. To stop this object from watching the filesystem, set this property to false.

InternalBufferSize

The internal buffer size in bytes for this object. It is used to store information about the raised filesystem events. This buffer defaults in size to 8192 bytes. See additional information about this property next.


The NotifyFilters enumeration values in Table 12-11 determine which events the FileSystemWatcher object watches. For example, the OnChanged event can be raised when any of the following NotifyFilters enumeration values are passed to the NotifyFilter property:

 NotifyFilters.Attributes NotifyFilters.Size NotifyFilters.LastAccess NotifyFilters.LastWrite NotifyFilters.Security NotifyFilters.CreationTime 

Table 12-10. NotifyFilters enumeration value definitions

Enumeration name

Description

Attributes

Watches for changes to a file or directory's attributes.

CreationTime

Watches for changes to a file or directory's creation time.

DirectoryName

Watches for changes to a directory's name.

FileName

Watches for changes to a file's name.

LastAccess

Watches for changes to a file or directory's last-accessed property.

LastWrite

Watches for changes to a file or directory's last-written-to property.

Security

Watches for changes to a file or directory's security settings.

Size

Watches for changes to a file or directory's size.


The OnRenamed event can be raised when any of the following NotifyFilters enumeration values are passed to the NotifyFilter property:

 NotifyFilters.DirectoryName NotifyFilters.FileName 

The OnCreated and OnDeleted events can be raised when any of the following NotifyFilters enumeration values are passed to the NotifyFilter property:

 NotifyFilters.DirectoryName NotifyFilters.FileName 

There are times when the FileSystemWatcher object cannot handle the number of raised events coming from the filesystem. In this case, the Error event is raised, informing you that the buffer has overflowed and specific events may have been lost. To reduce the likelihood of this problem, we can limit the number of raised events by minimizing the number of events watched for in the NotifyFilter property. Limiting the filter on the Filter property, however, will not affect the number of raised events. To decrease the number of raised events further, you can set the IncludeSubdirectories property to false. You might also consider increasing the InternalBufferSize property. To estimate what size to increase this buffer to, Microsoft provides the following tips:

  • A 4k-byte buffer can keep track of changes for about 80 files in a directory.

  • Every event consumes 16 bytes of buffer space.

  • In addition to these 16 bytes, the filename is stored as Unicode characters.

  • If you are using Windows 2000, consider increasing/decreasing the buffer size by a multiple of 4k bytes. This is the same size as a default memory page.

  • If you do not know your operating system's page size, use the following code to increase the FileSystemWatcher's buffer size:

     FileSystemWatcher fsw = new FileSystemWatcher( ); fsw.InternalBufferSize *= Multiplier; 

    where Multiplier is an integer used to increase the size of the buffer. This makes the most efficient use of the buffer space.

You should increase the InternalBufferSize only as a last resort. This is an expensive operation, because the buffer space is created in nonpaged memory. Nonpaged memory is memory available to the process that will always be in physical memory. It is a limited resource and is shared across all processes on the machine, so it is possible to affect the operation of other processes using this pool if too much is requested.

In many cases, a single action performed by the user produces many filesystem events. Creating a text file on the desktop yields the following changes:

 File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\documents and settings\administrator\ntuser.dat --> Changed File c:\documents and settings\administrator\ntuser.dat --> Changed File c:\documents and settings\administrator\ntuser.dat --> Changed File c:\documents and settings\administrator\ntuser.dat --> Changed File c:\documents and settings\administrator\ntuser.dat.log --> Changed File c:\winnt\system32\config\software.log --> Changed File c:\winnt\system32\config\software.log --> Changed File c:\winnt\system32\config\software.log --> Changed File c:\winnt\system32\config\software --> Changed File c:\winnt\system32\config\software --> Changed File c:\winnt\system32\config\software --> Changed File c:\winnt\system32\config\software --> Changed File c:\winnt\system32\config\software.log --> Changed File c:\documents and settings\administrator\desktop\newdoc.txt Created 

Much of this work is simply registry access. Not until the end of this listing is the text file actually created.

Another example of multiple filesystem events firing for a single action is when this newly created text file is opened by double-clicking on it. The following events are raised by this action:

 File c:\winnt\system32\notepad.exe --> Changed File c:\winnt\system32\notepad.exe --> Changed File c:\documents and settings\administrator\recent\newdoc.txt.lnk --> Deleted File c:\documents and settings\administrator\recent\newdoc.txt.lnk --> Created File c:\documents and settings\administrator\recent\newdoc.txt.lnk --> Changed File c:\winnt\system32\config\software.log --> Changed File c:\winnt\system32\shell32.dll --> Changed File c:\winnt\system32\shell32.dll --> Changed 

Of course, your results may vary, especially if another application accesses the registry or another file while the text file is being opened. Even more events may be raised if a background process or service, such as a virus checker, is accessing the filesystem.

See Also

See the "FileSystemWatcher Class" and "NotifyFilters Enumeration" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net