A while back, I had a post that showed how you could check if a file was a .NET assembly. Well, around that time, I wrote a simple application to test this function. However, the way I did the test app is not ... how should I put it ... common. I decided to use threading to facilitate the searching of the assemblies under the netfx folder...the threading has a little bit of a twist. It uses a custom delegate (a class that maps to System.Delegate ... well, it's more like System.MulticastDelegate, but I'll let you read about it!) to do the dirty work of searching!
If you use the delegate keyword in C#, the compiler maps this to a MulticastDelegate and gives you access to two useful methods: BeginInvoke, EndInvoke. These two methods allow you to do asynchronous calls to the delegate. Read Asynchronous Programming Overview for more information on this subject.
Well, enough is enough. Here's the code that makes everything work like a charm! Follow it, and if you have any questions, please feel free to ask me. Enjoy!
using System;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Lozanotek.Examples
{
///
/// Summary description for Class1.
///
class SearchTester
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//Create the class
AssemblySearcher asmSearcher = new AssemblySearcher();
//Subscribe to the complete event
asmSearcher.SearchingComplete += new AssemblySearchResultEventHandler(DoSearchingComplete);
Console.WriteLine("Start Invoking search...");
//Invoke the method!
asmSearcher.PerformSearch(@"C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322");
Console.WriteLine("End Invoking search...");
//Don't close quite yet...wait until the method is done searching!
Console.ReadLine();
}
//This method does the printing of the assemblies that were found!
public static void DoSearchingComplete(AssemblySearchResultEventArgs args)
{
Console.WriteLine("Found the following assemblies under {0}:",args.PathSearched);
foreach(string asm in args.Assemblies)
Console.WriteLine("Assembly: {0}",asm);
Console.WriteLine("Search complete!");
}
}
//For subscribers only
public delegate void AssemblySearchResultEventHandler(AssemblySearchResultEventArgs resultArgs);
///
/// Summary description for AssemblySearcher.
///
public class AssemblySearcher
{
//For internal-class use only!!
private delegate void AssemblySearchEventHandler(AssemblySearchEventArgs searchArgs);
//Event used to inform subscribers of search completion
public event AssemblySearchResultEventHandler SearchingComplete;
//Used by IsAssembly method
private const int COR_E_ASSEMBLYEXPECTED = -2147024885;
//This is the work-horse behind our class, however, it's called asynchronously!
private void SearchForAssemblies(AssemblySearchEventArgs searchArgs)
{
AssemblySearchResultEventArgs resultArgs = new AssemblySearchResultEventArgs(searchArgs.SearchPath);
foreach(string ext in searchArgs.Extensions)
{
string[] files = Directory.GetFiles(searchArgs.SearchPath,ext);
foreach(string file in files)
{
if(IsAssembly(file))
resultArgs.Assemblies.Add(file);
}
}
//Inform those who care about the end of the search
if(SearchingComplete != null)
SearchingComplete.BeginInvoke(resultArgs,null,null);
}
//Helper method, used in previous post
private bool IsAssembly(string asmFile)
{
bool isAsmbly = true;
try
{
AssemblyName.GetAssemblyName(asmFile);
}
catch(BadImageFormatException imageEx)
{
int hrResult = Marshal.GetHRForException(imageEx);
isAsmbly = (hrResult != COR_E_ASSEMBLYEXPECTED);
}
return isAsmbly;
}
//Methd that a 'client' can call
public void PerformSearch(string path)
{
AssemblySearchEventArgs args = new AssemblySearchEventArgs(path);
AssemblySearchEventHandler searchWorker = new AssemblySearchEventHandler(this.SearchForAssemblies);
//Since we do the begin invoke, we spawn a new thread for the execution
searchWorker.BeginInvoke(args,null,null);
}
}
///
/// Summary description for AssemblySearchEventArgs.
///
public class AssemblySearchEventArgs : EventArgs
{
private StringCollection _fileExtensions;
private string _pathToSearch;
public AssemblySearchEventArgs()
{
_fileExtensions = new StringCollection();
AddExtension("*.dll");
AddExtension("*.exe");
}
public AssemblySearchEventArgs(string path): this()
{
SearchPath = path;
}
public StringCollection Extensions
{
get
{
return _fileExtensions;
}
}
public void AddExtension(string extension)
{
_fileExtensions.Add(extension);
}
public string SearchPath
{
get
{
return _pathToSearch;
}
set
{
_pathToSearch = value;
}
}
}
///
/// Summary description for AssemblySearchResultEventArgs.
///
public class AssemblySearchResultEventArgs : EventArgs
{
private StringCollection foundAssemblies;
private string searchedPath;
public AssemblySearchResultEventArgs()
{
foundAssemblies = new StringCollection();
}
public AssemblySearchResultEventArgs(string path) : this()
{
PathSearched = path;
}
public StringCollection Assemblies
{
get
{
return foundAssemblies;
}
}
public string PathSearched
{
get
{
return searchedPath;
}
set
{
searchedPath = value;
}
}
}
}