Home > Articles > Programming > Windows Programming

This chapter is from the book

Directory and File Operations

In past versions of Visual Basic, we were relegated to the FileSystemObject (or a similar third-party control) or the Win32 API to implement most file operations. The System.IO name-space provides a robust set of objects for our tool belt. These objects can be leveraged to solve a host of problems and ease and simplify interaction with the file system.

As programmers, it is important for us to understand the key aspects of the Windows file system. A file, in Windows, is an ordered and named collection of a sequence of bytes having persistent storage. When you think of files, you think of a filename, file size, attributes, and directory path. Directories in Windows are simply another type of file that contains other files and subdirectories. This section illustrates how to interact with the Windows file system using the System.IO namespace.

Access and Attributes

To access directories and files using the .NET Class Library you work with two primary classes: DirectoryInfo and FileInfo. These classes are designed to provide most of the file system functionality and information that your applications need.

The DirectoryInfo and Directory classes are used to create, move, and enumerate through directories and subdirectories. The Directory class contains the associated static methods, while DirectoryInfo contains the instance methods. An instance of the DirectoryInfo class represents one physical directory. With it, you can call the GetDirectories method to return a list of the directory's subdirectories. You can also return a list of files in the directory with the GetFiles method. Of course, there are a number of other important properties and methods inside the DirectoryInfo class.

The FileInfo and File classes are used to create, copy, delete, move, and open files. The File class contains the associated static methods, where FileInfo contains the instance methods. Using an instance of the FileInfo class, you can return specific attributes of a given file. For example, you can read the file's name, size, and parent directory. The actual content of a file is accessed via the FileStream object. The FileStream class allows for both synchronous and asynchronous read and writes to a file.

To best illustrate the use of these classes, we will create a simple directory and file listing application. The application will read a list of directories, and for each directory, display a list of files contained by it. Additionally, for both the selected file and the selected directory, we will list a number of key attributes. Figure 7.1 illustrates the form that we will use to display the application's information. Of course, I use the term application loosely. This is just some sample code with an attached form.

Figure 7.1 Access and attributes form.

The code for the example is provided in Listing 7.1. It involves code to create the form, a form load event, and a pair of index change events for the two list boxes.

The code starts with a few global variable declarations to store a path, directory, and filename. This is followed by the basic form creation code.

LISTING 7.1 Access and Attributes

Public Class Form2 
   Inherits System.Windows.Forms.Form 

   'form-level scope declarations 
   Dim myPath As String = "c:\" 
   Dim myDirName As String 
   Dim myFileName As String 

# Region " Windows Form Designer generated code "
   Public Sub New() 
     MyBase.New()

     'This call is required by the Windows Form Designer. 
     InitializeComponent() 

     'Add any initialization after the InitializeComponent() call 
   End Sub 

   'Form overrides dispose to clean up the component list. 
Public Overloads Overrides Sub Dispose() 
   MyBase.Dispose() 
   If Not (components Is Nothing) Then 
     components.Dispose() 
   End If 
End Sub 
Private WithEvents label1 As System.Windows.Forms.Label 
Private WithEvents label2 As System.Windows.Forms.Label 
Private WithEvents labelDirectoryInfo As System.Windows.Forms.Label 
Private WithEvents listBoxFiles As System.Windows.Forms.ListBox 
Private WithEvents labelFileInfo As System.Windows.Forms.Label 
Private WithEvents listBoxDirectories As System.Windows.Forms.ListBox 
Private WithEvents buttonClose As System.Windows.Forms.Button 

   'Required by the Windows Form Designer 
Private components As System.ComponentModel.Container 

'NOTE: The following procedure is required by the Windows Form Designer 
'It can be modified using the Windows Form Designer. 
'Do not modify it using the code editor. 
<System.Diagnostics.DebuggerStepThrough()> Private Sub _ 
   InitializeComponent() 
   Me.labelFileInfo = New System.Windows.Forms.Label() 
   Me.listBoxFiles = New System.Windows.Forms.ListBox() 
   Me.labelDirectoryInfo = New System.Windows.Forms.Label() 
   Me.listBoxDirectories = New System.Windows.Forms.ListBox() 
   Me.buttonClose = New System.Windows.Forms.Button() 
   Me.label1 = New System.Windows.Forms.Label() 
   Me.label2 = New System.Windows.Forms.Label()
   Me.labelFileInfo.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D  
   Me.labelFileInfo.Location = New System.Drawing.Point(248, 216) 
   Me.labelFileInfo.Size = New System.Drawing.Size(200, 160) 
   Me.labelFileInfo.TabIndex = 4 
   Me.labelFileInfo.Text = "label3" 
   Me.listBoxFiles.Location = New System.Drawing.Point(8, 216) 
   Me.listBoxFiles.Size = New System.Drawing.Size(232, 160) 
   Me.listBoxFiles.TabIndex = 1 
   Me.labelDirectoryInfo.BorderStyle = _ 
       System.Windows.Forms.BorderStyle.Fixed3D
   Me.labelDirectoryInfo.Location = New System.Drawing.Point(248, 24) 
   Me.labelDirectoryInfo.Size = New System.Drawing.Size(200, 160) 
   Me.labelDirectoryInfo.TabIndex = 4 
   Me.labelDirectoryInfo.Text = "label3" 
   Me.listBoxDirectories.Location = New System.Drawing.Point(8, 24)
   Me.listBoxDirectories.Size = New System.Drawing.Size(232, 160) 
   Me.listBoxDirectories.TabIndex = 0 
   Me.buttonClose.Location = New System.Drawing.Point(376, 384) 
   Me.buttonClose.TabIndex = 5 
   Me.buttonClose.Text = "Close" 
   Me.label1.Location = New System.Drawing.Point(8, 8) 
   Me.label1.TabIndex = 2 Me.label1.Text = "Directories" 
   Me.label2.Location = New System.Drawing.Point(8, 200) 
   Me.label2.TabIndex = 3 
   Me.label2.Text = "Files" 
   Me.AcceptButton = Me.buttonClose 
   Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) 
   Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog 
   Me.ClientSize = New System.Drawing.Size(453, 410) 
   Me.Controls.AddRange(New System.Windows.Forms.Control() _ 
      {Me.buttonClose, Me.labelFileInfo, Me.listBoxFiles, _ 
     Me.listBoxDirectories, Me.labelDirectoryInfo, Me.label2, _ 
     Me.label1}) 
   Me.Text = "Directory And Files" 

End Sub 

Inside the form's load event we return a list of directories. To do so, first we instantiate a DirectoryInfo class with the line

Dim myDirectory As New System.IO.DirectoryInfo(path:=myPath). 

where myPath is a valid path (c:\). Next, we call the GetDirectories method of the DirectoryInfo class. This returns an array of DirectoryInfo objects that represent the path's subdirectories. Finally, we select an item in the directory list box. This fires the index-changed event of the directory list box.

Private Sub Form1_Load(ByVal sender As System.Object, _ 
      ByVal e As System.EventArgs) Handles MyBase.Load 
      'purpose: load the form, set the control values 

      'local scope 
     Dim myDirectories() As System.IO.DirectoryInfo 
     Dim i As Integer 

      'create a new directory object pointed at c: 
     Dim myDirectory As New System.IO.DirectoryInfo(path:=myPath) 
     'return the sub directories of c: from the global directory object 
     myDirectories = myDirectory.GetDirectories() 

     'loop through the directories and add them to the list box 
     For i = 0 To UBound(myDirectories) - 1 

        'add the directory name to the list 
        listBoxDirectories().Items.Add(myDirectories(i).Name) 

     Next 

     'select the first directory in the list 
     '   note: this will trigger the events that fill the label control 
     '       and the files list box and its label control 
     listBoxDirectories().SelectedIndex = 0 

End Sub 

Once a user selects a directory, we must return to her a list of files. In our example, we do this inside the listBoxDirectories index change event (listBoxDirectories_SelectedIndexChanged). We first create a new DirectoryInfo object based on our starting path value and the user's selected directory:

myDirectory = New System.IO.DirectoryInfo(path:=(myPath & myDirName)) 

Next we return an array of FileInfo objects using the method GetFiles of the DirectoryInfo class. Finally, we loop through the array and add the filenames to the list box and select the first file in the list.

Private Sub listBoxDirectories_SelectedIndexChanged(ByVal sender As _ 
   System.Object, ByVal e As System.EventArgs) Handles _ 
   listBoxDirectories.SelectedIndexChanged 

   'purpose: synchronize the files list box with the selected directory 
   '        and display the selected directory's information 

   'local scope 
  Dim myDirectory As System.IO.DirectoryInfo 
  Dim dirInfo As String 
  Dim myFiles() As System.IO.FileInfo 
  Dim i As Integer 

  'clear the listbox of its current contents 
  listBoxFiles().Items.Clear() 

   'get a directory info object based on the user's selection 
   myDirName = listBoxDirectories().SelectedItem.ToString & "\" 
   myDirectory = New System.IO.DirectoryInfo( _ 
      path:=(myPath & myDirName)) 

   'set the dir info 
   dirInfo = dirInfo & "Path: " & myDirectory.FullName & vbCrLf 
   dirInfo = dirInfo & "Attributes: " & myDirectory.Attributes.ToString _ 
     & vbCrLf 
   labelDirectoryInfo().Text = dirInfo 

   'get the files in the directory 
   myFiles = myDirectory.GetFiles() 

   'check for files 
   If UBound(myFiles) >= 0 Then 

   'loop through the files array and add to the listbox 
   For i = 0 To UBound(myFiles) - 1 
      listBoxFiles().Items.Add(myFiles(i).Name) 
   Next 

   'select the file in the list, this will trigger the event to change 
   '   the file info label control listBoxFiles().SelectedIndex = 0 

   End If 
End Sub 

As a filename is selected (listBoxFiles_SelectedIndexChanged event) we update the contents of the file information label. To do this we create an instance of the FileInfo class based on the file's path and name. We then read some of the file's properties and display them to a label control.

Private Sub listBoxFiles_SelectedIndexChanged(ByVal sender As _ 
   System.Object, ByVal e As System.EventArgs) Handles _ 
   listBoxFiles.SelectedIndexChanged 

   'purpose:   change the contents of the file properties label 

   'local scope 
   Dim myFile As System.IO.FileInfo 
   Dim fileInfo As String 

   'set the file name 
   myFileName = listBoxFiles().SelectedItem.ToString 

   'create a new file object 

   myFile = New System.IO.FileInfo(fileName:=myPath & myDirName & _ 
        myFileName) 

   'set the file info to display 
   fileInfo = fileInfo & "Directory: " & myFile.DirectoryName & vbCrLf 
   fileInfo = fileInfo & "Created Time: " & myFile.CreationTime & vbCrLf 
   fileInfo = fileInfo & "Size: " & myFile.Length & vbCrLf 
   fileInfo = fileInfo & "Last Accessed: " & myFile.LastAccessTime & _ 
     vbCrLf 
   fileInfo = fileInfo & "Attributes: " & myFile.Attributes.ToString & _ 
     vbCrLf 

   'set the label to the file's info 
   labelFileInfo().Text = fileInfo 

End Sub 
Private Sub buttonClose_Click(ByVal sender As System.Object, _ 
     ByVal e As System.EventArgs) Handles buttonClose.Click 

      'purpose:   end the application 

      'close the form 
     Me.Close() 
   End Sub 
#End Region 
End Class 

In the prior example we used the FileInfo class to display file attributes to the user. This class has a number of properties that are useful when working with files. Some of these that are of keen interest are listed in Table 7.2.

TABLE 7.2 Properties of the FileInfo Class

Property

Data Type

Description

Attributes

FileSystemInfo.Attributes

The Attributes property is used to get or set the file's attributes. The value of the property is based on a combination of the file-attribute flags found in FileSystemInfo.Attributes. Values include: archive, compressed, directory, hidden, offline, read-only, system, and temporary.

CreationTime

Datetime

The CreationTime property returns or sets the time that a file was created.

Directory

Directory class

The Directory property returns an instance of the file's parent directory as the Directory class.

DirectoryName

String

The DirectoryName property returns the full path to the file.

Exists

Boolean

The Exists property returns true if a file physically exists and false if it does not.

Extension

String

The Extension property returns the file's extension (.txt for text files).

FullName

String

The FullName property returns the complete path to the file and its name.

LastAcccessTime

DateTime

The LastAccessTime property returns or sets the time that the file was last accessed.

LastWriteTime

DateTime

The LastWriteTime property returns or sets the time the file was last written to.

Length

Long

The Length property returns the size of the file in bytes.

Name

String

The Name property returns the name of the file.


Creation, Deletion, and Manipulation

So far we've looked at how we access directories and files and return their attributes. Now we will explore the basic tasks of creating, deleting, and moving files and directories. The .NET Framework's System.IO offers us all the necessary methods to perform these tasks.

Directories

For manipulating directories we primarily use five basic methods: Create, CreateSubdirectory, Delete, MoveTo, and Refresh. These methods are called from the DirectoryInfo class:

  • The Create method creates the directory to which the DirectoryInfo object refers. The CreateSubdirectory method accepts the subdirectory's path as a parameter. It then creates the subdirectory and returns it as a DirectoryInfo instance.

  • The Delete method has two overloaded parameter sets. The first takes no parameters and simply deletes the directory and its contents to which the DirectoryInfo instance refers. The second takes a Boolean value that indicates whether the delete call should also be recursive; that is, whether it should delete all its subdirectories and their contents.

  • The MoveTo method takes a string as a parameter (destDirName) that represents the destination path. The destination path, as you may have guessed, defines the directory and its path to which the current directory (defined by the DirctoryInfo instance) should be moved.

  • The Refresh method refreshes the DirectoryInfo object instance. The method reloads the directory information. This is useful for long-running objects to maintain the most up-to-date information on the directory attributes.

Listing 7.2 is a procedure that demonstrates these methods. The procedure can be copied into a console application and executed.

LISTING 7.2 Directories Create, Move, and Delete

Module Module1 

   Sub Main() 
     'purpose:   demonstrate code that does the following: 
     '          1. creates a directory 
     '          2. creates a sub directory 
     '          3. moves the sub directory under the first directory 
     '          4. deletes the sub directory 
     'local scope 
     Dim myDirectoryParent As System.IO.DirectoryInfo 
     Dim myDirectory2 As System.IO.DirectoryInfo 

     'create a new instance of the directory info object 
     myDirectoryParent = New System.IO.DirectoryInfo(path:="c:\") 

     'create a sub directory under the parent directory  
    myDirectoryParent.CreateSubdirectory(path:="new directory 1") 

     'create another sub directory under the parent directory 
     myDirectoryParent.CreateSubdirectory(path:="new directory 2") 

     'create a directory object based on the 2nd directory 
     myDirectory2 = New System.IO.DirectoryInfo( _ 
          path:="c:\new directory 2") 

     'move the second directory under the first 
     'note: you must supply its new name (can be the same) 
     myDirectory2.MoveTo( _ 
          destDirName:="c:\new directory 1\new directory 2") 

     'delete the second directory and its contents 
     'note: you could set the recursive property to true 
     '      to delete its sub directories as well 
     myDirectory2.Delete() 

   End Sub 
End Module 

Files

For working with files we use a similar set of methods found in the FileInfo class. The Create, Delete, MoveTo, and Refresh methods are all present. However, programming with files requires a few additional methods: AppendText, CopyTo, CreateText, Open, OpenRead, and OpenWrite (all of which will be defined later in this chapter).

The Create, MoveTo, and Delete methods are very similar to the DirectoryInfo class. The CopyTo method copies a version of the file to which the FileInfo instance refers. The method is overloaded with two parameter sets. The first copies an instance of the given file to the new directory specified in the parameter destFileName. This method raises an exception if a file with the same name already exists in the destination directory. Upon success, the method returns an instance of the FileInfo class based on the copied file. The second overloaded method takes both the destFileName and the parameter overwrite as a Boolean. Set this parameter to True if you want the Copy method to overwrite any existing file with the same name and False if you do not. Sample code that illustrates these methods is provided for you in Listing 7.3.

Note the return type of the Create method. On file create, we are returned an instance of the FileStream object. This object is a stream object based on the new file. The stream object provides us the ability to read and write to the file. Streams, as well as reading and writing to files, are discussed later in this chapter.

LISTING 7.3 Files Create, Move, CopyTo, and Delete

Imports System.IO 

Module Module1 

   Sub Main()

     'purpose:   demonstrate code that does the following:
     '          1. creates a file
     '          3. moves the file to a different directory
     '          4. copies the file to another directory
     '          5. deletes the file
     'local scope
    Dim myFileStream As FileStream 
    Dim myFile As FileInfo 
    Dim myCopiedFile As FileInfo 

    'check if file exists, if so delete it 
     If File.Exists(path:="c:\newFile.txt") Then 
        File.Delete(path:="c:\newFile.txt") 
     End If 

     'create a new file using, set = to file stream object 
     myFileStream = File.Create(path:="c:\newFile.txt") 

     'close the file stream object 
      myFileStream.Close()
      
     'get the newly created file 
     myFile = New FileInfo(fileName:="c:\newFile.txt") 

     'check if move to file exists, if so delete it 
      If File.Exists(path:="c:\moveFile.txt") Then 
         File.Delete(path:="c:\moveFile.txt") 
      End If 

      'move the file 
      myFile.MoveTo(destFileName:="c:\moveFile.txt") 

      'check to see if directory exists, else create it 
      If Not System.IO.Directory.Exists(path:="C:\new directory 1") Then
         System.IO.Directory.CreateDirectory( _ 
             path:="C:\new directory 1") 
      End If 

      'copy the file back to the directory 
      'overwrite an existing file if need be 
      myCopiedFile = myFile.CopyTo( _ 
           destFileName:="C:\new directory 1\copyFile.txt", _ 
           overwrite:=True) 

      'wait for the user to stop the console application 
      'this allows you to view the directories and the results 
      Console.WriteLine("Enter 's' to stop the application.") 

      'loop until user presses s key 
      Do While Console.ReadLine <> "s" : Loop 

      'delete the files 
      myFile.Delete() 
      myCopiedFile.Delete() 

   End Sub 
End Module 

The level of access users are granted to a file is controlled by the FileAccess enumeration. The parameter is specified in many of the constructors of the File, FileInfo, and FileStream classes. Table 7.3 lists the members of the enumeration and a brief description of each.

TABLE 7.3 FileAccess Enumeration Members

Member

Description

Read

The Read member indicates that data can be read from a file.

ReadWrite

The ReadWrite member defines both read and write access to a given file.

Write

The Write member provides write access to a file.


The various attribute values for files and directories are controlled using the FileAttributes enumeration. It should be noted that not all members are applicable to both files and directories. Table 7.4 lists the members of the FileAttributes enumeration.

TABLE 7.4 FileAttributes Enumeration Members

Member

Description

Archive

The Archive member indicates a file's archive status. The archive status is often used to mark files for backup or removal.

Compressed

The Compressed member indicates that a file is compressed.

Directory

The Directory file attribute indicates that a file is actually a directory.

Encrypted

The Encrypted member indicates that a file is encrypted.

Hidden

The Hidden value indicates that the file is hidden, and thus not included in an ordinary directory listing.

Normal

The Normal value indicates the file has no other attributes set.

NotContentIndexed

The NotContentIndexed value indicates that the file will not be indexed by Windows' indexing service.

Offline

The Offline value indicates that the file's data is not readily accessible (offline).

ReadOnly

The ReadOnly member indicates that the file cannot be modified; it is for reading only.

ReparsePoint

The ReparsePoint member indicates that the file contains a block of user-defined data (reparse point).

SparsePoint

The SparsePoint member indicates that the file is a sparse file. Sparse files are large files whose data are mostly zero values.

System

The System member marks a file as being part of the operating system or used exclusively by the operating system.

Temporary

The Temporary value indicates that a file is temporary. Temporary files should be created and deleted by applications as they are needed and no longer needed.


To control whether a file is created, overwritten, opened, or some combination thereof, you use the FileMode enumeration. It is used in many of the constructors of the FileStream, File, FileInfo, and IsolatedStorageFileStream classes. Table 7.5 lists the members of the FileMode enumeration.

TABLE 7.5 FileMode Enumeration Members

Member

Description

Append

The Append parameter specifies that a file be opened or created, and its end searched (seek) out. FileMode.Append can only be used in conjunction with FileAccess.Write. Any attempt to read fails and throws an ArgumentException.

Create

The Create parameter specifies that Windows create a new file. If the file already exists, it will be overwritten. This requires FileIOPermissionAccess.Write and FileIOPermissionAccess.Append FileIOPermission.

CreateNew

The CreateNew parameter indicates that Windows should create a new file. This requires FileIOPermissionAccess.Read and FileIOPermissionAccess.Append FileIOPermission. If the file already exists, an IOException is thrown.

Open

The Open member indicates that Windows should open an existing file. This requires FileIOPermissionAccess.Read FileIOPermission.

OpenOrCreate

The OpenOrCreate member indicates that Windows should open a file if it exists; otherwise, a new file should be created. If the file is opened with FileAccess.Read, FileIOPermissionAccess.Read FileIOPermission is required. If file access is FileAccess.ReadWrite and the file exists, FileIOPermissionAccess.Write FileIOPermission is required. If file access is FileAccess.ReadWrite and the file does not exist, FileIOPermissionAccess.Append FileIOPermission is required in addition to Read and Write.

Truncate

The truncate member indicates that Windows should open an existing file and truncate its size to zero bytes. This requires FileIOPermissionAccess.Write FileIOPermission.


Use the FileShare enumeration to control whether two processes can access a given file simultaneously. For example, if a user opens a file marked FileShare.Read, other users can open the file for reading but cannot save or write to it. The FileShare enumeration is used in some of the constructors for the FileStream, File, FileInfo, and IsolatedStorageFileStream classes. Table 7.6 lists the FileShare enumeration members.

TABLE 7.6 FileShare Enumeration Members

Member

Description

None

The None value indicates that the file should not be shared in any way. All requests to open the file will fail until the file is closed.

Read

The Read member allows users to open a given file for reading only. Attempts to save the file (or write to it) but read-only processes will fail.

ReadWrite

The ReadWrite parameter indicates that a file can be opened for both reading and writing by multiple processes. Obviously this can cause problems because the last user to save has his changes applied.

Write

The Write parameter indicates that a file can be open for writing but not necessarily reading. This can be combined with Read to mimic the ReadWrite parameter.


Monitoring the File System

It seems there is always a need to monitor a directory for file drops or respond to file system events. For example, suppose your team needs to be notified whenever a documentation artifact for the project you're working on is created or modified. Wouldn't it be nice if you could write a simple monitoring service that responds to these events and notifies the team?

One of the most interesting objects in the namespace is the FileSystemWatcher class. This class can be easily configured to monitor events within directories. Before the file watcher class, Visual Basic programmers often had to implement a message queue application, monitor SMTP, or write a service which, at different intervals, queried a directory to check for changes. .NET provides the FileSystemWatcher as an easy-to-use class to solve these common programming dilemmas.

Identifying What to Monitor

The FileSystemWatcher can be used to monitor changes made to files and subdirectories of a specified parent directory. The component can be configured to work with the local file system, a directory on the local network, or a remote machine.

The FileSystemWatcher class allows you to

  • Watch files from either a Windows 2000 or an NT 4 install.

  • Watch all files, including those marked hidden.

The FileSystemWatcher does not allow you to

  • Watch a remote NT 4 computer from another NT 4 computer.

  • Work with CDs or DVDs because files on these devices are fixed and cannot change.

There are a number of properties of the FileSystemWatcher class that allow us to target the objects we wish to watch. The properties developers will use most often are Filter and Path. Path simply indicates the directory path to watch. Filter indicates the type of files to watch. The format for Filter uses Windows' standard wildcard notation to set its value. If you do not set the filter property, its default is *.* (star dot star), which indicates that you are monitoring all files in the directory. If you are trying to monitor only Microsoft Excel files, then you'd set the Filter property to *.xls. Table 7.7 lists additional properties and describes when you would implement their use.

TABLE 7.7 Configuration Properties of the FileSystemWatcher Class

Property

Scenario Description

Path

The Path property lets you indicate what directory to watch. You can indicate a path using standard directory path notation (c:\myDirectory) or in UNC format (\\serverName\directory\ name).

Filter

Set the Filter property to watch for changes made to a file or directory that are of a specific type.

Target

Use the Target property to watch for changes on only a file, only a directory, or both a file and a directory. By default, the Target property is set to watch for changes to both directory and file-level items.

IncludeSubDirectories

Set the IncludeSubDirectories property to True to monitor changes made to subdirectories that the root directory contains. The watcher will watch for the same changes in all directories.

ChangedFilter

Use the ChangedFilter property to watch for specific changes to a file or directory when handling the Changed event. Changes can apply to Attributes, LastAccess, LastWrite, Security, or Size.


Once you've decided what objects you are monitoring you'll need to indicate the events or actions to which you are listening. The changes that the FileSystemWatcher can monitor include changes in the directory's or file's properties, size, last write time, last access time, and security settings.

You use the NotifyFilters enumeration of the FileSystemWatcher class to specify changes for which to watch on files and directories. The members of this enumeration can be combined using BitOr (bitwise or comparisons) to watch for multiple kinds of changes. An event is raised when any change you are watching for is made. Table 7.8 lists the members of the NotifyFilters enumeration and provides a brief description of each.

TABLE 7.8 NotifyFilters Enumeration Members

Member

Description

Attributes

 

The Attributes member allows you to watch for changes made to the attributes of a file or directory.

CreationTime

 

The CreationTime member allows you to watch for changes made to the time the file or directory was created.

DirectoryName

 

The DirectoryName member allows you to watch for changes made to the name of the file or directory.

FileName

 

The FileName member allows you to watch for changes made to the name of a file.

LastAccess

 

The LastAccess member allows you to watch for changes made to the date the file or directory was last opened.

LastWrite

 

The LastWrite member allows you to watch for changes made to the date the file or directory had data written to it.

Security

 

The Security member allows you to watch for changes made to the security settings of the file or directory.

Size

 

The Size member allows you to watch for changes made to the size of the file or directory.


Responding to File System Events

At this point, we've indicated what we are watching and what events interest us; now we must hook up those events to our application. The FileSystemWatcher raises an event when files or directories are created, deleted, renamed, or otherwise changed. The events are raised and stored in a buffer before being passed to our instance of the FileSystemWatcher.

NOTE

You might notice that some common tasks such as file copy or move do not correspond directly to an event raised by the component. However, upon closer examination, you will notice that when a file is copied, the system raises a created event to the directory to which the file was copied. Similarly, when a file is moved, the system raises both a deleted event in the file's original directory and a created event in the file's new directory. These events serve in place of actual copy and move to events.

This buffer becomes very important in high-volume monitoring applications. It has the potential to receive a lot of events. Let's examine this further. Every change to a file in a directory raises a separate event. This sounds simple enough, but we have to be careful. For example, if we are monitoring a directory that contains 25 files and we reset the security settings on the directory, we will get 25 separate change events. Now if we write an application that renames those files and resets their security we'll get 50 events: one for each file for both change and rename.

All these events are stored in the FileSystemWatcher's internal buffer, which has a maximum size limit and can overflow. If this buffer overflows, the FileSystemWatcher will raise the InternalBufferOverflow event. Fortunately, the component allows us to increase this buffer size.

The default buffer size is set to 8KB. Microsoft indicates that this can track changes on approximately 160 files in a directory. You can reset the buffer size to better match your needs using the InternalBufferSize property. For best performance, this property should be set in increments of 4K (4096, 8192, 12288, and so on) because this corresponds to the operating system's (Windows 2000) default page size.

Increasing this internal buffer comes at a cost. The buffer uses non-paged memory that cannot be swapped to disk. Therefore, we need to keep the buffer size as small as possible. Strategies to limit the buffer's size include the NotifyFilter and IncludeSubDirectories properties to filter out those change notifications in which we have no interest. It should be noted that the Filter property actually has no effect on the buffer size since the filter is applied after the notifications are written to the buffer.

To actually connect to the FileSystemWatcher's events we add handlers in our code as we would with any other event. For example, to hook into the Changed event, we add code similar to the following:

AddHandler myWatcher.Changed, AddressOf watcher_OnChange 

This tells your application to intercept the Changed event and process through a custom event called watcher_onChange. The custom event need only have the correct function signature. Table 7.9 lists the events to which you can subscribe and their associated function signatures.

TABLE 7.9 FileSystemWatcher Events

Event

VB Handler and Function Signature

Changed

AddHandler myWatcher.Changed, AddressOf watcher_OnChange Sub watcher_OnChange(ByVal source As Object, _ByVal e As IO.FileSystemEventArgs)

Created

AddHandler myWatcher.Created, AddressOf watcher_OnCreate Sub watcher_OnCreate(ByVal source As Object, _ByVal e As IO.FileSystemEventArgs)

Deleted

AddHandler myWatcher.Deleted, AddressOf watcher_OnDelete Sub watcher_OnChange(ByVal source As Object, _ByVal e As IO.FileSystemEventArgs)

Error

AddHandler myWatcher.Error, AddressOf watcher_OnError Sub watcher_OnError(ByVal source As Object, _ByVal e As IO.ErrorEventHandler)

Renamed

AddHandler myWatcher.Renamed, AddressOf watcher_OnRename Sub watcher_OnRename(ByVal source As Object, _ByVal e As IO. RenamedEventHandler)


Once inside the event, we have access to a number of properties related to the event and event type. These properties come from the event arguments that are passed to us when the event is raised. They include things like the change type and the path and name to the file or directory.

The WatcherChangeTypes enumeration is used by events of the FileSystemWatcher class. The enumeration's members indicate to the event the type of change that occurred to a file or directory. Table 7.10 lists the members of the WatcherChangeTypes enumeration.

TABLE 7.10 WatcherChangeTypes Enumeration Members

Member

Description

All

The All member indicates that any of the creation, deletion, change, or renaming of a file or folder actions occurred.

Changed

The Changed member indicates that a change action occurred to a file or event. Changes can include: size, attributes, security, last write, and last access time.

Created

The Created member indicates that a file or folder was created.

Deleted

The Deleted member indicates that a file or folder was deleted.

Renamed

The Renamed member indicates that a file or folder was renamed.


Listing 7.4 provides a sample FileSystemWatcher application that serves to further illustrate these concepts. The code can be executed inside a console application (and downloaded from http://www.samspublishing.com). The application monitors a directory for changes to text files. When a change occurs, the user is notified with a simple call to Console.WriteLine from within the intercepted change event.

LISTING 7.4 FileSystemWatcher Directory Monitor

Imports System.IO 
Module Module1 

   Sub Main() 

       'Call directory() 
     Call watchDirectory(watchPath:="c:\watch") 

   End Sub 

   Sub watchDirectory(ByVal watchPath As String) 

       'purpose: watch a directory for changes to files 

       'local scope 
      Dim myWatcher As FileSystemWatcher 
      Dim stopValue As String 

       'check if directory exits, no = create 
     If Not Directory.Exists(path:=watchPath) Then 
        Directory.CreateDirectory(path:=watchPath) 
     End If 

       'instantiate a new system watcher object 
     myWatcher = New FileSystemWatcher() 

       'set the path of directory to watch 
     myWatcher.Path = watchPath 

       'tell the watcher object to watch only for text 
     files myWatcher.Filter = "*.txt" 

       'tell the watcher the type of changes to watch for 
     myWatcher.NotifyFilter = IO.NotifyFilters.DirectoryName _ 
        Or IO.NotifyFilters.LastAccess _ 
        Or IO.NotifyFilters.LastWrite _ 
        Or IO.NotifyFilters.FileName 

       'intercept the watcher events 
     AddHandler myWatcher.Changed, AddressOf watcher_OnChange 
     AddHandler myWatcher.Deleted, AddressOf watcher_OnChange 
     AddHandler myWatcher.Created, AddressOf watcher_OnChange 
     AddHandler myWatcher.Renamed, AddressOf watcher_OnRename 

       'tell the watcher to start watching 
     myWatcher.EnableRaisingEvents = True 

       'tell the user that watching is on 
     Console.WriteLine("Watching " & watchPath & "...") 

       'wait for the user to stop the console application 
     Console.WriteLine("Press 's' to stop the application.") 

       'loop until user presses s key 
     Do While Console.ReadLine <> "s" : Loop 

   End Sub 

   Sub watcher_OnChange(ByVal source As Object, _ 
     ByVal e As IO.FileSystemEventArgs) 

      'purpose:  respond to the changed notify event 
      '         (created, changed, deleted) 

      'indicate that a file is changed, created, or deleted 
     Console.WriteLine("File: " & e.FullPath & " " & e.ChangeType) 

   End Sub 
 
   Sub watcher_OnRename(ByVal source As Object, _ 
      ByVal e As IO.RenamedEventArgs) 

      'purpose:  respond to the renamed watcher notify event 

      'tell the user the file that was renamed 
     Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, _ 
         e.FullPath) 

   End Sub 
End Module 

Suggestions for Further Exploration

  • To read similar information about working with file I/O in .NET, read the MSDN chapter found at: Visual Studio .NET/.NET Framework/Programming with the .NET Framework/Working with I/O.

  • For additional information on .NET security issues (for user, file, directory, and code access), start with the System.Security namespace. Of course, you will also want to read Appendix D, ".NET Framework Base Data Types," which deals specifically with security in .NET.

  • You will want to check out the FileDialog class found in System.Windows.Forms. This class allows you to easily display a window's dialog that allows users to select a file. The class is the replacement of the old common dialog control.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020