Сборник по задачам и примерам Assembler

       

Разработка текста 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-библиотеку путем передачи ей одного из четырех предопределенных значений — флагов. Значения этих флагов перечислены в файле winnt.h. Рассмотрим эти флаги и возможные действия при их поступлении в DLL-библиотеку.

  • DLLPR0CESSATTACH-1 — передается операционной системой DLL-библиотеке при проецировании последней в адресное пространство процесса. Передача этого флага DLL-библиотеке производится всего один раз, обычно при загрузке приложения, использующего данную DLL-библиотеку. Если позже другой поток процесса попытается загрузить эту же библиотеку, то система попросту увеличит ее счетчик использования без посылки флага DLLPROCESSATTACH. Получив данный флаг, DLL-библиотека должна выполнить действия по созданию необходимой среды функционирования для своих функций. Например, обеспечить их кучей.

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

    # DLL_THREAD_DETACH=3 — передается операционной системой DLL-библиотеке при выгрузке потоком DLL-библиотеки.

    # DLL_PROCESS_DETACH=0 — передается операционной системой DLL-библиотеке при выгрузке DLL-библиотеки из адресного пространства процесса. Логично, что при этом требуется провести завершающие действия по освобождению всех ресурсов, которыми владеет DLL-библиотека. Обычно эти действия являются обратными по отношению к предпринятым при инициализации библиотеки (см. флаг DLLPROCESSATTACH).

    Во-вторых, имя точки входа DLL-библиотеки может быть любым. Главное, чтобы при наличии кода инициализации это имя было указано в директиве END.

    В-третьих, оформление кода инициализации в виде отдельной процедуры необязательно. Главное, выполнить два основных действия кода инициализации DLL-библиотеки (при его наличии):

  • # вернуть единицу в регистре ЕАХ;

    # удалить из стека три параметра, которые передаются DLL-библиотеке при передаче описанных выше флагов: hlnstDLL — дескриптор DLL-библиотеки, назначенный ей системой при ее загрузке в адресное пространство процесса;



  • vent — значение флага, передаваемого в DLL-библиотеку; f ImpLoad — параметр не равен 0, если библиотека загружена неявно (см. ниже), и равен 0 в обратном случае.

  • Структура полного варианта инициализациониого кода выглядит так:
    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.

    Содержание раздела