![]() |
Desktop Development »
Files and Folders »
File System
Intermediate
License: The Code Project Open License (CPOL)
Processing files within a file structure with plug-ins and eventsBy Alexandru GhiondeaPresents an application used to process files within a file structure using event handling and plug-ins. |
C#, Windows, .NET 1.1VS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
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.
I attached to this article a number of four files.
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 class that implements the directory processing is called DirParser. The UML diagram that represents this class is shown below:

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.
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):
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 interface that is used to handle the events is called IPlugin. The UML diagram that represents this interface is shown below:

The interface consists of:
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). ProcessFile that will contain the actual implementation of the event handler. We will use this method to handle the FoundDir event.
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 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.
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:
/// <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.
This does the same thing as the other plug-in except that it makes them all upper case.
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.
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:

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.
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:
///<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.
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:
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!
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.
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!
| You must Sign In to use this message board. | |||||||||||||||
|
|||||||||||||||
|
|||||||||||||||
|
|||||||||||||||
|
|||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 5 Mar 2005 Editor: Sumalatha K.R. |
Copyright 2005 by Alexandru Ghiondea Everything else Copyright © CodeProject, 1999-2009 Web12 | Advertise on the Code Project |