Главная

Категории:

ДомЗдоровьеЗоологияИнформатикаИскусствоИскусствоКомпьютерыКулинарияМаркетингМатематикаМедицинаМенеджментОбразованиеПедагогикаПитомцыПрограммированиеПроизводствоПромышленностьПсихологияРазноеРелигияСоциологияСпортСтатистикаТранспортФизикаФилософияФинансыХимияХоббиЭкологияЭкономикаЭлектроника






Динамически подключаемые библиотеки (DLL)


Динамически подключаемые библиотеки (DLL)

Динамически подключаемая библиотека(DLL, Dynamic Link Library) представляет собой программный модуль, который может быть загружен в виртуальную память процесса как статически, во время создания исполняемого модуля процесса, так и динамически, во время исполнения процесса операционной системой.

Мотивы использования DLL

• Расширение функциональности приложения.

• Возможность использования разных языков программирования.

• Более простое управление проектом.

• Экономия памяти.

• Разделение ресурсов.

• Упрощение локализации. Решение проблем, связанных с особенностями различных платформ.

• Реализация специфических возможностей.

Этапы создания DLL

• Прежде всего Вы должны подготовить заголовочный файл с прототипами функций, структурами и идентификаторами, экспортируемыми из DLL. Этот файл включается в исходный код всех модулей Вашей DLL. Как Вы потом увидите, этот же файл понадобится и при сборке исполняемого модуля (или модулей), который использует функции и переменные из Вашей DLL

• Вы пишете на С/С++ модуль (или модули) исходного кода с телами функций и определениями переменных, которые должны находиться в DLL. Так как эти модули исходного кода не нужны для сборки исполняемого модуля, они могут остаться коммерческой тайной компании-разработчика.

• Компилятор преобразует исходный код модулей DLL в OBJ-файлы (по одному на каждый модуль).

• Компоновщик собирает все OBJ-модули в единый загрузочный DLL-модуль, в который в конечном итоге помещаются двоичный код и переменные (глобальные и статические), относящиеся к данной DLL. Этот файл потребуется при компиляции исполняемого модуля

• Если компоновщик обнаружит, что DLL экспортирует хотя бы одну переменную или функцию, то создаст и LIB-файл. Этот файл совсем крошечный, поскольку в нем нет ничего, кроме списка символьных имен функций и переменных, экспортируемых из DLL. Этот LIB-файл тоже понадобится при создании ЕХЕ-файла.
Создав DLL, можно перейти к сборке исполняемого модуля.

• Во все модули исходного кода, где есть ссылки на внешние функции, переменные, структуры данных или идентификаторы, надо включить заголовочный файл, предоставленный разработчиком DLL.

Пояснения к схеме. Создание DLL

• 1) Заголовочный файл с экспортируемыми прототипами, структурами и идентификаторами (символьными именами)

• 2) Исходные файлы С/С++ в которых реализованы функции и определены переменные

• 3) Компилятор создаэт OBJ-файл из каждого исходного файла С/С++

• 4) Компоновщик собирает DLL из OBJ-модулей

• 5) Если DLL экспортирует хотя бы одну переменную или функцию, компоновщик создает и LIB-файл

• 6) Заголовочный файл с импортируемыми прототипами структурами и идентификаторами

• 7) Исходные файлы С/С++, из которых вызываются импортируемые функции и переменные

• 8) Компилятор создает OBJ-файл из каждого исходного файла С/С++

• 9) Используя OBJ модули и LIB-файл и учитывая ссылки на импортируемые идентификаторы компоновщик собирает ЕХЕ-модуль (в котором также размещается таблица импорта — список необходимых DLL и импортируемых идентификаторов)

• 10) Создается адресное пространство процесса, проецируется исполняемый exe модуль

• 11) Используя записи в таблице импорта, загрузчик производит поиск библиотек, от которых зависит исполняемый модуль, проецирует их в память.

• 12) Шаг 11) повторяется для каждой из загруженных библиотек.

• 13) В случае успешного разрешения всех зависимостей создается первичный поток, приложение начинает выполняться

Последовательность поиска DLL

• Каталог, содержащий ЕХЕ-файл.

• Текущий каталог процесса.

• Системный каталог Windows

• Основной каталог Windows

• Каталоги, указанные в переменной окружения PATH.

Искажение имен

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

• Представьте, что DLL написана на С++, а исполняемый код — на стандартном С. При сборке DLL имя функции будет искажено, но при сборке исполняемого модуля — нет.

• Пытаясь скомпоновать исполняемый модуль, компоновщик сообщит об ошибке исполняемый модуль обращается к несуществующему идентификатору.

• Модификатор extern не дает компилятору искажать имена переменных или функций, и они становятся доступными исполняемым модулям, написанным на С, С++ или любом другом языке программирования

• Пользуйтесь этим модификатором только в коде на С++, но ни в коем случае не в коде на стандартном С.

Раздел экспорта

• Если перед переменной, прототипом функции или С++-классом указан модификатор __declspec(dllexport) , компилятор Microsoft С/С++ встраивает в конечный OBJ-файл дополнительную информацию. Она понадобится компоновщику при сборке DLL из OBJ-файлов.

• Обнаружив такую информацию, компоновщик создает LIB-файл со списком идентификаторов, экспортируемых из DLL Этот LIB-файл нужен при сборке любого ЕХЕ модуля, ссылающегося на такие идентификаторы .

• Компоновщик также вставляет в конечный DLL-файл таблицу экспортируемых идентификаторов - раздел экспорта,в котором содержится список (в алфавитном порядке) идентификаторов экспортируемых функций, переменных и классов. Туда же помещается относительный виртуальный адрес (relative virtual address, RVA) каждого идентификатора внутри DLL модуля.

Явная загрузка библиотеки

 

• В любой момент поток может спроецировать DLL на адресное пространство процесca, вызвав одну из двух функций:

HINSTANCE LoadLibrary(
PCTSTR pszDLLPathName
);
HINSTANCE LoadLibraryEx(
PCTSTR pszDLLPathName,
HANDLE hFile,
DWORD dwFlags
);

• Значение типа HINSTANCE, возвращаемое этими функциями, сообщает адрес виртуальной памяти, но которому спроецирован образ файла. Если спроецировать DLL на адресное пространство процесса не удалось, функции возвращают NULL Дополнительную информацию об ошибке можно получить вызовом GetLastError

Явная выгрузка DLL

• Если необходимость в DLL отпадает, ее можно выгрузить из адресного пространства процесса, вызвав функцию.

BOOL FreeLibrary(HINSTANCE hinstDll);

• Вы должны передать в FreeLibrary значение типа HINSTANCE, которое идентифицирует выгружаемую DLL. Это значение Вы получаете в результате вызова LoadLibrary(Ex).

Подсчет ссылок

• Операционная система хранит количество ссылок на каждый модуль

• В случае неоднократного вызова функции LoadLibrary(Ex) для загрузки одной и той же библиотеки переменная, хранящая количество ссылок для данного модуля, увеличивается на 1

• При вызове FreeLibrary переменная, содержащая количество ссылок, уменьшается на 1. Если количество ссылок становится 0, библиотека выгружается

Поиск функции

• Поток получает адрес экспортируемого идентификатора из явно загруженной DLL вызовом GetProcAddress:

FARPROC GetProcAddress(
HINSTANCE hinstDll,
PCSTR pszSymbolName
);

Пример:

FARPROC pfn = GetProcAddress(hinstDll, "SomeFuncInDll");

FARPROC pfn = GetProcAddress(hinstDll, MAKEINTRESOURCE(2));

Функция входа

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, PVOID fImpLoad)

{

switch (fdwReason)

{

case DLL_PROCESS_ATTACH:

// DLL проецируется на адресное пространство процесса

break;

case DLL_THREAD_ATTACH:

// создается поток

break;

case DLL_THREAD_DETACH:

// поток корректно завершается

break;

case DLL PROCESS_DETACH

// DLL отключается от адресного пространства процесса

break;

}

return(TRUE);

// используется только для DLL_PROCESS_ATTACH

}

• Параметр hinstDll содержит описатель экземпляра DLL. Это значение — виртуальный адрес проекции файла DLL на адресное пространство процесса.

• Параметр fdwReason сообщает о причине, по которой система вызвала эту функцию. Он принимает одно из четырех значений: DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, DLL_THREAD_ATTACH или DLL_THREAD_DETACH.

• Последний параметр, fImpLoad, отличен от 0, если DLL загружена неявно, и равен 0, если она загружена явно.

ВНИМАНИЕ!

• Не вызывайте из DllMain функции LoadLibrary(Ex) и FreeLibrary, так как это может привести к взаимной блокировке.

• Не используйте функции из других библиотек (кроме kernel32), так как их в памяти может не оказаться.

• Вышеперечисленные правила относятся и к конструкторам глобальных и статических объектов

TerminateProcess() & TerminateThread()

• Если процесс завершается в результате вызова TerminateProcess, система НЕвызывает DllMain со значением DLL_PROCESS_DETACH.

• А значит, ни одна DLL, спроецированная на адресное пространство процесса, не получит шанса на очистку до завершения процесса. Последствия могут быть плачевны – вплоть до потери данных.

• Аналагично с TerminateThread() - ни одна DLL, спроецированная на адресное пространство процесса, НЕ получит шанса на выполнение очистки до завершения потока

• Вызывайте TerminateProcess и TerminateThread() только в самом крайнем случае!

Динамически подключаемые библиотеки (DLL)

Динамически подключаемая библиотека(DLL, Dynamic Link Library) представляет собой программный модуль, который может быть загружен в виртуальную память процесса как статически, во время создания исполняемого модуля процесса, так и динамически, во время исполнения процесса операционной системой.

Мотивы использования DLL

• Расширение функциональности приложения.

• Возможность использования разных языков программирования.

• Более простое управление проектом.

• Экономия памяти.

• Разделение ресурсов.

• Упрощение локализации. Решение проблем, связанных с особенностями различных платформ.

• Реализация специфических возможностей.

Этапы создания DLL

• Прежде всего Вы должны подготовить заголовочный файл с прототипами функций, структурами и идентификаторами, экспортируемыми из DLL. Этот файл включается в исходный код всех модулей Вашей DLL. Как Вы потом увидите, этот же файл понадобится и при сборке исполняемого модуля (или модулей), который использует функции и переменные из Вашей DLL

• Вы пишете на С/С++ модуль (или модули) исходного кода с телами функций и определениями переменных, которые должны находиться в DLL. Так как эти модули исходного кода не нужны для сборки исполняемого модуля, они могут остаться коммерческой тайной компании-разработчика.

• Компилятор преобразует исходный код модулей DLL в OBJ-файлы (по одному на каждый модуль).

• Компоновщик собирает все OBJ-модули в единый загрузочный DLL-модуль, в который в конечном итоге помещаются двоичный код и переменные (глобальные и статические), относящиеся к данной DLL. Этот файл потребуется при компиляции исполняемого модуля

• Если компоновщик обнаружит, что DLL экспортирует хотя бы одну переменную или функцию, то создаст и LIB-файл. Этот файл совсем крошечный, поскольку в нем нет ничего, кроме списка символьных имен функций и переменных, экспортируемых из DLL. Этот LIB-файл тоже понадобится при создании ЕХЕ-файла.
Создав DLL, можно перейти к сборке исполняемого модуля.

• Во все модули исходного кода, где есть ссылки на внешние функции, переменные, структуры данных или идентификаторы, надо включить заголовочный файл, предоставленный разработчиком DLL.



Последнее изменение этой страницы: 2016-06-09

headinsider.info. Все права принадлежат авторам данных материалов.