Jan Schreuder on .Net

.Net code samples, experiences, observations

View my professional profile on LinkedIn

Recent Posts

Tags

News

  • Inappropriate comments will be deleted at my discretion.

    The information and code samples in this weblog is provided "AS IS" without warranty of any kind, either expressed or implied, including but not limited to the merchantability and/or fitness for a particular purpose.

Community

Email Notifications

Tool suppliers

Tools

General

Microsoft

Favorite blogs

Archives

How to: Scan directories using recursion

Introduction

I created a class that allows you to scan through a directory and it's subdirectories. The class uses recursion and events. The events allow the class to be unaware of whatever you want to do to the directory tree, so you will be able to build all sorts of functionality which requires you to scan (sub)directories. The class as it can be downloaded has the following features:

  • Public methods to start scanning a directory (and it's subdirectories) by providing a path as a string, or a DirectoryInfo object.
  • An event raised for each file found during the scan, providing file details in a FileInfo object.
  • An event raised when a scan enters or leaves a directory, providing directory details in a DirectoryInfo object.
  • Arguments in the events to notify the class to stop the scan.
  • Overridable methods which allow you to handle found files and/or directories without the events.
  • Wildcarch search pattern.

In this document I will only explain the basics of how I built this class. The combined code-snippets here will not reveal the complete implementation. For that I refer to the downloads and the XML documentation tags in the code.

Scanning the directories

To scan through the directories I first created a simple method which uses recursion to "walk" the tree. The basic class looks like this:

public class ScanDirectory
{
    public void WalkDirectory(string directory)
    {
        WalkDirectory(new DirectoryInfo(directory));
    }
 
    private void WalkDirectory(DirectoryInfo directory)
    {
        // Scan all files in the current path
        foreach (FileInfo file in directory.GetFiles())
        {
            // Do something with the current file.
        }
 
        DirectoryInfo [] subDirectories = directory.GetDirectories();
 
        // Scan the directories in the current directory and call this method 
        // again to go one level into the directory tree
        foreach (DirectoryInfo subDirectory in subDirectories)
        {
            WalkDirectory(subDirectory);
        }
    }
}

The public method WalkDirectory calls the private method WalkDirectory with a new instance of the DirectoryInfo object. The private method uses recursion to walk through all the levels of the directory tree.

At this point I have a very simple way of scanning all files in a directory tree. But I still needed a way to allow an action to take place for each file without having to implement it in this method. For that I created an event which is raised for each file found in during the scan. The event will have to send information about the current file to calling application. So first define the FileEventArgs object which will be sent to the calling application when the event is raised:

public class FileEventArgs : EventArgs
{
    internal FileEventArgs(FileInfo fileInfo)
    {
        if (fileInfo == null) throw new ArgumentNullException("fileInfo");
 
        // Get File information 
        _fileInfo = fileInfo;
    }
 
    private FileInfo _fileInfo;
 
    /// <summary>
    /// Gets the <see cref="FileInfo"/> object.
    /// </summary>
    /// <value>The info.</value>
    public FileInfo Info
    {
        get { return _fileInfo; }
    }
}

Now that this is done, the event can be implemented in the basic class. To do this, we first need to add a delegate to the code which is basicaly the prototype definition for the event.

/// <summary>
/// Definition for the FileEvent.
/// </summary>
public delegate void FileEventHandler(object sender, FileEventArgs e);         

Then we can define the a public event property to allow the calling application to connect an event procedure to this class:

/// <summary>
/// Event is raised for each file in a directory.
/// </summary>
public event FileEventHandler FileEvent; 

A method is implemented in this class to make sure the event can be raised safely. It checks if the calling application has connected an event to this class. If so, the event argument object is instantiated and the event is raised.

/// <summary>
/// Raises the file event.
/// </summary>
/// <param name="fileInfo"><see cref="FileInfo" /> object for the current file.</PARAM>
private void RaiseFileEvent(FileInfo fileInfo)
{
    // Only do something when the event has been declared.
    if (FileEvent != null)
    {
        // Create a new argument object for the file event.
        FileEventArgs args = new FileEventArgs(fileInfo);
 
        // Now raise the event.
        FileEvent(this, args);
    }
}

Last thing to do is raise the event when a file has been found. The loop in the WalkDirectory method that scans the files in a specific directory will now look like this:

// Scan all files in the current path
foreach (FileInfo file in directory.GetFiles())
{
    // Raise the event for the current file.
    RaiseFileEvent(file);
}

This is the basic code for the ScanDirectory class which is included in the download. The complete code, which is in the download, also features a DirectoryEvent, an appropriate EventArgs object for that event and properties in both event arguments to allow you to stop the scan. All methods and properties have XML comments.

Overridable methods

I introduced overridable methods for both the DirectoryEvent and the FileEvent after having a discussion about performance issues with the event driven model. I tested both scenarios and found overridable methods to be 50% faster then the events.

I introduced two overridable methods for this class. The first is called when the scan enters or leaves a directory:

/// <summary>
/// Processes the directory.
/// </summary>
/// <param name="directoryInfo">The directory info.</param>
/// <param name="action">The action.</param>
/// <returns><see langword="true"/> when the scan is allowed to continue. <see langword="false"/> if otherwise;</returns>
public virtual bool ProcessDirectory(DirectoryInfo directoryInfo, ScanDirectoryAction action)
{
    if (DirectoryEvent != null)
    {
        return RaiseDirectoryEvent(directoryInfo, action);
    } 
    return true;
}

The second overridable method is called for each file found in a directory during the scan:

/// <summary>
/// Processes the file.
/// </summary>
/// <param name="fileInfo">The file info.</param>
/// <returns><see langword="true"/> when the scan is allowed to continue. <see langword="false"/> if otherwise;</returns>
public virtual bool ProcessFile(FileInfo fileInfo)
{
    // Only do something when the event has been declared.
    if (FileEvent != null)
    {
        RaiseFileEvent(fileInfo);
    }
    return true;
}

As you can see, the overridable methods will call the default RaiseFileEvent and RaiseDirectoryEvent methods. By doing this, the class still supports the event driven model while introducing the option to inherit from the base class and "Roll your own" directory and file handling without events.

SearchPattern property

Last,. but not least, is the implementation of a SearchPattern property. You can specify a search pattern to the class. The class will make sure that only files that match that pattern are scanned and returned to your application. This works for the event driven model as well as for the overridable methods. It is possible to specify more than one pattern by separating each pattern with a semi-colon. The code for the SearchPattern property looks like this:

/// <summary>
/// Gets or sets the search pattern.
/// </summary>
/// <example>
/// You can specify more than one seach pattern
/// </example>
/// <value>The search pattern.</value>
public string SearchPattern
{
    get { return _searchPattern;  }
    set 
    {
        // When an empty value is specified, the search pattern will be the default (= *.*)
        if (value == null || value.Trim().Length == 0)
        {
            _searchPattern = "*.*";
        }
        else
        {
            _searchPattern = value; 
            // make sure the pattern does not end with a semi-colon
            _searchPattern = _searchPattern.TrimEnd(new char [] {';'});
        }
    }
}

The reason for trimming any trailing semi-colons will be evident when you look at the following code. This shows the final implementation of the method which is responsible for scanning all files in a directory:

/// <summary>
/// Walks the directory tree starting at the specified path.
/// </summary>
/// <param name="directory"><see cref="DirectoryInfo"/> object for the current path.</param>
/// <returns><see langword="true"/> when the scan was cancelled. <see langword="false"/> if otherwise;</returns>
private bool WalkFilesInDirectory(DirectoryInfo directory)
{
    bool continueScan = true;
 
    // Break up the search pattern in separate patterns
    string [] searchPatterns = _searchPattern.Split(';');
 
    // Try to find files for each search pattern
    foreach (string searchPattern in searchPatterns)
    {
        if (!continueScan)
        {
            break;
        }
        // Scan all files in the current path
        foreach (FileInfo file in directory.GetFiles(searchPattern))
        {
            if (!(continueScan = this.ProcessFile(file))) 
            {
                break;
            }
        }
    }
 
    return continueScan;
}

As you can see, the value for the SearchPattern property is split into an array of separate patterns. If the trailing semi-colons would still be in the SearchPattern, then the array would contain an empty pattern at the end. This would then result in a search that would never return any results, so this is a small optimization for that problem. The DirectoryInfo object will not throw an error when you attempt a call to GetFiles() with an empty search pattern. You just get an empty FileInfo collection

Using the code 

Using the event driven model

If, for example, you just want to list all the files that can be found in the Program Files directory, you can use the ScanDirectory class as follows:

// Create a new ScanDictory object
ScanDirectory scanDirectory = new ScanDirectory();
 
// Add a FileEvent to the class
scanDirectory.FileEvent += new ScanDirectory.FileEventHandler(scanDirectory_FileEvent);
 
scanDirectory.WalkDirectory("C:\\Program Files");  

The scanDirectory_FileEvent will then look like this:

/// <summary>
/// Handles the FileEvent event of the scanDirectory control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="temp.FileEventArgs"/> instance containing the event data.</param>
private void scanDirectory_FileEvent(object sender, FileEventArgs e)
{
    Console.WriteLine(e.Info.FullName);

Using overridable methods

The code below shows an example of a class that inherits from ScanDirectory to write the names of the files found to the console:

public class ShowInConsole : ScanDirectory
{
    /// <summary>
    /// Processes the file.
    /// </summary>
    /// <param name="fileInfo">The file info.</param>
    /// <returns>
    /// <see langword="true"/> when the scan is allowed to continue. <see langword="false"/> if otherwise;
    /// </returns>
    public override bool ProcessFile(FileInfo fileInfo)
    {
        Console.WriteLine(fileInfo.FullName);
        return true;
    }
}

You can download a demo project here. This project includes the class which contains the code you see in this post, and implementations for the event driven model.. The code is fully documented using XML tags. You're free to use it when and where you want. You can also find this information on CodeProject.com.

Comments

Dennis van der Stelt said:

Nice article mate! And great layout! Good job!
# May 31, 2006 1:39 AM

Ramon Smits said:

Hi Jan, this code isn't that usefull. You should really add a multi-wildcard search option to it for performance reason's. A really good implementation would be to make use of yield return to return a collection of FileInfo's.

But then it could be that you receive a securityexception when scanning a path recursively. You should catch that too and add an event handler for such scenarios to determine what to do.

Also add a return parameter that return information like total files, directories, filesize, etc.

Then it would be an interesting piece of reusable code ;-)

Hope you don't mind my comments...

If you have done that then it would look almost the same as the search class that I reuse all the time LOL.
# May 31, 2006 3:49 AM

Jan Schreuder said:

Ramon, I was already contemplating about adding the search option, so a new version is uploaded to include that functionality.

But I want to keep this class as lean as possible. You don't always need the properties you mention. But if you do, you can inherit from this base class and implement those features.

As for the security exception, my first version used the Directory.GetDirectories() and Directory.GetFiles() methods. And in that scenario, I did get the UnauthorizedAccessException. But by changing to the DirectoryInfo.GetDirectories() and DirectoryInfo.GetFiles() methods, I got rid of that exception. And these methods return a collection of DirectoryInfo and FileInfo objects. So there's your yield return ;-)

And no, I don't mind comments. My opinion on this is, that if nobody comments on your code you'll never be able to improve yourself. Nobody writes perfect code by default, but parts of my code are excellent ;-)

# June 1, 2006 2:48 AM

Jan Schreuder on .Net said:

In my previous post I explained how you could build a class that uses recursion to scan through directories....
# July 4, 2006 11:52 PM

Luis Lebron said:

First thanks for the excellent code. I am trying to convert the code to VB but ran into 2 functions I am having trouble "translating" correctly: the RaiseFileEvent and the RaiseDirectoryEvent functions.

/// Raises the file event.
/// </summary>
/// <param name="fileInfo"><see cref="FileInfo"/> object for the current file.</param>
private bool RaiseFileEvent(FileInfo fileInfo)
{
bool continueScan = true;

// Create a new argument object for the file event.
FileEventArgs args = new FileEventArgs(fileInfo);

// Now raise the event.
FileEvent(this, args);

continueScan = !args.Cancel;

return continueScan;
}

private bool RaiseDirectoryEvent(DirectoryInfo directory, ScanDirectoryAction action)
{
bool continueScan = true;

// Only do something when the event has been declared.
if (FileEvent != null)
{
// Create a new argument object for the file event.
DirectoryEventArgs args = new DirectoryEventArgs(directory, action);

// Now raise the event.
DirectoryEvent(this, args);

continueScan = !args.Cancel;
}
return continueScan;
}

# July 26, 2006 7:39 AM

Jan Schreuder said:

I'm not really a VB.Net developer, but my guess is that is should be something like this:
-------------------------------------
Public Delegate Sub FileEventHandler(ByVal sender As Object, ByVal e As FileEventArgs)

Public Event FileEvent As FileEventHandler

Private Function RaiseFileEvent(ByVal fileInfo As FileInfo) As Boolean
Dim continueScan As Boolean = True
Dim args As FileEventArgs = New FileEventArgs(fileInfo)
FileEvent(Me, args)
continueScan = Not args.Cancel
Return continueScan
End Function

Public Delegate Sub DirectoryEventHandler(ByVal sender As Object, ByVal e As DirectoryEventArgs)

Public Event DirectoryEvent As DirectoryEventHandler

Private Function RaiseDirectoryEvent(ByVal directory As DirectoryInfo, ByVal action As ScanDirectoryAction) As Boolean
Dim continueScan As Boolean = True
If Not (FileEvent Is Nothing) Then
Dim args As DirectoryEventArgs = New DirectoryEventArgs(directory, action)
DirectoryEvent(Me, args)
continueScan = Not args.Cancel
End If
Return continueScan
End Function

-------------------------------------

You could try and download SharpDevelop. It contains a tool to convert C# to VB.Net code (and vice versa), which sofar has helped me a lot when I needed to migrate existing C# code to VB.Net
# July 26, 2006 10:50 PM

llebron said:

Thanks for your help. The key to solving the problem was using
If Not FileEventEvent is Nothing

If Not DirectoryEventEvent is Nothing.

Btw, how do you deal with permission issues. When I try to go through my C:\ drive it alwasys stops at the System Volume Information folder with an access denied. I would like to skip the System folders and process the rest
# August 7, 2006 6:22 PM

Jan Schreuder said:

The easiest way to deal with that is to catch the UnauthorizedAccessException exception. If you specifically catch that exception you can allow the application to continue.
# August 7, 2006 11:21 PM

llebron said:

Thanks for all your help. I'm fairly new to .Net. Can you provide tell me on which function or subroutine I need to catch the UnauthorizedAccessException. Would it be the WalkDirectories and WalkFilesInDirectories functions?

thanks
# August 8, 2006 5:33 AM

Jan Schreuder said:

WalkFilesInDirectories is the most likely candidate. Both would be the safest option.
# August 8, 2006 12:14 PM

Jan Schreuder on .Net said:

A few weeks ago, Patrick Wellink blogged about how you would get a copy of a DLL when it's in the GAC....
# August 9, 2006 12:47 AM

John Brookes said:

This is a bit off-topic, but can you tell me how to monitor traffic on a computer, such as writing to a USB drive or CD?

-Just thought a man of your talents would have some idea.

Thanks in advance,

JB

# November 1, 2007 9:11 PM

Vinay Chandrashekar said:

I am new to c#.Could any one please tell me the procedure to skip System Volume Information folder while scanning for all folders in a particular drive.

I just read here u can do it by catching UnauthorizedAccessException.I am not sure what to write inside the catch block so that my application countinues working even if it encounters this exception.

# December 2, 2007 9:47 AM

Hari said:

Thanks for your excellent code and this is useful for me.But, I like to know how can I get the assembly with their version name instead of file name.I have more than one assembly with the same name but different versions in the GAC.I want to get a specific version needed.How can I do this?

# December 7, 2007 11:26 PM

Dave said:

I like the code and the event support.  Thanks!

1. I believe the search pattern solution will cause an individual file to be processed N times in the event that N of the search patterns match the file.

2. Does *.* search pattern behave the same as * with regard to extensionless files?

Cheers,

Dave

# June 19, 2009 3:57 AM

Dave said:

RaiseDirectoryEvent has some File/Directory inconsistency.

1. Should "if (FileEvent != null)" be "if (DirectoryEvent != null)"

2. Should "for the file event" be "for the directory event".

Cheers,

Dave

# June 19, 2009 4:42 AM

Dave said:

If you are interested, here is a revision of the code that...

1. fires events for UnauthorizedAccessException and continues scanning subsequent directories

2. fixes file/directory inconsistencies in RaiseDirectoryEvent()

  a. changed FileEvent to DirectoryEvent

  b. changed "for the file event" to "for the directory event"

3. initializes the _searchPattern = _patternAllFiles

(to avoid "System.NullReferenceException: Object reference not set to an instance of an object." in certain cases)

4. added a note about the search pattern logic fallout (redundant file processing)

Note:

The construction of the new try/catch blocks is critical (i.e., not bracketing more code than is necessary).  This ensures proper processing when 1. scanning directories only and 2. scanning directories and files.

namespace ScanDirectoryDemo

{

   /// <summary>

   /// Defines the action on a directory which triggered the event

   /// </summary>

   public enum ScanDirectoryAction

   {

       /// <summary>

       /// Enter a directory

       /// </summary>

       Enter,

       /// <summary>

       /// Leave a directory

       /// </summary>

       Leave

   }

   #region Event argument definition for ScanDirectory.FileEvent

   /// <summary>

   /// Information about the file in the current directory.

   /// </summary>

   public class FileEventArgs : EventArgs

   {

       #region Constructors

       /// <summary>

       /// Block the default constructor.

       /// </summary>

       private FileEventArgs() {    }

       /// <summary>

       /// Initializes a new instance of the <see cref="DirectoryEventArgs"/> class.

       /// </summary>

       /// <param name="fileInfo"><see cref="FileInfo"/> object for the current file.</param>

       internal FileEventArgs(FileInfo fileInfo)

       {

           if (fileInfo == null) throw new ArgumentNullException("fileInfo");

           // Get File information

           _fileInfo = fileInfo;

       }

       #endregion

       #region Properties

       private bool            _cancel;

       private FileInfo        _fileInfo;

       /// <summary>

       /// Gets the current file information.

       /// </summary>

       /// <value>The <see cref="FileInfo"/> object for the current file.</value>

       public FileInfo Info

       {

           get { return _fileInfo; }

       }

       /// <summary>

       /// Gets or sets a value indicating whether to cancel the directory scan.

       /// </summary>

       /// <value>

       /// <see langword="true"/> if the scan must be cancelled; otherwise, <see langword="false"/>.

       /// </value>

       public bool Cancel

       {

           get { return _cancel; }

           set { _cancel = value; }

       }

       #endregion

   }

   #endregion

   #region Event argument definition for ScanDirectory.DirectoryEvent

   /// <summary>

   /// Event arguments for the DirectoryEvent

   /// </summary>

   public class DirectoryEventArgs : EventArgs

   {

       #region Constructors

       /// <summary>

       /// Block the default constructor.

       /// </summary>

       private DirectoryEventArgs() {    }

       /// <summary>

       /// Initializes a new instance of the <see cref="DirectoryEventArgs"/> class.

       /// </summary>

       /// <param name="directory"><see cref="DirectoryInfo"/> object for the current path.</param>

       /// <param name="action">The action.</param>

       internal DirectoryEventArgs(DirectoryInfo directory, ScanDirectoryAction action)

       {

           if (directory == null) throw new ArgumentNullException("directory");

           // Get File information

           _directoryInfo = directory;

           _action = action;

       }

       #endregion

       #region Properties

       private DirectoryInfo        _directoryInfo;

       private ScanDirectoryAction    _action;

       private bool                _cancel;

       /// <summary>

       /// Gets the current directory information.

       /// </summary>

       /// <value>The <see cref="DirectoryInfo"/> object for the current directory.</value>

       public DirectoryInfo Info

       {

           get { return _directoryInfo; }

       }

       /// <summary>

       /// Gets the current directory action.

       /// </summary>

       /// <value>The <see cref="ScanDirectoryAction"/> action value.</value>

       public ScanDirectoryAction Action

       {

           get { return _action; }

       }

       /// <summary>

       /// Gets or sets a value indicating whether to cancel the directory scan.

       /// </summary>

       /// <value>

       /// <see langword="true"/> if the scan must be cancelled; otherwise, <see langword="false"/>.

       /// </value>

       public bool Cancel

       {

           get { return _cancel; }

           set { _cancel = value; }

       }

       #endregion

   }

   #endregion

   #region Event argument definition for ScanDirectory.UnauthorizedAccessExceptionEvent

   /// <summary>

   /// Event arguments for the UnauthorizedAccessExceptionEvent

   /// </summary>

   public class UnauthorizedAccessExceptionEventArgs : EventArgs

   {

       #region Constructors

       /// <summary>

       /// Block the default constructor.

       /// </summary>

       private UnauthorizedAccessExceptionEventArgs() { }

       /// <summary>

       /// Initializes a new instance of the <see cref="UnauthorizedAccessExceptionEventArgs"/> class.

       /// </summary>

       /// <param name="directory"><see cref="DirectoryInfo"/> object for the current path.</param>

       /// <param name="action">The action.</param>

       internal UnauthorizedAccessExceptionEventArgs(DirectoryInfo directory, UnauthorizedAccessException exception)

       {

           if (directory == null) throw new ArgumentNullException("directory");

           _directoryInfo = directory;

           _exception = exception;

       }

       #endregion

       #region Properties

       private DirectoryInfo _directoryInfo;

       private UnauthorizedAccessException _exception;

       private bool _cancel;

       /// <summary>

       /// Gets the current directory information.

       /// </summary>

       /// <value>The <see cref="DirectoryInfo"/> object for the current directory.</value>

       public DirectoryInfo Info

       {

           get { return _directoryInfo; }

       }

       /// <summary>

       /// Gets the current UnauthorizedAccessException.

       /// </summary>

       /// <value>The <see cref="UnauthorizedAccessException"/> exception value.</value>

       public UnauthorizedAccessException Exception

       {

           get { return _exception; }

       }

       /// <summary>

       /// Gets or sets a value indicating whether to cancel the directory scan.

       /// </summary>

       /// <value>

       /// <see langword="true"/> if the scan must be cancelled; otherwise, <see langword="false"/>.

       /// </value>

       public bool Cancel

       {

           get { return _cancel; }

           set { _cancel = value; }

       }

       #endregion

   }

   #endregion

   /// <summary>

   /// Scan directory trees

   /// </summary>

   public class ScanDirectory

   {

       private const string _patternAllFiles = "*.*";

       #region Handling of the FileEvent

       /// <summary>

       /// Definition for the FileEvent.

       ///    </summary>

       public delegate void FileEventHandler(object sender, FileEventArgs e);

       /// <summary>

       /// Event is raised for each file in a directory.

       /// </summary>

       public event FileEventHandler FileEvent;

       /// <summary>

       /// Raises the file event.

       /// </summary>

       /// <param name="fileInfo"><see cref="FileInfo"/> object for the current file.</param>

       private bool RaiseFileEvent(FileInfo fileInfo)

       {

           bool continueScan = true;

           // Create a new argument object for the file event.

           FileEventArgs args = new FileEventArgs(fileInfo);

           // Now raise the event.

           FileEvent(this, args);

           continueScan = !args.Cancel;

           return continueScan;

       }

       #endregion

       #region Handling of the DirectoryEvent

       /// <summary>

       /// Definition for the DirectoryEvent.

       /// </summary>

       public delegate void DirectoryEventHandler(object sender, DirectoryEventArgs e);

       /// <summary>

       /// Event is raised for each directory.

       /// </summary>

       public event DirectoryEventHandler DirectoryEvent;

       /// <summary>

       /// Raises the directory event.

       /// </summary>

       /// <param name="directory"><see cref="DirectoryInfo"/> object for the current path.</param>

       /// <param name="action">The <see cref="ScanDirectoryAction"/> action value.</param>

       /// <returns><see langword="true"/> when the scan is allowed to continue. <see langword="false"/> if otherwise;</returns>

       private bool RaiseDirectoryEvent(DirectoryInfo directory, ScanDirectoryAction action)

       {

           bool continueScan = true;

           // Only do something when the event has been declared.

           if (DirectoryEvent != null)

           {

               // Create a new argument object for the directory event.

               DirectoryEventArgs args = new DirectoryEventArgs(directory, action);

               // Now raise the event.

               DirectoryEvent(this, args);

               continueScan = !args.Cancel;

           }

           return continueScan;

       }

       #endregion

       #region Handling of the UnauthorizedAccessExceptionEvent

       /// <summary>

       /// Definition for the UnauthorizedAccessExceptionEvent.

       /// </summary>

       public delegate void UnauthorizedAccessExceptionEventHandler(object sender, UnauthorizedAccessExceptionEventArgs e);

       /// <summary>

       /// Event is raised for each UnauthorizedAccessException.

       /// </summary>

       public event UnauthorizedAccessExceptionEventHandler UnauthorizedAccessExceptionEvent;

       /// <summary>

       /// Raises the UnauthorizedAccessException event.

       /// </summary>

       /// <param name="directory"><see cref="DirectoryInfo"/> object for the current path.</param>

       /// <param name="exception">The <see cref="UnauthorizedAccessException"/> action value.</param>

       /// <returns><see langword="true"/> when the scan is allowed to continue. <see langword="false"/> if otherwise;</returns>

       private bool RaiseUnauthorizedAccessExceptionEvent(DirectoryInfo directory, UnauthorizedAccessException exception)

       {

           bool continueScan = true;

           // Only do something when the event has been declared.

           if (UnauthorizedAccessExceptionEvent != null)

           {

               // Create a new argument object for the UnauthorizedAccessException event.

               UnauthorizedAccessExceptionEventArgs args = new UnauthorizedAccessExceptionEventArgs(directory, exception);

               // Now raise the event.

               UnauthorizedAccessExceptionEvent(this, args);

               continueScan = !args.Cancel;

           }

           return continueScan;

       }

       #endregion

       #region Public methods

       /// <summary>

       /// Walks the specified path.

       /// </summary>

       /// <param name="path">The path.</param>

       /// <returns><see langword="true"/> when the scan finished without being interupted. <see langword="false"/> if otherwise;</returns>

       public bool WalkDirectory(string path)

       {

           // Validate path argument.

           if (path == null || path.Length == 0) throw new ArgumentNullException("path");

           return WalkDirectory(new DirectoryInfo(path));

       }

       /// <summary>

       /// Walks the specified directory.

       /// </summary>

       /// <param name="directory"><see cref="DirectoryInfo"/> object for the current path.</param>

       /// <returns><see langword="true"/> when the scan finished without being interupted. <see langword="false"/> if otherwise;</returns>

       public bool WalkDirectory(DirectoryInfo directory)

       {

           if (directory == null)

           {

               throw new ArgumentNullException("directory");

           }

           return WalkDirectories(directory);

       }

       #endregion

       #region Overridable methods

       /// <summary>

       /// Processes the directory.

       /// </summary>

       /// <param name="directoryInfo">The directory info.</param>

       /// <param name="action">The action.</param>

       /// <returns><see langword="true"/> when the scan is allowed to continue. <see langword="false"/> if otherwise;</returns>

       public virtual bool ProcessDirectory(DirectoryInfo directoryInfo, ScanDirectoryAction action)

       {

           if (DirectoryEvent != null)

           {

               return RaiseDirectoryEvent(directoryInfo, action);

           }    

           return true;

       }

       /// <summary>

       /// Processes the file.

       /// </summary>

       /// <param name="fileInfo">The file info.</param>

       /// <returns><see langword="true"/> when the scan is allowed to continue. <see langword="false"/> if otherwise;</returns>

       public virtual bool ProcessFile(FileInfo fileInfo)

       {

           // Only do something when the event has been declared.

           if (FileEvent != null)

           {

               RaiseFileEvent(fileInfo);

           }

           return true;

       }

       #endregion

       #region Private methods

       /// <summary>

       /// Walks the directory tree starting at the specified directory.

       /// </summary>

       /// <param name="directory"><see cref="DirectoryInfo"/> object for the current directory.</param>

       /// <returns><see langword="true"/> when the scan is allowed to continue. <see langword="false"/> if otherwise;</returns>

       private bool WalkDirectories(DirectoryInfo directory)

       {

           bool continueScan = true;

           if (continueScan = ProcessDirectory(directory, ScanDirectoryAction.Enter))

           {

               // Only scan the files in this path when a file event was specified

               if (this.FileEvent != null)

               {

                   continueScan = WalkFilesInDirectory(directory);

               }

               if (continueScan)

               {

                   DirectoryInfo[] subDirectories;

                   try

                   {

                       subDirectories = directory.GetDirectories();

                   }

                   catch (UnauthorizedAccessException e)

                   {

                       Console.WriteLine("UnauthorizedAccessException in " + directory.FullName + "("+e.Message+")");

                       subDirectories = null;

                   }

                   if (subDirectories != null)

                   {

                       foreach (DirectoryInfo subDirectory in subDirectories)

                       {

                           // It is possible that users create a recursive directory by mounting a drive

                           // into an existing directory on that same drive. If so, the attributes

                           // will have the ReparsePoint flag active. The directory is then skipped.

                           // See: blogs.msdn.com/.../332704.aspx

                           if ((subDirectory.Attributes & FileAttributes.ReparsePoint) != 0)

                           {

                               continue;

                           }

                           if (!(continueScan = WalkDirectory(subDirectory)))

                           {

                               break;

                           }

                       }

                   }

               }

               if (continueScan)

               {

                   continueScan = this.ProcessDirectory(directory, ScanDirectoryAction.Leave);

               }

           }

           return continueScan;

       }

       /// <summary>

       /// Walks the directory tree starting at the specified path.

       /// </summary>

       /// <param name="directory"><see cref="DirectoryInfo"/> object for the current path.</param>

       /// <returns><see langword="true"/> when the scan was cancelled. <see langword="false"/> if otherwise;</returns>

       private bool WalkFilesInDirectory(DirectoryInfo directory)

       {

           bool continueScan = true;

           // Break up the search pattern in separate patterns

           string [] searchPatterns = _searchPattern.Split(';');

           /**

            *  tempdt

            *    This search pattern strategy will cause files to be hit in order of

            *    the search pattern.  Also, is it possible we get dups when a file matches

            *    more than 1 of the patterns?

            */

           // Try to find files for each search pattern

           foreach (string searchPattern in searchPatterns)

           {

               if (!continueScan)

               {

                   break;

               }

               // Identify all files in the current path

               FileInfo[] files;

               try

               {

                   files = directory.GetFiles(searchPattern);

               }

               catch (UnauthorizedAccessException e)

               {

                   Console.WriteLine("UnauthorizedAccessException in " + directory.FullName + "(" + e.Message + ")");

                   files = null;

               }

               // Scan all files in the current path

               if (files != null)

               {

                   foreach (FileInfo file in files)

                   {

                       if (!(continueScan = this.ProcessFile(file)))

                       {

                           break;

                       }

                   }

               }

           }

           return continueScan;

       }

       #endregion

       #region Properties

       private string _searchPattern = _patternAllFiles;

       /// <summary>

       /// Gets or sets the search pattern.

       /// </summary>

       /// <example>

       /// You can specify more than one seach pattern

       /// </example>

       /// <value>The search pattern.</value>

       public string SearchPattern

       {

           get { return _searchPattern;  }

           set

           {

               // When an empty value is specified, the search pattern will be the default (= *.*)

               if (value == null || value.Trim().Length == 0)

               {

                   _searchPattern = _patternAllFiles;

               }

               else

               {

                   _searchPattern = value;

                   // make sure the pattern does not end with a semi-colon

                   _searchPattern = _searchPattern.TrimEnd(new char [] {';'});

               }

           }

       }

       #endregion

   }

}

# June 19, 2009 6:00 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Please add 7 and 2 and type the answer here: