Разработка текста DLL-библиотеки
Шаг 1. Разработка текста DLL-библиотеки
Как мы уже отметили, DLL-библиотека представляет собой обычную программу на языке ассемблера. Выбор примера для демонстрации разработки и использования DLL-библиотеки неслучаен. Тем самым мы подтвердим тезис о том, что обычная программа и DLL-библиотека имеют много общего. С точки зрения структуры DLL-библиотека является набором функций, переменных и констант, а также необязательного кода инициализации, которые оформлены в соответствии с требованиями ассемблера. Ниже приведен пример DLL-библиотеки для нашей задачи.
;maket_dll.asm - текст DLL-библиотеки. :Содержит одну функцию - WriteCon
locals
.model flat.STDCALL ;модель памяти flat.
Объявление внешними используемых в данной программе функций Win32 (ASCII):
:обьявление процедуры WriteCon общедоступной publicdll WriteCon
.data
.code
DllMainproc
arg №h I nst: dword. @@event: dword. @<ano_use: dword
@@m: moveax.l
ret
DllMainendp
WriteCon ргос :см. дискету и prg05_ll.asm из главы 5 arg@@adr_str:dword.@@len_str:dword
ret
endp WriteCon endDllMain
Хорошо видно, что DLL-библиотека является действительно обычным файлом ассемблера. Есть все, даже имя точки входа, указываемое в последней директиве END. Но здесь и начинаются странности. На самом деле это не обычная точка входа, которую мы привыкли указывать в любой программе на ассемблере, а адрес команды в DLL-библиотеке, получающей управление в строго определенных случаях. Эта команда является первой в цепочке команд, составляющих так называемый код инициализации DLL-библиотеки. Назначение этого кода — выполнить необходимые действия по инициализации DLL-библиотеки при наступлении определенных событий. Наличие этого кода в DLL-библиотеке необязательно, и при его отсутствии нет необходимости указывать соответствующую метку в заключительной директиве END. Если все же код инициализации присутствует в DLL-библиотеке, то он должен быть разработан с учетом определенных требований.
# DLL_THREAD_DETACH=3 — передается операционной системой DLL-библиотеке при выгрузке потоком DLL-библиотеки.
# DLL_PROCESS_DETACH=0 — передается операционной системой DLL-библиотеке при выгрузке DLL-библиотеки из адресного пространства процесса. Логично, что при этом требуется провести завершающие действия по освобождению всех ресурсов, которыми владеет DLL-библиотека. Обычно эти действия являются обратными по отношению к предпринятым при инициализации библиотеки (см. флаг DLLPROCESSATTACH).
Во-вторых, имя точки входа DLL-библиотеки может быть любым. Главное, чтобы при наличии кода инициализации это имя было указано в директиве END.
В-третьих, оформление кода инициализации в виде отдельной процедуры необязательно. Главное, выполнить два основных действия кода инициализации DLL-библиотеки (при его наличии):
# удалить из стека три параметра, которые передаются DLL-библиотеке при передаче описанных выше флагов: hlnstDLL — дескриптор DLL-библиотеки, назначенный ей системой при ее загрузке в адресное пространство процесса;
Структура полного варианта инициализациониого кода выглядит так:
includeWindowConA.inc;проверьте присутствие значений флагов в этом файле"
DllMain ргос
arg hlnstDLL:dword. event:dword,fImpLoad:dword
cmp [event].DLL_PROCESS_ATTACH
jne m выполняем действия для DLL_PROCESS_ATTACH
cmp [event].DLL_THREAD_ATTACH
jnem :выполняем действия для DLL_THREAD_ATTACH
cmp [event]. DLL_THREAD_DETACH
jnem выполняем действия для DLL_THREAD_DETACH
cmp [event].DLL_PROCESS_DETACH
jnem
выполняем действия для DLL_PROCESS_DETACH m: moveax.l
ret DllMainendp
Минимальный вариант может выглядеть так, как это сделано в нашем примере:
DllMain ргос
arg hlnstDLL:dword. event:dword,fImpLoad:dword
m: mov eax.l
ret DllMainendp
Или так:
DllMain: m: moveax.l ret 12
He забывайте, что директива arg приводит к тому, что в код, генерируемый транслятором, вставляются команды ENTERD и LEAVED (см. выше разделы «Реализация рекурсивных процедур» и «Реализация вложенных процедур»). Кроме этого, команда RET процедуры дополняется значением, равным сумме длин параметров, указанных в директиве ARG . Исполнение такой команды приводит к удалению из стека количества байт, равного этому сформированному значению.
Что касается кода функций (процедур), составляющих DLL-библиотеку, то для их написания используются обычные правила разработки программ. Описание данных также ничем не отличается от обычной программы ассемблера. Ведь в конечном итоге код и данные процедур DLL-библиотеки оказываются в адресном пространстве процесса наравне с его кодом и данными.
Последнее, что необходимо отметить, — все экземпляры данных и имена процедур, которые должны быть видны вне пределов DLL-библиотеки, объявляются общими с использованием одной из директив PUBLIC или PUBLICDLL.