Click here to Skip to main content
15,886,806 members
Articles / Programming Languages / C#
Article

Processing files within a file structure with plug-ins and events

Rate me:
Please Sign up or sign in to vote.
4.82/5 (12 votes)
5 Mar 2005CPOL8 min read 41.4K   1.1K   57   2
Presents an application used to process files within a file structure using event handling and plug-ins.

Introduction

In this article, I would like to present to you a small application I wrote. This application uses a class that runs through the files and folders of a directory structure and for each file and folder found, triggers an event that can be handled by a plug-in.

A little background to explain the reason for writing this application. A few weeks ago, I had to do some processing work on a bunch of files. The work consisted in some complicated work that could be done more easily by code, not by scripts. The idea was to reuse the code I wrote to process those files for further use.

So, I decided to write a class to process a structure of directories and to announce me when it finds a file or a folder. I would also write a bunch of plug-ins for each kind of work I had to perform. This way I could reuse my code and build additional plug-ins very quickly.

What does the files contain?

I attached to this article a number of four files.

  • Source files: the source files to the entire project.
  • Demo project: only the application discussed here (strong named by me).
  • The interface: the interface needed to build your own plug-ins (strong named by me).
  • The SDK: the class that does the processing work (not the plug-in handling) (strong named by me).

The idea

The idea to process directories came from this great article that I read sometime ago. I decided to “take it to the next level” and to design my own class and add some events to the class that handled the file and directory discovery.

I also wanted to be able to write new plug-ins very quickly and to be able to do that without having to recompile the entire application. The answer to this approach is to create an application that will use plug-ins, and these plug-ins will do the processing on the files within the directory structure.

The solution is divided into four projects (three Class libraries and one Windows application):

  • The SDK project is used to create the directory processing functions. This class will expose some events for finding files and directories.
  • The IPlugin project is the project that defines the plug-in interface that needs to be implemented.
  • The Plugins project is the project used to implement three plug-ins.
  • The TestApp project is the application that combines all of these projects into a working application.

Directory processing

The class

The class that implements the directory processing is called DirParser. The UML diagram that represents this class is shown below:

DirParser UML Diagram

As you can see, this class exposes two methods and two events. Let's take a closer look.

The methods:

  • ProcessDir(string RootDir): This methods initiates the processing of the directory specified in RootDir.
  • Abort(): This method aborts the processing of the current directory structure.

The events:

  • FoundFile(): This event is triggered when a file is found.
  • FoundDir(): This event is triggered when a directory is found.

As you can see from the diagram, the two events use two different delegates. This is done because the parameters for the two events need to be different.

The implementation

So, the class design is quite simple and so is the code. The code to process the directory is very similar to recursive back-tracking. The pseudo code of the algorithm used is this:

For the current folder:
1. Get the list of files in the directory
2. For each file
   2.1. Trigger the FoundFile event
3. Get the list of directories in the current dir
4. For each of the directories
   4.1. Trigger the FoundDir event
   4.2. Call this function

The C# code looks like this (stripped for visibility):

C#
FileInfo[] files = CurrentDir.GetFiles();
// Processes the files in the current directory
foreach(FileInfo f in files)
    FoundFile(f);
files = null;
// Processes the subdirs in the current directory
DirectoryInfo[] subDirs = CurrentDir.GetDirectories();
foreach(DirectoryInfo dir in subDirs)
{
    // Announces the directory
    FoundDir(dir);
    processDir(dir);
}

For more information please read the article written by Andrew Boisen.

I know that, by now, you might think: “Ok, but that’s a piece of cake”. Yes, you are right. This part is very easy to build. However, the true power of what I did was to take these events and to produce an interface that will handle them.

In doing so, I wanted to be able to extend the functionality of my application by simply implementing the given interface and so to create a plug-in. More on that in the next section.

The plug-in interface

The interface

The interface that is used to handle the events is called IPlugin. The UML diagram that represents this interface is shown below:

IPlugin UML Diagram

The interface consists of:

  • One event WriteOutput which is used by the plug-in to send results to the outside world. (in our case, these events will be captured by the test application).
  • A function ProcessFile that will contain the actual implementation of the event handler. We will use this method to handle the FoundDir event.
  • A function Description which is used to provide a small description of the plug-in that was created. This small function is used within the test application.

By itself, this interface is quite useless. However, when implemented, it will be of immense help.

The implementations

The implementation of the interface allows me to write small scripts to handle the FoundFile event. I only discussed FoundFile event simply because I didn’t need support for directory processing. However, this type of handling may appear in a later version, depending on your feedback.

Another thing I must point out is that you have to overload the ToString() so that your plug-in will return the name you give it.

Let’s now briefly present the plug-in that I wrote.

The ToLowerCase plug-in

It simply converts all file names into lower case (A -> a). This is simply a teaching example that might not have any actual/real use.

The code that does this is this:

C#
/// <SUMMARY>
/// This function is called when a file is found
/// </SUMMARY>
/// The file name that was just found
public void ProcessFile(FileInfo file)
{
    if (WriteOutput!=null)
        WriteOutput(this,"Renaming file "+file.FullName);
    try
    {
        System.IO.File.Move(file.FullName,file.FullName.ToLower());       
    }
    catch{}
}

Believe that this code is so trivial that I won't insist on it.

The ToUpperCase plugin

This does the same thing as the other plug-in except that it makes them all upper case.

The MP3 get detailed information plug-in

This plug-in retrieves information about MP3 files. How it does, will not be covered by this article. The source code includes comments so that you can figure out for yourself what it does. If you can’t, ask me.

The one important thing to remember about this plug-in is that it creates an Excel file that contains all the information. The file is created in the same folder as the application and it is called mp3info.csv.

Test Application or SDK in use

Ok. Now that you have some idea on how the little pieces work, let’s look at the big picture.

I have written a working application that uses both the SDK and the plug-ins. A screen shot of the application is here:

TestApp screenshot

The application allows you to select a root directory and to add or remove the plug-in to run on the files in that structure. The output from the plug-ins is displayed in a ListView control.

Loading the plug-ins

When the application starts, it will load all the plug-ins from the configuration file (the .config). We assume that all the plug-ins are located in the same directory as the application. The file looks like this, and each new plug-in should add an entry to this file:

The .config file:

configuration
  appSettings
    add key="Plugin_NamesToUpper" 
             value="Plugin.dll, Plugins.ProcessNamesToUpperCase"/add
    add key="Plugin_NamesToLower" 
             value="Plugin.dll, Plugins.ProcessNamesToLowerCase"/add
    add key="Plugin_Mp3GetInfo" 
             value="Plugin.dll, Plugins.ProcessMp3FileGetInfo"/add
  /appSettings
/configuration

This is actually an XML document. Each key will register a plug-in. The key attribute constitutes the name of the plug-in and the value attribute constitutes the assembly DLL and the strong name of the class. This information is used by the Activator class to load the plug-in. The code that does this is displayed below:

C#
///<SUMMARY>
///Loads the plug-in from the configuration file.
///</SUMMARY>
private void LoadPlugins()
{
  char[] sep = new char[]{','};
  foreach(string s in ConfigurationSettings.AppSettings.AllKeys)
  {
    if (s.StartsWith("Plugin"))
    {
      string[] temp = ConfigurationSettings.AppSettings.GetValues(s);
      string[] var = temp[0].Split(sep);
      System.Runtime.Remoting.ObjectHandle obj = null;
      try
      {
         //
         // We try to create an instance of an object that contains an 
         // implementation of the IPlugin interface.
         // The object to load is specified in the app.config file.
         //
      obj = Activator.CreateInstanceFrom(Application.StartupPath + "\\"+ 
                                                 var[0].Trim(),var[1].Trim());
        Plugin.IPlugin plug = (Plugin.IPlugin)obj.Unwrap();
      AvailableHandlers.Items.Add(plug);
    }
    catch (Exception e)
    {
      MessageBox.Show (this,e.Message,"Error");
    }
     }
  }
}

Basically, this code will process through each entry in the configuration file and for each entry that starts with the word plug-in will create an object of the type specified in the configuration file, and load it into the AvailableHandlers list.

Registering plug-ins to handle events

When the user selects a specific handler to be used in order to process the file within a file structure, the list item that is the plug-in will be moved to the SelectedHandlers list.

When the user clicks the Process button, the handlers that are contained within the SelectedHandlers list will be registered to the FileFound event of the class that processes the files. The code is shown below:

C#
private void ProcessDir_Click(object sender, System.EventArgs e)
{
  Dir = new SDK.DirParser();
  //
  // We catch these events in order to show them in the GUI
  //
  Dir.FoundFile+=new SDK.AnnounceFile(Dir_FoundFile);
  Dir.FoundDir+=new SDK.AnnounceDirectory(Dir_FoundDir);
  //
  // Adds the handlers to the class that actually runs through the directories
  //
  foreach(object k in SelectedHandlers.Items)
  {
    Plugin.IPlugin plg = (Plugin.IPlugin)k;
   
    //
    // For each plug-in, we register a method to handle the information
    // coming from the plug-in
    //
    plg.WriteOutput+=OutputWriter;
    //
    // We add each handler to the class that processes the directories
    //
    Dir.FoundFile+=new SDK.AnnounceFile(plg.ProcessFile);
  }
  Dir.ProcessDir(Dir2Process.Text);
  //
  // Removes the handle for the messages from the plug-in. This is done
  // because we do not need it anymore.
  //
  foreach(object k in SelectedHandlers.Items)
  {
    Plugin.IPlugin plg = (Plugin.IPlugin)k;
    plg.WriteOutput-=OutputWriter;
  }
  Dir=null;
  MessageBox.Show(this,"Done.");
  CurrentDir.Text = "";
  CurrentFile.Text = "";
}

And these are the key points in the application. If you want to know more about it, please look into the source files, or e-mail me!

Future extensions

Well, for further extensions I was thinking about implementing an interface that handles the DirectoryFound event as well. Also, I was thinking about writing some new plug-ins, but with real effect, not just to show how they can be done.

Sum up and Feedback

I would like to sum up and tell you what this article is all about. It shows an application capable of running custom methods on files within a directory structure. The custom methods are encapsulated within plug-ins and each plug-in implements the IPlugin interface. By doing so, you will end up having a lot of useful plug-ins for different types of files that will accomplish custom actions against your files.

At least this was the desired functionality of this application. I hope that you find this article useful.

Special thanks to my girlfriend Maria L. and my good friend Monica V.

Happy coding!

Bibliography

  1. For information about creating a plug-in framework click here.
  2. For information about recursing through files click here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Microsoft
United States United States
I am working on the C# compiler at Microsoft since 2007.

Microsoft Certified Professional since 2006

Interests: C#, ASP.NET, LINQ

Comments and Discussions

 
QuestionUpdate? Pin
Micu Radu13-Oct-05 1:29
Micu Radu13-Oct-05 1:29 
AnswerRe: Update? Pin
Alexandru Ghiondea18-Apr-08 20:16
Alexandru Ghiondea18-Apr-08 20:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.