Einar Egilsson

Module initializers in C#

Posted: Last updated:

One feature of the CLR that is not available in C# or VB.NET are module initializers (or module constructors). A module initializer is simply a global function which is named .cctor and marked with the attributes SpecialName and RTSpecialName. It is run when a module (each .NET assembly is comprised of one or more modules, typically just one) is loaded for the first time, and is guaranteed to run before any other code in the module runs, before any type initializers, static constructors or any other initialization code. I wanted to use this feature for a project I was doing but was unable to use it directly in C# so I created my own solution.

Now, why did I need this? Well, I was loading my other assemblies from an unusual place and I wanted to subscribe to the AppDomain.AssemblyResolve event before any types in my assembly were initialized. This feature is clearly not something you're going to need every day, (actually I can't think of a single other use case where I'd need it) but if you do, here's how I solved it.

I used the excellent Mono.Cecil library to create a small program that injects a module initializer into an existing assembly. Cecil is a fantastic library that makes it incredibly easy to manipulate assemblies and can do pretty much anything that you could possibly want to do with IL code. Using it I wrote a small program that simply takes in an assembly filename and the name of a parameterless static void method that exists in the assembly, and injects a module initializer that does nothing but call that method. The original code I wrote, the interesting part without all the error checking and stuff, is shown below:

string assemblyName = "Test.dll"; string typeName = "Foo.Bar.ModuleInit"; string methodName = "Run"; AssemblyDefinition assembly = AssemblyFactory.GetAssembly (assemblyName); TypeReference voidRef = assembly.MainModule.Import(typeof(void)); var attributes = MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; var cctor = new MethodDefinition( ".cctor", attributes, voidRef); TypeDefinition type = assembly.MainModule.Types[typeName]; MethodReference methodRef = type.Methods.GetMethod(methodName,new Type[]{}); cctor.Body.CilWorker.Append(cctor.Body.CilWorker.Create(OpCodes.Call, methodRef)); cctor.Body.CilWorker.Append(cctor.Body.CilWorker.Create(OpCodes.Ret)); assembly.MainModule.Inject(cctor, assembly.MainModule.Types["<Module>"]); AssemblyFactory.SaveAssembly(assembly, assemblyName);

I then added all the neccessary error handling, unit testing etc. and the resulting program can be downloaded here. The program is just a single executable since I embedded the Cecil assemblies as resources and load them by handling the AppDomain.Current.AssemblyResolve event. You can run it from the command line and give it the filename of your assembly and optionally specify the method to call and a key file to strong name sign the assembly after the injection. If no method is explicitly specified then the program looks for a type named ModuleInitializer (may be in any namespace) and looks for a method named Run in that type and calls that method in the module initializer.

Run without specifying the method, looks for ModuleInitializer::Run in any namespace.

InjectModuleInitializer.exe Test.dll

Run and specify the method as Foo.Bar.SomeClass::SomeMethod

InjectModuleInitializer.exe /m:Foo.Bar.SomeClass::SomeMethod Test.dll

Run and specify a keyfile to use for strong name signing

InjectModuleInitializer.exe /k:mykeyfile.snk Test.dll

Realistically though, if you want to do this you probably want to do it right after building your assembly.  The best way to do that is to use the <Exec/> command in MSBuild in the AfterBuild target. To do that you can have the following in your .csproj file:

<Target Name="AfterBuild"> <Exec Command='InjectModuleInitializer.exe "$(TargetPath)"' /> </Target>

Or, if you want to explicitly specify the method to run:

<Target Name="AfterBuild"> <Exec Command='InjectModuleInitializer.exe /m:MyNameSpace.MyClassName::MyMethodName "$(TargetPath)"' /> </Target>

If you have set up your project to sign your assembly you will have the name of the strong name file in the $(AssemblyOriginatorKeyFile) variable. You can sign the assembly after the injection with:

<Target Name="AfterBuild"> <Exec Command="InjectModuleInitializer.exe /k:"$(AssemblyOriginatorKeyFile)" /m:MyNameSpace.MyClassName::MyMethodName "$(TargetPath)" /> </Target>

And there you have it. You can download the console program or get the source on GitHub and build it yourself (You can download a zip file there if you don't want to mess with Git). Enjoy.

UPDATE 03.02.2012: Now updated to version 1.3. Dropped MSBuild support, although it can of course still be used in MSBuild with the Exec command. Added support for strong name signing

UPDATE 12.01.2012: Now updated to version 1.2. Supports injecting into .NET 2.0 and .NET 4.0 assemblies. Source code now lives on github at https://github.com/einaregilsson/InjectModuleInitializer.

UPDATE 04.05.2010: Now updated to version 1.1 with better support for Pdb files and using Mono.Cecil v0.9 instead of 0.6. That also means that the simple example in the blog post is no longer like the actual code, since Mono.Cecil changed drastically between versions. Thanks to Jonathan Evans for his help in figuring out how to get debug symbols to work correctly.


If you read this far you should probably follow me on Twitter.