Операция загрузчика набора API

Важный

Сведения в этом разделе относятся ко всем версиям Windows 10 и более поздним версиям. Здесь мы будем ссылаться на эти версии как "Windows", вызывая все исключения при необходимости.

Наборы API полагаются на поддержку ОС в загрузчике библиотеки, чтобы ввести перенаправление пространства имен модуля в процесс привязки библиотеки. Имя контракта набора API не называет файл. Загрузчик выполняет перенаправление во время выполнения из этого имени контракта в двоичный файл узла, содержащий реализацию.

Когда загрузчик обнаруживает зависимость от набора API во время выполнения, он обращается к данным конфигурации на изображении, чтобы определить двоичный файл узла для этого набора API. Эти данные конфигурации называются схемы набора API. Схема собирается в качестве свойства ОС, а сопоставление между наборами API и двоичными файлами может отличаться в зависимости от того, какие двоичные файлы включены на данном устройстве. Схема — это то, что позволяет импортированной функции в одном двоичном файле правильно направляться на разных устройствах, даже если модуль, на котором размещена реализация, был переименован, разделен или рефакточен.

Как импорт достигает реализации

Двоичный файл может достичь реализации набора API двумя способами, определяемым именем в таблице импорта:

  • Импорт набора прямых api. Двоичный файл импортирует имя контракта набора API. Загрузчик разрешает это имя с помощью схемы набора API для двоичного файла узла на текущем устройстве.
  • Импорт устаревшего модуля. Двоичный файл импортирует устаревшее имя модуля Windows, например samplefeature.dll. В выпуске, который поставляет этот модуль, загрузчик привязывается к нему напрямую. В выпуске, заменив его, обратный переадресатор с тем же именем перенаправляет импорт в набор API, который загрузчик затем разрешает через схему.

Какие из этих имен в конечном итоге в таблице импорта обычно определяются библиотекой, которую вы связываете, а не источником, на который вы пишете. См. Windows библиотеки зонтиков.

Предпочитайте имя контракта API для кода, предназначенного для текущих версий Windows. Загрузчик разрешает его прямо на узел без переадресации между ними. Импортируйте устаревшее имя модуля, если требуется один двоичный файл, который также выполняется в версиях Windows, выпущенных до существования набора API. Обратная переадресация сохраняет работу двоичного файла с выпусками, в которых был заменен устаревший модуль.

Импорт набора прямых API

Разрешение — это трехэтапная последовательность:

  1. Двоичный файл импортирует имя контракта набора API или передает один в LoadLibrary.
  2. Загрузчик ищет контракт в схеме набора API на текущем устройстве и находит двоичный файл узла, с которым сопоставляется схема.
  3. Загрузчик загружает двоичный файл узла и привязывает импортированную функцию к экспорту узла.

Так как сопоставление живет в схеме, а не в файловой системе, то же самое импорт может разрешаться в разные двоичные файлы на разных устройствах:

Устройство api-win-core-samplefeature сопоставляется с
Устройство, включающее функцию samplefeature.dll
Устройство, которое поставляет рефакторинг реализации samplefeaturecore.dll
Устройство, которое не включает функцию Не сопоставлено

Именаsamplefeature, используемые здесь, являются иллюстрированными именами для вымышленного компонента Windows.

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

Импорт имени контракта разрешается в одной операции без участия промежуточного модуля пересылки. Это наиболее эффективная форма и обычный путь для кода, написанного на основе наборов API.

Имена наборов API и суффикс .dll

Так как сопоставления хранятся в схеме, а не на диске, имя набора API, заканчивающееся .dll , не ссылается на файл этого имени. Часть .dll — это только соглашение об именовании, перенесенное из способа написания имен модулей в таблице импорта. Имя набора API больше похоже на псевдоним или виртуальное имя для физического DLL-файла.

Когда операция загрузчика получает имя, начинающееся с api- или ext-, загрузчик направляет его в среду выполнения набора API, расширение загрузчика, разрешающего контракты через схему. Среда выполнения набора API анализирует имя по правилам именования наборов API, а не как имя файла, поэтому суффикс .dll не является частью имени контракта, которое получает разрешение. Включите суффикс при работе с именем, как оно отображается в таблице импорта; в противном случае его можно оставить.

Загрузчик разрешает обе формы имени контракта, имя контракта с версиями и псевдоним контракта с помощью одной схемы. Соглашения, управляющие этими именами, см. в разделе " Набор имен контрактов API".

Стабильность имен не совпадает с доступностью

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

Указанный контракт может отсутствовать на устройстве или присутствовать, но не сопоставляться с узлом. Ничего о названии не говорит вам, что. Чтобы узнать, существует ли реализация, см. статью "Обнаружение доступности набора API".

Какое разрешение требуется

Для вызова через набор API для достижения реализации необходимо хранить все следующие компоненты:

  • Контракт присутствует в схеме на текущем устройстве.
  • Схема сопоставляет контракт с двоичным файлом узла и его можно загрузить.
  • Узел экспортирует определенную функцию, вызываемую двоичным файлом.

Если один из них не удерживает, где поверхности сбоев зависят от импорта набора API:

Стиль импорта Поведение, когда не удается разрешить контракт
Статический импорт Не удается запустить процесс. Загрузчик разрешает статические импорты перед выполнением любого кода.
Импорт с задержкой Процесс начинается нормально. Разрешение отложено до первого вызова API, где код может обрабатывать сбой.

Отсутствующий экспорт сообщается отдельно от отсутствующих контрактов; Двоичный файл, который импортирует функцию, узел не экспортирует ошибку с ошибкой отсутствующих точек входа.

То, что успешная загрузка не сообщает вам

Разрешение привязывает контракт к узлу. Он не оценивает состояние отдельных возможностей в рамках этого контракта.

Контракт может упорядочивать отдельные доступные возможности в именованные группы. Группа может быть недоступна на устройстве, даже если контракт, который несет его, обычно разрешается, так как загрузчик привязывается к детализации контракта и не обращается к состоянию группы при привязке. Это намеренно: отказ привязать узел является смертельным для статического импорта, поэтому загрузчик принимает неизрешительный путь и оставляет более тонкий вопрос вызывающей.

Следствием вашего кода является то, что успешный вызов LoadLibrary или успешный вызов LoadLibrary не является свидетельством того, что определенная возможность доступна. Задайте этот вопрос явным образом с запросом на доступность. Сведения о доступности набора API см. в статье "Обнаружение доступности набора API".

Необязательные наборы API и задержка загрузки

Если приложение вызывает набор API, который может не присутствовать, проверка доступности недостаточно: при статичном импорте процесс не запускается, поэтому выполнение никогда не достигает проверки.

Чтобы обеспечить доступ к необязательному пути кода, настройте модуль, который несет необязательный API для задержки загрузки, или динамически разрешайте целевой объект с помощью LoadLibrary и GetProcAddress после успешного выполнения запроса доступности. Дополнительные сведения об обоих подходах см. в разделе "Обнаружение доступности набора API".

Обратная переадресация

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

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

Полный выпуск desktop поставляет исходные модули, поэтому импорт устаревшего имени модуля привязывается к модулю, так как он всегда имеет. В выпуске, заменив этом модуле, обратный сервер пересылки с тем же именем охватывает разрыв.

Операция загрузчика выполняется следующим образом:

  1. Загрузчик представлен зависимостью от устаревшего имени модуля компьютера Windows, который отсутствует на устройстве.
  2. Загрузчик находит обратный переадресатор, который несет это имя модуля и загружает его.
  3. Обратный переадресатор перенаправляет импортированную функцию в набор API.
  4. Загрузчик разрешает этот API, заданный через схему, как описано ранее в этом разделе.

Концептуально сопоставление выглядит следующим образом:

Импортированная библиотека DLL: samplefeature.dll

  • В выпуске с исходным модулем: samplefeature.dll
  • В выпуске, заменив его: samplefeature.dll обратного пересылки ->api-win-core-samplefeature>samplefeaturecore.dll

Ограничение на этот путь — экспорт покрытия. Обратный сервер пересылки содержит только экспорты с эквивалентами набора API, поэтому он не обязательно экспортирует каждую функцию, созданную в исходном модуле. Двоичный файл, который импортирует функцию обратного переадресатора, не загружается с ошибкой отсутствующих точек входа.

Обратная пересылка также является причиной не рассматривать успешное разрешение как доказательство того, что реализация присутствует. Вызов GetProcAddress к имени устаревшего модуля может возвращать допустимый указатель функции, разрешающий заглушку, возвращающую ошибку. Вместо этого явная доступность запросов. Сведения о доступности набора API см. в статье "Обнаружение доступности набора API".

Заметка

Обратная переадресация охватывает только подмножество поверхности API Win32. Это не позволяет приложениям, предназначенным для классических версий Windows запускаться на всех Windows устройствах. Если двоичные объекты нацелены на текущие версии Windows, то имя контракта набора API является более прямым выбором.

См. также