Какова самая ранняя точка входа, которую CLR вызывает перед вызовом любого метода в сборке? - PullRequest
4 голосов
/ 29 июля 2010

В последние годы я иногда задавался вопросом, какой эквивалент (в) знаменитой DLL_PROCESS_ATTACH был доступен в мире .NET. Любая моя документация, слегка упрощенная, говорит, что самой ранней точкой входа в класс является статический конструктор (cctor), но вы не можете влиять на , когда это называется , и вы не можете определить один cctor, который гарантированно будет вызываться до любого другого инициатора cctor или field, хак, он может даже не вызываться вообще, если класс никогда не используется.

Итак, если вы хотите гарантировать инициализацию чего-либо за до , то вызывается любой метод вашей сборки, и вам не нужно добавлять cctor к каждому классу в вашей сборке, какой подход вы можете использовать ? Или в .NET есть простое управляемое решение, которое я пропустил все эти годы?

Ответы [ 3 ]

4 голосов
/ 30 июля 2010

Обычно я не отвечал на свой вопрос, но в то же время я нашел ответ, которого здесь раньше не было, так что вот и я.

После некоторого исследования я попал на этот пост Microsoft , в котором объясняются проблемы смешивания управляемого и неуправляемого кода внутри DllMain и решения, которое появилось со 2-й версией CLI, модуль инициализаторов . Цитата:

Этот инициализатор запускается сразу после родной DllMain (другими словами, за пределами блокировки загрузчика) но перед любым управляемый код выполняется или управляемые данные доступ из этого модуля. семантика модуля .cctor очень похожи на классы .cctors и определены в ECMA C # и Общая языковая инфраструктура Стандарты.

Хотя я не смог найти термин инициализатор модуля внутри текущей спецификации ECMA, он логически следует из инициализатора типа и глобального специального класса <Module> (see section 22.26 на MethodDef, подпункт 40). Эта функция была реализована после .NET 1.1 (то есть, начиная с 2.0). Смотрите также это полуофициальное описание .

Этот вопрос был не о C #, а потому, что он является языком общения в .NET: C # не знает глобальных методов, и вы не можете создать <Module>, не говоря уже о его cctor. Тем не менее, Эйнар Эгильссон распознал этот очевидный недостаток и создал InjectModuleInitializer.exe, который позволяет сделать это как шаг после компиляции в Visual Studio. В C ++. NET использование этого метода тривиально и рекомендуется вместо DllMain. См. Также этот SO ответ Бена Фойгта (не принятый ответ) и этот SO ответ yoyoyoyosef .

Короче говоря, инициализатор модуля - это первый метод, который вызывается после загрузки модуля (не обязательно при загрузке сборки!) И перед вызовом любого метода класса или экземпляра. Он не принимает параметров, не возвращает значений, но может содержать любой управляемый код в своем теле.

2 голосов
/ 29 июля 2010

На самом деле не совсем верно, что cctor вызывается первым. Если у вас есть статическое поле, инициализированное статическим методом, будет вызван статический метод.

Посмотрите на этот код:

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();
        }
    }
}

РЕДАКТИРОВАТЬ : Также рассмотрите возможность использования Заводской шаблон , который поможет вам выполнить всю необходимую инициализацию перед возвратом созданного объекта.

1 голос
/ 29 июля 2010

Это сделано специально: оно минимизирует связь между статическими конструкторами. Вы знаете, что ваш cctor будет вызываться до того, как инициализируется что-либо в вашем классе, и после cctor любых классов, используемых вашим классом. Но нет никакой гарантии, когда он будет работать по сравнению с несвязанными классами в одном приложении.

Если вы хотите убедиться, что ваш код выполняется перед точкой входа, подумайте над написанием оболочки для основного приложения. Простым способом было бы поместить его в отдельный исполняемый файл.

Более автономный способ сделать это может быть:

  1. Запустите любой необходимый код запуска в правильном порядке. Не указывайте в сборках какие-либо типы, которые не должны инициализироваться.
  2. Создайте свой собственный домен приложения
  3. Запуск реальной точки входа в этом втором домене приложения
...