Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
3 семестр, WinAPI, MFC.pdf
Скачиваний:
388
Добавлен:
15.06.2014
Размер:
6.17 Mб
Скачать

Функция DllMain и стандартная библиотека С

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

Например, в DLL есть глобальная переменная — экземпляр какого-то класса C++. Прежде чем DLL сможет безопасно ее использовать, для переменной нужно вызвать ее конструктор, а это работа для стартового кода.

При сборке DLL компоновщик встраивает в исполняемый файл адрес DLL-функции входа/выхода. Вы задаете этот адрес компоновщику ключом /ENTRY. Если у Вас компоновщик фирмы Microsoft и Вы указали ключ /DLL, компоновщик считает (по умолчанию), что входная функция называется _DllMainCRTStartup. Эта функция содержится в файле стандартной библиотеки и при компоновке статически связывается с Вашей DLL — даже если Вы используете DLL-версию стандартной библиотеки С.

Когда Ваша DLL проецируется на адресное пространство процесса, система на самом деле вызывает именно _DllMainCRTStartup, а не Вашу функцию DllMain.

Получив уведомление DLL_PROCESS_ATTACH, функция _DllMainCRTStartup

инициализирует стандартную библиотеку и конструирует все глобальные и статические С++ объекты. Закончив, _DllMainCRTStartup вызывает Вашу функцию DllMain.

Как только DLL получает уведомление DLL_PROCESS_DETACH, система вновь вызывает _DllMainCRTStartup, которая теперь обращается к Вашей функции DllMain, и, когда та вернет управление, _DllMainCRTStartup вызовет деструкторы для всех глобальных и статических С++ объектов. Функция _DllMainCRTStartup отвечает и за любую дополнительную инициализацию и очистку, связанную со стандартной библиотекой С (после того, как поступят уведомления DLL_THREAD_ATTACH и DLL_THREAD_DETACH).

Как говорилось выше, реализация в коде Вашей DLL-библиотеки функции DllMain

не обязательно. И если у Вас нет этой функции, стандартная библиотека С использует свою реализацию DllMain:

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID flmpLoad) { return(TRUE);

}

При сборке DLL компоновщик, не найдя в Ваших OBJ-файлах функции DllMain, подключает DllMain из стандартной библиотеки.

Функция LibEntry

До тех пор, пока ни одно из приложений не затребовало функцию из DLLбиблиотеки, сама библиотека находится в файле на диске. В оперативной памяти ее нет. Однако как только приложение затребует функцию из DLL-библиотеки или загрузит

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

В процессе инициализации после загрузки библиотеки в память Windows вызывает функцию LibEntry , которая должна быть определена в каждой DLL-библиотеке. Можно считать, что функция LibEntry является точкой входа библиотеки, получающей управление при загрузке библиотеки в память.

Задачей функции LibEntry является инициализация локальной области памяти, если она определена для DLL-библиотеки.

(!!!)Перечислим и опишем параметры, передаваемые функции LibEntry при загрузке DLL-

библиотеки в память.

Регистры

DS

DI

CX

ES:SI

Описание

Селектор, указывающий на сегмент данных DLL-библиотеки. Отметим, что хотя сразу после загрузки DLL-библиотека имеет собственный сегмент данных, в нем не определена область локальных данных. Для определения области локальных данных функция LibEntry должна вызвать функцию LocalInit

Идентификатор DLL-библиотеки, присвоенный ей операционной системой Windows после загрузки. По своему назначению аналогичен идентификатору приложения hInstance, передаваемому обычному приложению через соответствующий параметр функции WinMain. Идентификатор DLL-библиотеки используется функциями библиотеки при создании таких, например, объектов, как окна (если окно создается функцией, расположенной в DLLбиблиотеке, при его создании следует использовать не идентификатор приложения hInstance, вызвавшего функцию, а идентификатор DLL-библиотеки)

Требуемый размер локальной области данных, указанный в операторе HEAPSIZE файла определения модуля DLL-библиотеки. При вызове функции инициализации локальной области памяти LocalInit в качестве значения параметра uStartAddr следует использовать нуль, а в качестве значения параметра uEndAddr - содержимое регистра CX

Дальний указатель на строку параметров, которая может быть указана при явной загрузке DLL-библиотеки (позже мы рассмотрим различные способы загрузки DLL-библиотеки). Обычно этот параметр не используется

Не надо определять функцию LibEntry самостоятельно, так как при создании файла DLL-библиотеки редактор связей, входящий в систему разработки, включит уже имеющийся в стандартной библиотеке модуль.

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

Функция LibMain

Функция LibMain должна присутствовать в каждой стандартной DLL-библиотеке. Эту функцию вам надо определить самостоятельно, причем вы можете воспользоваться языком программирования С.

По своему назначению функция LibMain напоминает функцию WinMain обычного приложения Windows. Функция LibMain получает управление при загрузке DLL-

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

Приведем прототип функции LibMain :

int FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment,

WORD wHeapSize, LPSTR lpszCmdLine);

Параметр hInstance при вызове функции содержит идентификатор DLL-библиотеки. Через параметр wDataSegment передается селектор, соответствующий сегменту данных DLLбиблиотеки. Значение параметра wDataSegment соответствует содержимому регистра DS перед вызовом функции LibEntry. Параметр wHeapSize содержит размер в байтах локальной области данных DLL-библиотеки. Если DLL-библиотека не имеет сегмента данных, в этом параметре находится нулевое значение. Через параметр lpszCmdLine передается указатель на командную строку, которую можно передать DLL-библиотеке при ее явной загрузке в память.

Если инициализация DLL-библиотеки выполнена успешно, функция LibMain

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

Если функция LibMain заказывает блоки памяти из глобальной области данных, следует использовать флаг GMEM_SHARE. Такие блоки памяти будут принадлежать создавшей их DLL-библиотеке. Освобождение заказанных глобальных блоков памяти можно выполнить в функции WEP, получающей управление при выгрузке DLL-библиотеки из памяти.

Приведем пример простейшего варианта функции LibMain:

int FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment, WORD wHeapSize, LPSTR lpszCmdLine)

{

if(wHeapSize != 0) UnlockData(0); return 1;

}

Эта функция проверяет размер локальной области данных, и если эта область данных существует, она расфиксируется при помощи макрокоманды UnlockData. После этого возвращается признак успешной инициализации.

Функция WEP

Windows перед выгрузкой вызывает функцию WEP . Эта функция, как и функция

LibMain, вызывается только один раз. Она может быть использована для уничтожения структур данных и освобождения блоков памяти, заказанных при инициализации DLLбиблиотеки.

Приведем прототип функции WEP (при использовании системы разработки Borland Turbo C++ for Windows):

int FAR PASCAL WEP(int bSystemExit);

Параметр bSystemExit может принимать значения WEP_FREE_DLL и WEP_SYSTEM_EXIT

. Этот параметр указывает причину выгрузки DLL-библиотеки из памяти. Если же параметр имеет значение WEP_SYSTEM_EXIT, выгрузка библиотеки происходит из-за завершения работы операционной системы Windows.

Функция WEP должна всегда возвращать значение 1.

Функция WEP добавляется в DLL-библиотеку транслятором. Вы, однако, можете определить собственную функцию WEP, если перед выгрузкой DLL-библиотеки требуется освободить заказанные ранее блоки памяти.

Приведем пример простейшей функции WEP:

int FAR PASCAL WEP(int bSystemExit)

{

return 1;

}