Какой код содержится в dll и exe файлах платформы net

Обновлено: 04.07.2024

заголовок файла Windows;

заголовок файла CLR;

дополнительные встроенные ресурсы.

Хотя первые два элемента представляют собой такие блоки данных, на которые обычно можно не обращать внимания, краткого рассмотрения они все-таки заслуживают. Ниже предлагаются краткие описания всех перечисленных элементов.

Заголовок файла Windows

Ниже показано, как будет выглядеть информация в заголовке Windows сборки Program.exe:

Пример заголовка файла

Теперь, важно уяснить, что большей части программистов, работающей с .NET, никогда не придется беспокоиться о формате встраиваемых в сборку .NET данных заголовков. Если только не требуется разрабатывать новый компилятор для одного из языков .NET (где такая информация действительно важна), можно не вникать в тонкие детали данных заголовков. Однако помните, что эта информация используется "за кулисами", когда Windows загружает двоичный образ в память.

Заголовок файла CLR

Пример заголовка файла CLR

CIL-код, метаданные типов и манифест сборки

Кроме того, в любой сборке должен обязательно содержаться ассоциируемый с ней манифест (по-другому еще называемый метаданными сборки). В этом манифесте описан каждый входящий в состав сборки модуль, версия сборки, а также любые внешние сборки, на которые ссылается текущая сборка (что отличает сборки от библиотек типов СОМ, в которых не существовало никакой возможности для документирования внешних зависимостей). Как будет показано далее в главе, манифест сборки интенсивно применяется в CLR-среде во время определения места, на которое указывают представляющие внешние сборки ссылки.

Необязательные ресурсы сборки

Однофайловые и многофайловые сборки

С технической точки зрения сборка может состоять из нескольких так называемых модулей (module). Модуль на самом деле представляет собой не более чем просто общий термин, применяемый для обозначения действительного двоичного файла .NET. В большинстве ситуаций, однако, сборка состоит из единственного модуля. В этом случае между (логической) сборкой и лежащим в ее основе (физическим) двоичным файлом существует соответствие типа "один к одному" (откуда и происходит термин "однофайловая сборка").

В однофайловых сборках все необходимые элементы (заголовки Windows и CIL, метаданные типов, манифест и необходимые ресурсы) размещаются внутри единственного пакета *.ехе или *.dll. На рисунке схематично показано устройство однофайловой сборки:

Однофайловая сборка

Текст объемный и рассчитан на:

Примеры процессов выполнения описаны для ОС Windows, но работают по тому же принципу и на других ОС (с учетом различных расширений исполняемых файлов и нативных библиотек).

0. Pay-for-Play


BCL располагается в GAC, откуда приложения загружают необходимые для работы зависимости.

Примеры компонентов, которые поставляются через NuGet:

Этот подход называется «pay-for-play»; другими словами, приложения загружают только ту функциональность, которая им необходима, но каждая такая функциональность содержится в отдельной сборке.

1. FDD vs SCD

  • Portable (Framework-dependent deployment — FDD)
  • Standalone (Self-contained deployment — SCD)

В Standalone (SCD)-приложении все компоненты для выполнения (CoreCLR, CoreFX), а также сторонние библиотеки, то есть абсолютно все зависимости, поставляются вместе с самим приложением (чаще всего в одной папке).

Важно понимать, что Standalone-приложение привязано к определенной ОС и архитектуре (например, Windows 7 x64 или OSX 10.12 x64). Такой идентификатор называется Runtime identifier (RID). Для каждой ОС/архитектуры существует своя версия библиотеки Core CLR (и прочих нативных компонентов), поэтому для Standalone-приложений на этапе компиляции в свойстве RuntimeIdentifier нужно указывать параметры целевой системы (RID).


Файлы фреймворка(-ов) хранятся в папке C:\Program Files\dotnet\shared.

Можно установить несколько версий фреймворка:


Для выполнения Portable-приложения необходимо запустить хост-процесс dotnet.exe и передать ему в качестве аргумента путь к управляемой сборке.

«C:\Program Files\dotnet» добавляется к значению переменной среды PATH, благодаря чему Portable-приложения теперь могут запускаться из командной строки:

Этот файл является обязательным для Portable-приложений.


Уменьшение количества файлов объясняется тем, что в Core FX 1.0 отсутствовали многие библиотеки, поэтому они шли в составе приложения, как обычные зависимости. В Core FX 2.0 эти сборки были добавлены, поэтому они больше не поставляются с приложением, а берутся из папки фреймворка.


Наблюдается картина, противоположная Portable-приложениям — чем больше становится Core FX, тем больше файлов поставляется с приложением.

Рекомендации по выбору типа развертывания

5. Runtime Configuration Files

dotnet.exe ([AppName].exe) использует файл [AppName].deps.json для определения абсолютных путей всех зависимостей приложения при его запуске.

Структура [AppName].deps.json:

Секция targets определяет платформу и дерево зависимостей для нее в формате

[ID зависимости (пакета)]/[версия]: dependencies: < список зависимостей (пакетов) данного пакета >,
относительные пути к управляемым и нативным файлам данного пакета
>

Рассмотрим подробнее содержимое файла deps.json Standalone-приложения:

В свойстве dependencies перечислены зависимости (пакеты) конкретного пакета.
Свойство runtimeTargets используется в deps-файле Portable-приложения и определяет пути файлов библиотек для конкретного RID. Такие RID-specific библиотеки поставляются вместе с Portable-приложением в папке runtimes.

Свойства runtime и native содержат относительные пути управляемых (managed) и нативных библиотек соответственно. Свойство resources содержит относительные пути и локали локализованных сборок-ресурсов.

Пути относительны к NuGet package cache, а не deps-файлу.

Добавить сторонний deps-файл можно передав значение аргумента --additional-deps или переменную среды DOTNET_ADDITIONAL_DEPS.

Такая возможность доступна только для Portable приложений.

Когда dotnet.exe (MyApp.exe) определяет пути зависимостей приложения, для каждой отдельной библиотеки составляется список из runtime- и native-путей.

6.1. Запуск приложения
выполняется при помощи мультплексора (muxer) из командной строки (одинаково на любой ОС).

6.2. [corehost] Поиск и загрузка Framework Resolver (hostfxr.dll)
На этом этапе dotnet.exe идет в папку [own directory]/host/fxr/. Для Portable-приложений эта библиотека расположена в общей папке C:\Program Files\dotnet\host\fxr\[FXR version]\hostfxr.dll. Если версий будет несколько, dotnet.exe будет всегда использовать последнюю.

После загрузки hostfxr.dll (Framework Resolver) процесс запуска переходит в рамки этой библиотеки.

6.3. [hostfxr] Определение режима выполнения (standalone, muxer, split/FX)
Первая задача hostfxr — определить режим, в котором будет работать хост процесс и таким образом тип приложения — Portable (FDD) или Standalone (SCD). В Portable (FDD)-режиме он также определяет: это запускаемое приложение или команда SDK.

— если среди аргументов есть такой, значение которого оканчивается на .dll или .exe — процесс запуска продолжится в режиме выполнение указанного файла. Если такого аргумента нет, управление будет передано SDK. Для этого из папки [own directory]\sdk\[version] (если такая существует) будет запущен dotnet.dll (как Portable приложение), и этой сборке будут переданы аргументы текущего хост процесса.

Алгоритм проверки очень простой — если в папке, откуда был запущен мультиплексор [AppName].exe (в нашем случае dotnet.exe), отсутствует coreclr.dll или [AppName].dll, то приложение Portable. Если один из этих двух файлов существует, то далее идет проверка — приложение Portable (split/FX) или Standalone. Если существует [AppName].dll, то приложение Standalone, иначе — Portable (split/FX).

Запуск Portable-приложения может также осуществляться в так называемом Exec mode.
Для этого команда запуска первым аргументом должна содержать exec C:\> dotnet exec . .

При запуске в таком режиме можно явно указать пути к файлам конфигурации:
--depsfile <PАTH>
--runtimeconfig <PАTH>
которые будут использованы вместо файлов в папке приложения.

На текущем этапе hostfxr определяет (по данным файла конфигурации), является ли приложение Portable или Standalone.

При выборе версии учитывается параметр Roll Forward On No Candidate Fx, который указывает строгость соответствия заданной версии и имеющихся на машине.

6.5. [hostfxr] Поиск и загрузка hostpolicy.dll
На текущем этапе всё готово для определения путей runtime-компонентов. Этой задачей занимается библиотека hostpolicy.dll, которая называется Host library.

Если файл не был найден на предыдущем этапе, hostpolicy.dll будет найдено в папке фреймворка.

Как только опеределена hostpolicy.dll, hostfxr загружает эту библиотеку и передает ей управление.

6.6. [hostpolicy] Определение списка зависимостей
Библиотека hostpolicy.dll отвечает за определение абсолютных путей всех зависимостей приложения.

Прежде всего hostpolicy создаст компонент под названием Dependencies Resolver, который в свою очередь загрузит два deps-файла — файл фреймворка и файл приложения.

Сперва загружается список из deps-файл фреймворка, где будут определены такие зависимости, как CoreCLR и библиотеки CoreFX. Затем список из deps-файла приложения, в котором указаны сборки нашего приложения и их зависимости.

Для каждого deps-файла Dependency Resolver составляет список всех зависимостей для указанной runtimeTarget.

Для каждого пакета сначала составляется список файлов из всех секций runtimeTargets (RID specific зависимости), далее — список всех файлов из секций native и runtime. Такой объединенный список относительных путей всех зависимостей в условном формате
ID пакета — RID — тип asset'а (runtime, native) — пути к файлам называется Target assets.

После того, как были составлены эти два списка файлов зависимостей (RID и не RID), выполняется процесс под названием Reconciling libraries with targets (согласования). Он заключается в том, что для каждого пакета из секции libraries проверяется, существует ли RID specific-файлы, которые должны переопределить обычные.

6.7. [hostpolicy] Определение путей TPA, Core CLR и CLR Jit
Далее Dependency resolver составляет список абсолютных путей файлов управляемых сборок — зависимостей приложения. Этот список называется TPA (Trusted Platform Assemblies) и передается Core CLR для настройки AppDomain. Также составляется список абсолютных путей директорий, в которых находятся остальных файлы зависимостей (кроме coreclr, corejit).

Определение абсолютных путей управляемых сборок происходит путем поиска файлов в Probe paths (путей зондирования). По умолчанию их два — папка фреймворка и папка приложения, и они основаны на расположении deps-файлов. Также можно добавить дополнительные пути:

1) передав аргумент --additionalprobingpath, например
--additionalprobingpath %UserProfile%\\.nuget\\packages


В папке фреймворка и приложения наличие файла проверятся (при условии, что он был указан в соответствующем deps-файле) без учета относительного пути, в остальных директориях с учетом относительно пути, потому что эти директории рассматриваются как кеш NuGet-пакета.

  • папка приложения;
  • папка фреймворка
  • Probe paths

После составления списка TPA, определяются пути CoreCLR и CLRJit.

При отсутствии deps-файла приложения, dotnet.exe вначале попытается найти эти библиотеки в [app directory]\lib\. При обычном выполнении пути берутся из папки фреймворка (отбросив относительный путь и взяв только имя файла).

Устанавливаются следующие настройки CoreCLR:

  • TRUSTED_PLATFORM_ASSEMBLIES — список обсолютных путей всех управляемых библиотек приложения.
  • NATIVE_DLL_SEARCH_DIRECTORIES — абсолютные пути директорий, где найдены нативные зависимости.
  • PLATFORM_RESOURCE_ROOTS — абсолютные пути директорий, где найдены зависимости-ресурсы
  • AppDomainCompatSwitch — константа «UseLatestBehaviorWhenTFMNotSpecified».
  • APP_CONTEXT_BASE_DIRECTORY — папка приложения.
  • APP_CONTEXT_DEPS_FILES — абсолютные пути deps-файлов приложения и фреймворка.
  • FX_DEPS_FILE — абсолютный путь deps-файла фреймворка.
  • PROBING_DIRECTORIES — дополнительные пути зондирования (если они были указаны).

Процесс запуска Standalone-приложения отличается от Portable только начальным этапом, а также местоположением компонентов, которые по умолчанию должны располагаться в папке приложения.

7.2. Процесс запуска
происходит так же, как у Portable-приложения, за исключением того, что существует только один deps-файл и все зависимости ищутся в папке приложения или по указанным --additionalprobepaths.

Сборки имеют следующие составляющие:

Манифест, который содержит метаданные сборки

Метаданные типов. Используя эти метаданные, сборка определяет местоположение типов в файле приложения, а также места размещения их в памяти

Все эти компоненты могут находиться в одном файле, и тогда сборка представляет один единственный файл в формате exe или dll.

Компоненты сборки в .NET

Но также может быть, что эти компоненты хранятся в отдельных файлах. Например, есть основной файл exe, который имеет метаданные сборки и типов и который использует дополнительные файлы ресурсов - различные изображения, звуковые файлы, вспомогательные модули, элементы интерфейса, локализованные для различных культур.


Манифест сборки

Ключевым компонентом сборки является ее манифест . Если у сборки отсутствует манифест, то заключенный в ней код MSIL выполняться не будет. Манифест может находиться в одном файле с исполняемым кодом сборки, а может размещаться и в отдельном файле.

Манифест хранит следующие данные:

Номер версии : основной и дополнительный номера. Используется для управления версиями

Язык и региональные параметры : информация о языке и региональных параметрах, которые поддерживает сборка

Информация о строгом имени : открытый ключ издателя

Список всех файлов сборки : хэш и имя каждого из входящих в сборку файлов

Список ссылок на другие сборки , которые использует текущая сборка

Список ссылок на типы , используемые сборкой

Таким образом, манифест позволяет системе определить все файлы, входящие в сборку, сопоставить ссылки на типы, ресурсы, сборки с их файлами, управлять контролем версий.

Атрибуты сборки

По умолчанию Visual Studio при создании проекта добавляет файл AssemblyInfo.cs, который можно найти в узле Properties:

Смысл этого файла состоит в том, что он задает настройки манифесту сборки. Через атрибуты типа [assembly: AssemblyVersion("1.0.0.0")] можно установить значения в манифесте. Префикс assembly: перед атрибутом указывает на то, что это атрибут уровня сборки, в данном случае атрибут номера версии сборки.

В принципе, исходя из названия, я думаю, предназначение многих атрибутов итак понятно:

AssemblyCompany : название компании

AssemblyConfiguration : конфигурация сборки (Retail или Debug)

AssemblyCopyright : авторское право на программу

AssemblyDefaultAlias : псевдоним по умолчанию, используемый при ссылке на данную сборку из других сборок

AssemblyDescription : краткое описание сборки

AssemblyProduct : информация о продукте

AssemblyTitle : название сборки как информационного продукта

AssemblyTrademark : сведения о торговой марке

AssemblyCulture : задает язык и региональные параметры, поддерживаемые сборкой (например, установка русской культуры: [assembly:AssemblyCultureAttribute("ru")] )

AssemblyInformationalVersion : полная версия сборки

AssemblyVersion : версия сборки

AssemblyFileVersion : номер версии файла Win32. По умолчанию совпадает с версией сборки.

Также есть графический способ определения информации о сборке. Для этого нажмем в окне Solution Explorer (Обозреватель решений) на название проекта правой кнопкой мыши и выберем в появившемся меню пункт Properties (Свойства). В открывшемся окне нажмем на кнопку Assembly Information. , и нам предстанет окно изменения атрибутов:

Приложения (программы) можно разрабатывать на разных языках программирования, которые поддерживают эту технологию.

  • совместное использование разных языков программирования;
  • безопасность и переносимость программ;
  • общую модель программирования на базе платформы Windows.
  • общеязыковая исполнительная среда CLR ( Common Language Runtime );
  • библиотека базовых классов.

Библиотека базовых классов включает в себя определение разнообразных примитивов, которыми могут быть: потоки, графические API-интерфейсы, реализация баз данных, файловый ввод-вывод и прочее.

3. Какой принцип действия общеязыковой среды выполнения CLR ( Common Language Runtime )?

Основное назначение CLR – превратить промежуточный код MSIL в исполнительный код в процессе выполнения программы.

Технология .NET. Процесс преобразования исходного кода в код на языке MSIL (CIL) и создание файла сборки

Рис. 1. Процесс преобразования исходного кода в код на языке MSIL ( CIL или IL ) и создание файла сборки ( *.dll или *.exe )

Исполнительная среда CLR отвечает за определение места размещения сборки (assembly).

Запрашиваемый тип, который размещается в сборке (например, класс ArrayList или другой тип), определяется в двоичном файле ( *.dll или *.exe ) с помощью считывания метаданных этого файла.

После этого CLR размещает в памяти считанный из сборки тип.

Затем CLR превращает CIL-код в соответствующие инструкции, которые подстраиваются под конкретную платформу (в зависимости от ПК, операционной системы и т.п.). Кроме того, на этом этапе происходят необходимые проверки на предмет безопасности.

Последним происходит выполнение запрашиваемого программного кода.

4. Что такое промежуточный язык MSIL ( Microsoft Intermediate Language ) или CIL ( Common Intermediate Language )?

MSIL есть псевдокодом. MSIL определяет набор инструкций, которые:

  • могут переноситься на разные платформы;
  • не зависят от конкретного процессора.

Фактически, MSIL – это язык переносного ассемблера

Сборка предназначена для сохранения пространств имен ( namespaces ). Пространства имен содержат типы. Типами могут быть классы, делегаты, интерфейсы, перечисления, структуры.

Сборка может содержать любое количество пространств имен. Любое пространство имен может содержать любое количество типов (классов, интерфейсов, структур, перечислений, делегатов).

6. Что размещается в сборках?

В сборках размещается CIL -код ( MSIL -код или IL -код) и метаданные.

7. Что такое манифест ( manifest )?

Манифест – это описание самой сборки с помощью метаданных.

В манифесте размещается информация:

  • о текущей версии сборки;
  • сведения о культуре (локализация строчных и графических ресурсов);
  • перечень ссылок на все внешние сборки, которые нужны для правильного функционирования.

Если в исходном коде используются библиотеки базовых классов (например из сборки mscorlib.dll ), то они загружаются с помощью загрузчика классов.

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

После этого приложение выполняется.

Технология .NET. Связь между исходным кодом, компилятором и механизмом выполнения .NET

9. Какие существуют виды сборок?

Существует два вида сборок:

  • однофайловые сборки;
  • многофайловые сборки.

Сборка, которая состоит из одного единого модуля ( *.dll или *.exe ) называется однофайловой. В однофайловых сборках все необходимые CIL -инструкции, метаданные и манифесты размещаются в одном, четко определенном пакете.

В многофайловой сборке один из модулей есть главным ( primary ).

10. В каком файле размещается главная сборка библиотеки MS Visual Studio?

Главная сборка размещается в файле “ mscorlib.dll ”.

11. Что такое общая система типов CTS ?

CTS ( Common Type System ) – система типов, которая содержит полное описание всех возможных типов данных и программных конструкций, которые поддерживаются общеязыковой исполнительной средой CLR . Также здесь описывается то, как эти сущности могут взаимодействовать между собою.

Типами могут быть классы, интерфейсы, структуры, перечисления, делегаты.

12. Какое назначение общеязыковой спецификации CLS?
14. Что такое пространство имен ( namespace )?

Пространство имен предназначено для объединения группы типов, которые связаны между собою с семантической точки зрения. Типы размещаются в сборках ( assembly ). Под типами понимаются классы, делегаты, интерфейсы, структуры, перечисления.

Примеры названий пространств имен:

Например, в пространстве имен System.Data размещаются основные типы для работы с базами данных, в пространстве имен System.Collections размещаются основные типы для работы с коллекциями.

15. Как вывести содержимое сборок, пространств имен и типов в MS Visual Studio ?

В системе Microsoft Visual Studio есть утилита Object Browser , которая вызывается с меню View (рисунок 3).

Microsoft Visual Studio. Команда вызова утилиты Object Browser

Рис. 3. Вызов утилиты Object Browser

В результате откроется окно Object Browser , в котором будут отображаться сборки, которые используются в определенной технологии.

Microsoft Visual Studio. Окно Object Browser с выделенной сборкой mscorlib.dll

Рис. 4. Окно Object Browser с выделенной сборкой mscorlib.dll

Если раскрыть содержимое сборки mscorlib (знак “ + ”), то будет отображен список всех пространств имен данной сборки (рисунок 5). Как видно из рисунка, сборка включает пространства имен Microsoft.Win32 , System , System.Collections , System.Collections.Concurrent и много других.

Microsoft Visual Studio. Сборка mscorlib и список пространств имен, которые входят в нее

Рис. 5. Сборка mscorlib и список пространств имен, которые входят в нее

Аналогично раскрывается любое из пространств имен. В пространствах имен описываются типы. В типах описываются методы, свойства, константы и т.п.

На рисунке 6 изображен класс BinaryReader из пространства имен System.IO . По всей видимости, в классе реализованы методы с именами BinaryReader() , Close() , Dispose() , FillBuffer() и прочие.

Microsoft Visual Studio. Утилита Object Browser. Содержимое класса BinaryReader

Рис. 6. Содержимое класса BinaryReader

Для подключения пространства имен используется ключевое слово using .

Примеры подключения пространств имен:

После подключения пространства имен можно обращаться к типам, которые в них реализованы.

Читайте также: