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.
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 merged Mono.Cecil into it using the excellent ILMerge tool. You can run it from the command line and give it the filename of your assembly and optionally specify the method to call. 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
Realistically though, if you want to do this you probably want to do it right after building your assembly. That’s why the assembly also includes an MSBuild task, so that it can be used directly in a .csproj file. The best target to use for this is the AfterBuild target. To use this task in your project you need to edit your .csproj file and add two things:
<UsingTask TaskName="InjectModuleInitializer"
AssemblyFile="c:\some\folder\InjectModuleInitializer.exe" />
If the InjectModuleInitializer.exe assembly is registered in the global assembly cache you could also write that as
<UsingTask TaskName="InjectModuleInitializer"
AssemblyName="InjectModuleInitializer, Version=1.1.0.0, Culture=neutral, PublicKeyToken=818425a64317679d" />
<Target Name="AfterBuild">
<InjectModuleInitializer AssemblyFile="$(TargetPath)" />
</Target>
Or, if you want to explicitly specify the method to run:
<Target Name="AfterBuild">
<InjectModuleInitializer
AssemblyFile="$(TargetPath)"
ModuleInitializer="SimpleTest.Program::Con" />
</Target>
And there you have it. You can download the console program/MSBuild task or download the source code and build it yourself. Enjoy.
Perfect! I have just spent half a day searching for a solution to this problem. I have a support library that must register exception handlers and the like – I’d like to be able to do it implicitly (referring to the assembly is enough to trigger it.) This looks like it is spot on. Thanks.
Hi Jonathan.
Well, due to the way .NET loads assemblies I would think just referring to it is not enough. .NET will load the assembly the first time something from it is used. So, the module initializer is guaranteed to run before any OTHER code in the assembly runs, but if no other code from the assembly ever runs then the module initializer won’t run either because the assembly simply won’t be loaded.
I might be wrong about this, but that is my understanding of how assembly loading works. Let me know if it works out for you
It is working fine for me. But… when running in debug mode the debugger is very confused. I would guess that this is because the PDB is out of sync with the DLL. I’ve had a look and reckon it could be solved with the following calls:
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(AssemblyFile);
>>> assembly.MainModule.LoadSymbols();
…
AssemblyFactory.SaveAssembly(assembly, AssemblyFile);
>>> assembly.MainModule.SaveSymbols();
Also, I couldn’t get the MSBuild task to work correctly, but I didn’t try too hard – it was easy to just make the call from the AfterBuild event in the project properties.
Thanks for the info. I didn’t really think about the debug symbols when I made this but yes, that looks like it should do the trick. Thanks.
I can’t persuade the source you’ve supplied to compile and run correctly – a problem with Cecil’s own PDB being missing (not that I can see why it would need it). I tried downloading Cecil from the Mono weekly build but something else is missing. Have I missed something obvious in the way it should be built.
I have built a new version that handled the PDBs as well. I can’t persuade it all into one exe though – even with ILMerge. The problem I was having was with Mono.Cecil.Pdb.dll being missing. This is separate to the rest of Cecil because it has dependecies on Windows/MS – specific code. I found compiled versions in Reflexil(http://www.codeproject.com/KB/msil/reflexil.aspx?msg=2576050). So, I have a zip with the modified source and Cecil dlls. Sadly I still have problems debugging. Please get in touch if your interested in the changes I made.
Hi
Ah yes, Mono.Cecil has a seperate assembly for pdbs. If you send me your source I’ll take a look at it and see if I can figure out what the debug problems are.
I can’t find an email address anywhere. Contact me on “jonathan at open-collar dot org dot uk”.
So I’m trying to get some code to run in an assembly right after it is loaded via Assembly.LoadFrom, but this doesn’t do it. The ModuleInitializer.Run function is called just before the static constructor for the first class in the module is called. I would love to have a way to execute something in the assembly immediately after Assembly.LoadFrom, but that may be asking too much… I wonder if there’s an Assembly level .cctor. Where does one learn about these cool .NET functions that aren’t accessible from C# and VB.Net?
Anyhow, thanks for this tool, it’s the closest I got to what I needed all day.
@Phil: I don’t think there’s any such thing as an assembly level constructor, and even if there was, .NET prefers to call all kinds of initializers at the last possible moment, e.g. module initializers are only called just before some other code in a module is about to run, static constructors are only run just before the type is actually used for the first time etc.
As for learning about these types of features, I learnt about this when building a .NET compiler for my masters thesis. I don’t actually think there are that many features available in .NET that aren’t accessible from C#. The two big ones I know of are this and global functions.