Как DLL загружаются в CLR?

Мое предположение всегда заключалось в том, что среда CLR загружала все DLL файлы, необходимые для запуска домена приложения. Однако я написал пример, который заставляет меня подвергнуть сомнению это предположение. Я запускаю свое приложение и проверяю, сколько модулей загружено.

Process[] ObjModulesList;
ProcessModuleCollection ObjModulesOrig;

//Get all modules inside the process
ObjModulesList = Process.GetProcessesByName("MyProcessName");
// Populate the module collection.
ObjModulesOrig = ObjModulesList[0].Modules;

Console.WriteLine(ObjModulesOrig.Count.ToString());

Затем я повторяю тот же самый код, и мой счет отличается. Дополнительная DLL - C:\WINNT\system32\version.dll.

Я действительно смущен, почему подсчеты будут разными.

Разве кто-то может рассказать о том, что делает CLR и как он загружает эту вещь, и по какой логике она это делает?

Ответы

Ответ 1

Ниже скопировано из Don Box отличное Essential.Net. (доступно здесь)
(и, imho, должно быть для любого профессионального разработчика .Net)

Загрузчик CLR

Загрузчик CLR отвечает за загрузку и инициализацию сборок, модулей, ресурсов и типов. Загрузчик CLR загружается и инициализируется как можно меньше. В отличие от загрузчика Win32, загрузчик CLR не разрешает и автоматически загружает подчиненные модули (или сборки). Скорее, подчиненные части загружаются по требованию только в том случае, если они действительно необходимы (как в случае с функцией задержки при загрузке Visual С++ 6.0). Это не только ускоряет время инициализации программы, но и уменьшает количество ресурсов, потребляемых запущенной программой. В CLR загрузка обычно запускается компилятором Just-time (JIT) на основе типов. Когда компилятор JIT пытается преобразовать тело метода из CIL в машинный код, ему необходим доступ к типу определения типа объявления, а также определения типов для полей типа. Более того, компилятору JIT также необходим доступ к определениям типов, используемым любыми локальными переменными или параметрами метода, скомпилированным JIT. Загрузка типа подразумевает загрузку как сборки, так и модуля, который содержит определение типа. Эта политика типов загрузки (и сборок и модулей) по требованию означает, что части программы, которые не используются, никогда не попадают в память. Это также означает, что работающее приложение часто увидит новые сборки и модули, загруженные с течением времени, поскольку типы, содержащиеся в этих файлах, необходимы во время выполнения. Если это не то поведение, которое вы хотите, у вас есть два варианта. Один из них - просто объявить скрытые статические поля типов, которые вы хотите явно взаимодействовать с загрузчиком.

Обычно загрузчик выполняет свою работу неявно от вашего имени. Разработчики могут напрямую взаимодействовать с загрузчиком через загрузчик сборок. Сборщик загружается разработчиками с помощью статического метода LoadFrom в классе System.Reflection.Assembly. Этот метод принимает строку CODEBASE, которая может быть либо путем файловой системы, либо единым локатором ресурсов (URL), который идентифицирует модуль, содержащий манифест сборки. Если указанный файл не найден, загрузчик будет генерировать исключение System.FileNotFoundException. Если указанный файл можно найти, но не является модулем CLR, содержащим манифест сборки, загрузчик будет генерировать исключение System.BadImageFormatException. Наконец, если CODEBASE - это URL-адрес, который использует схему, отличную от file:, вызывающий должен иметь права доступа WebPermission, иначе генерируется исключение System.SecurityException. Кроме того, сборка по URL-адресам с протоколами, отличными от file:, сначала загружается в кэш загрузки до загрузки.

В листинге 2.2 показана простая программа С#, которая загружает сборку, расположенную в file://C:/usr/bin/xyzzy.dll, а затем создает экземпляр содержащегося типа с именем AcmeCorp.LOB.Customer. В этом примере все, что предоставляется вызывающим абонентом, является физическим расположением сборки. Когда программа использует загрузчик сборок таким образом, CLR игнорирует четырехчастное имя сборки, включая номер версии.

Пример 2. 2. Загрузка сборки с явной CODEBASE

using System;
using System.Reflection;
public class Utilities {
  public static Object LoadCustomerType() {
    Assembly a = Assembly.LoadFrom(
                    "file: //C:/usr/bin/xyzzy. dll") ;
    return a.CreateInstance("AcmeCorp.LOB.Customer") ;
  }
}

Хотя загрузка сборок по местоположению несколько интересна, большинство сборок загружаются по имени с помощью преобразователя. Агрегатор сборки использует имя сборки из четырех частей, чтобы определить, какой базовый файл загружается в память, используя загрузчик сборок. Как показано в Рисунок 2.9, этот процесс разрешения имен для местоположения учитывает множество факторов, включая каталог, в котором размещено приложение, политики версий и другие детали конфигурации (все из которых будут рассмотрены далее в этой главе).

Решатель сборки отображается разработчикам с помощью метода Load класса System.Reflection.Assembly. Как показано в листинге 2.3, этот метод принимает имя сборки с четырьмя частями (либо как строка, либо как ссылка AssemblyName), и внешне похоже похоже на метод LoadFrom, открытый загрузчиком сборки. Сходство является только глубоким, потому что метод Load сначала использует распознаватель сборки для поиска подходящего файла с использованием довольно сложной серии операций. Первая из этих операций - применить политику версий, чтобы точно определить, какая версия нужной сборки должна быть загружена.

Пример 2.3. Загрузка сборки с помощью резонатора сборки

using System;
using System.Reflection;
public class Utilities {
  public static Object LoadCustomerType() {
    Assembly a = Assembly.Load(
      "xyzzy, Version=1. 2. 3.4, " +
      "Culture=neutral, PublicKeyToken=9a33f27632997fcc") ;
    return a.CreateInstance("AcmeCorp.LOB.Customer") ;
  }
}

Ответ 2

CLR загружает сборки по требованию. Когда вы выполняете метод, он смотрит, где он (какой модуль и т.д.), И если он не загружен, он загружает его.

Вот статья о производительности CLR и рассказывает о загрузке сборок:

Когда CLR точно в срок (JIT) компилирует метод Start, ему необходимо загрузить все сборки, на которые делается ссылка в этом методе. Это означает, что все сборки, указанные в обработчике исключений, будут загружены, даже если они могут не понадобиться большую часть времени, когда приложение выполняется.

Эта статья предназначена для SilverLight, но немного говорит о том, что происходит с CLR.

Ответ 3

Это немного не соответствует вашему вопросу, но вы можете сразу загрузить все сборки, если вы предпочитаете, чтобы это не было по требованию. Что-то вроде этого должно сделать трюк

foreach (AssemblyName asn in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
{
    var asm = Assembly.Load(fn);
    // I've found get types does a good job of ensuring the types are loaded.
    asm.GetTypes();
}

Ответ 4

COM DLLS загружаются по требованию всякий раз, когда создается соответствующий COM-объект. Это также может происходить и с DLL не COM.