What is the earliest entrypoint that the CLR calls before calling any method in an assembly?

与世无争的帅哥 提交于 2020-01-23 00:20:08

问题


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?


回答1:


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 (i.e., 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.




回答2:


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.




回答3:


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:

  1. Run whatever startup code is needed, in the right order. Don't reference any types in assemblies that shouldn't get initialized.
  2. Create your own app domain
  3. Run the real entry point within this second app domain


来源:https://stackoverflow.com/questions/3363569/what-is-the-earliest-entrypoint-that-the-clr-calls-before-calling-any-method-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!