What is the earliest entrypoint that the CLR calls before calling any method in an assembly?
In the past years I've occasionally been wondering what equivalent of the (in)famous DLL_PROCESS_ATTACH
was available in the .NET world. Any documentation I have says, slightly simplified, that the earliest entry point to a class is the static constructor (cctor), but you cannot influence when it is called, nor can you define one cctor that's guaranteed to be called prior to any other cctor or field initializer, hack, it may not even be called at all if the class is never used.
So, if you want to guarantee something's initialized before any method of your assembly is called and you don't want to have to add a cctor to every class in your assembly, what approach can you take? Or is there an easy, managed solution in .NET that I have missed all these years?
I'd normally not answer my own question, but meanwhile I did find an answer that hasn't come up here before, so here I go.
After some research, I happened on this post by Microsoft, which explains the problems of mixing managed and unmanaged code inside DllMain
and the solution, which came about with the 2nd version of the CLI, module initializers . Quote:
This initializer runs just after the native DllMain (in other words, outside of loader lock) but before any managed code is run or managed data is accessed from that module. The semantics of the module .cctor are very similar to those of class .cctors and are defined in the ECMA C# and Common Language Infrastructure Standards.
While I wasn't able to find the term module initializer inside the current ECMA specification, it follows logically from type initializer and the global <Module>
special class (see section 22.26 on MethodDef, sub-point 40). This feature was implemented after .NET 1.1 (ie, from 2.0 onwards). See also this semi-official description.
This question wasn't about C#, but because it is the lingua franca of .NET: C# doesn't know global methods, and you can't create a <Module>
, let alone its cctor. However, Einar Egilsson has recognized this apparent deficiency and created InjectModuleInitializer.exe that allows you to do this as a post/compile step from Visual Studio. In C++.NET, using this method is trivial and recommended practice in place of DllMain
. See also this SO answer by Ben Voigt (not the accepted answer) and this SO answer by yoyoyoyosef.
In short, the module initializer is the first method that is called after loading the module (not necessarily when loading assembly!) and before calling any class or instance method. It takes no parameters, returns no value, but can contain any managed code in its body.
Actually it is not exactly true that cctor
is called first. If you have static field initialized by static method that static method will be called.
Have a look at this code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CallSequence
{
internal class Test
{
internal Test()
{
Console.WriteLine("non-static constructor");
}
static Test()
{
Console.WriteLine("static constructor");
}
static int myField = InitMyField();
static int InitMyField()
{
Console.WriteLine("static method : (InitMyField)");
return 0;
}
}
class Program
{
static void Main(string[] args)
{
Test t = new Test();
}
}
}
EDIT : Also consider using Factory pattern which will help you do all the needed initialization before returning created object.
This is by design: it minimises coupling between static constructors. You know that your cctor will be invoked before anything in your class initializes, and after the cctors of any classes used by your class. But there's no guarantee on when it will run compared to unrelated classes in the same application.
If you want to make sure some code of yours runs before the entry point, consider writing a wrapper for the main application. A straightforward way would be to put it in a separate executable.
A more self-contained way to do this might be to:
上一篇: F#内联函数的最大深度是多少?