Sunday, April 19, 2009

My first naive look at MS Cci – inspect your managed dll versions

As there have been few announcements already on MS Common Compiler Infrastructure going open source on Codeplex, I thought it could be fun to give it a first naive try for one of my current needs:

- Create an inventory of assembly versions deployed in production/test

I started with downloading the sources and looking around:

image I was quite focused on the ability to read the assembly so PeReader and PEReaderTests were most principal for my needs.
In particular ModuleReaderTest was the most interesting to study.
I had to get xunit from the codeplex and put it to the correct relative location. Other than that no extra steps were required to get the code to compile.
After really short while, I got to a very concise code. First a HostEnvironment, that as I understood at the first glance is a context and “cache” resolver for your module/assembly read operations:

using System;
using Microsoft.Cci;
 
namespace Tools.Net.Cci.Samples
{
    internal class HostEnvironment : MetadataReaderHost
    {
        PeReader peReader;
        internal HostEnvironment()
        {
            this.peReader = new PeReader(this);
        }
        public override IUnit LoadUnitFrom(string location)
        {
            return this.peReader.OpenModule(BinaryDocument.GetBinaryDocumentForFile(location, this));
 
        }
    }
}

And secondly the utility itself:

using System;
using System.IO;
 
using Microsoft.Cci;
 
namespace Tools.Net.Cci.Samples
{
    class Program
    {
        static HostEnvironment env;
 
        static void Main(string[] args)
        {
            if (args == null || args.Length != 2)
            {
                Console.WriteLine("Usage: assemblyversion path searchPattern");
                Console.WriteLine("Usage: assemblyversion \"c:\\product\" *.dll");
            }
            else
            {
                env = new HostEnvironment();
 
                AnalyzeDir(args[0], args[1]);
            }
        }
 
        private static void AnalyzeDir(string path, string searchPattern)
        {
            if (Directory.Exists(path))
            {
                string[] files = Directory.GetFiles(path, searchPattern, SearchOption.AllDirectories);
 
                foreach (string file in files)
                {
                    try
                    {
                        IAssembly assembly =
                            (IAssembly)env.LoadUnitFrom(file);
 
                        Console.WriteLine(
                                String.Format("{0}|{1}|{2}|{3}",
                                    assembly.AssemblyIdentity.Location,
                                    assembly.AssemblyIdentity.Name,
                                    assembly.AssemblyIdentity.Version));
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(
                            String.Format("{0}|{1}|{2}|{3}",
                                    file,
                                    Path.GetFileName(file),
                                    "N/A",
                                    ex.Message));
                    }
                }
            }
        }
    }
}

For the unmanaged dlls call to the LoadUnit was resulting in the exception, so excuse my naive try/catch for this, subject to study more soon.
Definitely Cci represents a good competitor to Mono.Cecil, and you can read a Jb Evain’s opinion on it and Cecil here: http://evain.net/blog/articles/2009/04/18/cecil-and-the-cci
Another good entry on Cci/Cecil vs System.Reflection is from who else than Patrick Smacchia: http://codebetter.com/blogs/patricksmacchia/archive/2008/03/18/mono-cecil-vs-system-reflection.aspx

My next point of naive interest will be to see how CCI handles (if at all) pseudo attributes.

You can get the complete solution here: http://code.google.com/p/toolsdotnet/source/browse/trunk/Tools.Net/spikes/Cci/Cci.sln
And utility itself can be downloaded from here: http://cid-c651f6a9f36fb87d.skydrive.live.com/self.aspx/Public/assemblyversion.exe

Here is the sample usage of the utility:

assemblyversion.exe "C:\dev\svn\Tools.Net\spikes\Cci\Tools.Cci.Samples\bin\Debug" *.dll

and output:

C:\dev\svn\Tools.Net\spikes\Cci\Tools.Cci.Samples\bin\Debug\Microsoft.Cci.MetadataHelper.dll|Microsoft.Cci.MetadataHelper|0.0.0.2|
C:\dev\svn\Tools.Net\spikes\Cci\Tools.Cci.Samples\bin\Debug\Microsoft.Cci.MetadataModel.dll|Microsoft.Cci.MetadataModel|0.0.0.2|
C:\dev\svn\Tools.Net\spikes\Cci\Tools.Cci.Samples\bin\Debug\Microsoft.Cci.PeReader.dll|Microsoft.Cci.PeReader|0.0.0.2|

Very raw, but subject to improve…

1 comment:

Petr Lazecky said...

I appologize my ignorance... But why you use big guns like Cecil for something like that? It seems to me that Reflection will be simple solution to the problem. Was this some exploration or is this something more behind your decision?

Just curious :-)