27

Module initializers are a feature of the CLR that are not directly available in C# or VB.NET. They are global static methods named .cctor that are guaranteed to run before any other code (type initializers, static constructors) in an assembly are executed. I recently wanted to use this in a project and hacked together my own solution (console program/msbuild task) using Mono.Cecil, but I was wondering:

  1. Is there any way to trick the C# compiler into emitting module intializers? Any attributes (e.g. CompilerGenerated, SpecialName) or other trickery that could be used?

  2. Do the C# / VB.NET ever emit these initializers themselves for some purpose? From what I've seen they are used by managed C++ for some interop purposes, but I couldn't find any reference to them being used for other purposes. Any ideas?

user247702
  • 23,641
  • 15
  • 110
  • 157
Einar Egilsson
  • 3,438
  • 9
  • 36
  • 47
  • 1
    You could use a similar technique to [Jon Skeet's Unconstrained Melody library](http://code.google.com/p/unconstrained-melody/), which he uses to get around the fact that you can't use enum constraints in C#. His solution is to use placeholder attributes to drive a post-process program (written in C#) that drives ilasm.exe to re-write the IL to correctly implement the stuff that can't be implemented in C#. – Merlyn Morgan-Graham Jul 24 '11 at 21:57
  • Related discussion: http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx – user423430 Jan 22 '13 at 16:01

6 Answers6

15

After eleven years, this is finally supported by the language in C#9

https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#support-for-code-generators

Module initializers are methods that have the ModuleInitializerAttribute attribute attached to them. These methods will be called by the runtime before any other field access or method invocation within the entire module. A module initializer method:

  • Must be static

  • Must be parameterless

  • Must return void

  • Must not be a generic method

  • Must not be contained in a generic class

  • Must be accessible from the containing module

That last bullet point effectively means the method and its containing class must be internal or public. The method can't be a local function. For more information, see ModuleInitializer attribute.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Carlos Araujo
  • 414
  • 3
  • 7
  • Can you elaborate how source generators enable module initialization? They seem like 2 distinct features from a first glance. – julealgon Nov 27 '20 at 15:23
  • The code generator is just the tool that make module initializers possible – Carlos Araujo Nov 27 '20 at 15:36
  • 2
    I'm not quite following your reasoning here. Code Generators are a completely separate feature to module initializers, that work completely differently. Module initializers are enabled via an attribute specific for that purpose. Yes, you could use a generator to embed an initializer into someone else's module, but that's something completely different. – julealgon Nov 29 '20 at 04:16
12

Check out the module initializer addon of the awesome opensource IL-Weaver project "fody", written by Simon Cropp: https://github.com/fody/moduleinit

It allows you to specify a method which will be translated into an assembly initializer by fody:

public static class ModuleInitializer
{
    public static void Initialize()
    {
        //Init code
    }
}

gets this:

static <Module>()
{
    ModuleInitializer.Initialize();
}
Simon
  • 33,714
  • 21
  • 133
  • 202
David Roth
  • 677
  • 4
  • 14
  • 10
    Thanks for the suggestion. That's actually based on my own code that I wrote after asking this question. Simon Cropp then adapted it as a plugin for his excellent Fody project. – Einar Egilsson Jul 15 '13 at 19:02
9

No, there is no way to emit them in C#, because C# puts everything in a class/struct and module initializers need to be global.

You will have to use a different tool to write them, preferably IL-Assembler.

As for the second question, I have to admit that I don't know, but I have never seen any generated by C#, and I use ILDasm quite often, so I assume that it doesn't emit them.

Maximilian Mayerl
  • 11,253
  • 2
  • 33
  • 40
3

Maybe System.Reflection.Emit namespace can help you. MethodAttributes enumeration contains similar elements (SpecialName, RTSpecialName).

axel22
  • 32,045
  • 9
  • 125
  • 137
Nikolay
  • 31
  • 1
1

Einar, Your question did not make clear to me that you already wrote a tool that allows to inject a module initializer into an already compiled Assembly.

http://einaregilsson.com/module-initializers-in-csharp

I tried your application and it works perfectly. As you write it should work with all current frameworks from 2 to 4.5.

There is only one problem that makes your solution useless for me: Your initializer is called when the application first ACCESSES anything in the assembly.

What I need is that the module initializer should be called immediately when the Assembly is loaded into the process. But it is not. So if the application does not access the assembly it will never be initialized.

After investigating a whole day it seems to me that the only way to get this is to write a Managed C++ Assembly and execute the code in DllMain().

Elmue
  • 7,602
  • 3
  • 47
  • 57
  • 1
    .NET assemblies are typically delay-loaded. Meaning: your assumption that the module initializer is executed some time after the assembly is loaded probably isn't true. It's just that the assembly is only loaded when it's needed, *not* immediately when the application starts. – Paul Groke Nov 06 '15 at 22:34
  • Maybe I was not clear enough. I'am talking about loading a C# DLL into a C# process via Assembly.Load(). In this case the initializer is not executed. I was searching for an equivalent for DllMain() in C++ that is executed immediately when the DLL is loaded into the process. – Elmue Nov 07 '15 at 12:28
  • Ah, I see. That's strange then. – Paul Groke Nov 08 '15 at 14:46
-5

If you have static constructors or singleton classes which you can access true a static variable C# compiler will emit .cctor.

Shamika
  • 569
  • 2
  • 7
  • 14
  • 4
    It emits a .cctor in the class where you defined a static constructor, yes, but that is just the static constructor of that class and will run the first time the class is accessed. A module initializer is a .cctor on the pseudo class and is guaranteed to run before ANY other code in the assembly, regardless of what code is accessed first. – Einar Egilsson Dec 17 '09 at 06:36