Thursday, September 30, 2004

One of the gotchas of the Xceed FileSystem is its behaviour when it can't process a file a folder: it throws an exception right away. Call it lazy by default! Take this code for example:

AbstractFolder folder = new DiskFolder( @"c:\" );
AbstractFile[] gdiFiles = folder.GetFiles( true, "gdiplus.dll" );

foreach( AbstractFile file in gdiFiles )
{
  Console.WriteLine( "{0}: {1}", 
    file.LastWriteDateTime.ToShortDateString(), file.FullName );
}

You would expect this to list all gdiplus.dll files found on your C: drive. On most computers, it will throw an UnauthorizedAccessException, because the currently logged in user probably does not have read access to all folders, like "System Volume Information" or other users' profiles under "Documents and Settings".

Does that mean you're doomed? No. Every time a method that processes many items encounters a problem with an item, it will raise the ItemException event. If you handle this event, you have access to the current and target item (if applies) and the exception that is about to be thrown. More important, you can instruct the sender to try again on the same item, skip the faulty item and continue with the next one, or abort the current operation and throw the exception (default).

Before we take a closer look, you must understand how events work with the FileSystem. Since all files and folders are represented by their own instance, it would be unthinkable to advise for events on each and every one, or have delegates propagated on every child instance a folder creates for its processing.

Instead, every method that can raise events exposes pairs of overloads, one taking a FileSystemEvents and object as its first two parameters. You create yourself a single FileSystemEvents instance (or ZipEvents), advise for events you wish to handle on it, then pass it to every method which can accept one. The second object parameter can be anything you wish, and is passed back to the event handler. In French, we call this a "fourre-tout" parameter. Babelfish translates this to "hold-all". The library calls it "user data".

The above code would now look like this:

FileSystemEvents events = new FileSystemEvents();
events.ItemException += new ItemExceptionEventHandler( FileSystemEvents_ItemException );

AbstractFolder folder = new DiskFolder( @"c:\" );
AbstractFile[] gdiFiles = folder.GetFiles( events, null, true, "gdiplus.dll" );

foreach( AbstractFile file in gdiFiles )
{
  Console.WriteLine( "{0}: {1}", 
    file.LastWriteDateTime.ToShortDateString(), file.FullName );
}

With the event handler doing the simplest thing:

private static void FileSystemEvents_ItemException( object sender, ItemExceptionEventArgs e )
{
  System.Diagnostics.Debug.WriteLine( e.Exception.Message );
  e.Action = ItemExceptionAction.Ignore;
}

Good. This should work. You run it. You see a blank screen. Nothing seems to be running. Sure, you ear your hard drive rumbling, but your application looks dead. That's bad. The ScanningFolder, ItemProgression and ByteProgression events come to the rescue. The first one is fired everytime the contents of a folder is retrieved. The second one is fired when an item is about to be copied or moved. The last one is fired every 64k of copied or moved data between files. In our case, we will advise for the ScanningFolder event, by adding this line:

events.ScanningFolder += new ScanningFolderEventHandler( FileSystemEvents_ScanningFolder );

And implementing this event handler:

private static char[] mg_animationChars = new char[] { '|', '/', '-', '\\' };
private static int mg_animationIndex = 0;

private static void FileSystemEvents_ScanningFolder( object sender, ScanningFolderEventArgs e )
{
  Console.Write( mg_animationChars[ mg_animationIndex++ ] );
  Console.Write( '\b' );
  mg_animationIndex %= mg_animationChars.Length;
}

Wow! DOS-style animation, my favourites! (Hey, it was my school). For the remaining events, it's yours to discover. Feel free to post your questions on our forums. Though we do not guarantee a fast answer, I personally try to monitor my own products' groups as often as possible.



9/30/2004 12:28:13 PM (Eastern Daylight Time, UTC-04:00)  #