Разработка DLL (курсовая работа) (545376), страница 2
Текст из файла (страница 2)
Позже мы займемся этим вопросом вплотную.Для чего используются DLL-библиотеки ?Прежде всего для экономии памяти, так как все запущенные приложения могут использоватьодин модуль DLL-библиотеки, не включая стандартные функции в состав своих модулей.С помощью DLL-библиотек можно организовать коллективное использование ресурсов илиданных, расположенных в сегменте данных библиотеки. Более того, вы можете создать DLLбиблиотеки, состоящие только из одних ресурсов, например, из пиктограмм или изображенийbitmap.
В состав Windows входит DLL-библиотека moricons.dll, состоящая из одних пиктограмм.Файлы с расширением fon представляют собой ни что иное, как DLL-библиотеки, содержащиешрифты в виде ресурса.Функции, входящие в состав DLL-библиотеки, могут заказывать блоки памяти с атрибутомGMEM_SHARE.
Такой блок памяти не принадлежит ни одному приложению и поэтому неосвобождается автоматически при завершении работы приложения. Так как в Windows версии 3.1все приложения используют общую глобальную память, блоки памяти с атрибутомGMEM_SHARE можно использовать для обмена данными между приложениями. Управлятьтаким обменом могут, например, функции, расположенные в соответствующей DLL-библиотеке.Однако в следующих версиях Windows каждое приложение будет работать в собственномадресном пространстве, поэтому для организации обмена данных между приложениями следуетиспользовать специальный механизм динамического обмена данными DDE, который мырассмотрим позже в отдельной главе.Использование DLL-библиотек повышает модульность приложений и самой операционнойсистемы Windows. С точки зрения приложения DLL-библиотека является не более чем наборомфункций с тем или иным интерфейсом, а также, возможно, набором ресурсов.
Внутреннее"устройство" и алгоритмы работы функций, а также используемые функциями структуры данныхполностью скрыты от приложения. Поэтому при внесении изменений или усовершенствований вDLL-библиотеки нет необходимости выполнять повторную сборку приложений (если неизменился интерфейс или набор функций, входящих в библиотеку).Область применения DLL-библиотек достаточно широка. Помимо предоставления приложениямфункций организации пользовательского интерфейса и реализации различных расширенийWindows типа мультимедиа или систем управления базами данных, DLL-библиотеки необходимыдля обеспечения ряда системных операций.Например, приложение может организовать "перехват" системных сообщений или функций, приэтом соответствующие модули "перехватчика" необходимо располагать в фиксированномсегменте кода DLL-библиотеки.
Для удаляемых (discardable) блоков памяти можно, вызвавфункцию GlobalNotify, определить функцию, которой будет передаваться управление припопытке удалить блок из памяти. Такая функция должна находиться в фиксированном сегментекода в DLL-библиотеке. Если ваше приложение обрабатывает аппаратные прерывания или самовызывает программные прерывания, ему также не обойтись без DLL-библиотек (единственныйспособ обработки прерываний в реальном времени - создание виртуального драйвера). Наконец,все обычные драйвера устройств в операционной системе Windows реализованы с помощью DLLбиблиотек.Даже если вы не собираетесь обрабатывать или вызывать прерывания и не разрабатываетесобственный драйвер, отдельные подсистемы большого приложения имеет смысл оформлять ввиде DLL-библиотек из соображений модульности и доступности библиотек для другихприложений.
Например, в приложении SMARTPAD мы создали орган управления Toolbar сиспользованием разработанного нами класса С++ Toolbar. Однако если бы мы сосредоточили всефункции этого класса в DLL-библиотеке, нашим органом управления могли бы воспользоваться идругие созданные нами приложения.Разумеется, у динамической компоновки есть и свои недостатки.Во-первых, DLL-библиотеки сложнее в разработке по сравнению с обычными библиотекамистатической компоновки. Приходится принимать во внимание неравенство содержимогорегистров DS и SS во время выполнения функций, расположенных в DLL-библиотеке, а также то,что DLL-библиотека имеет единственный сегмент данных, общий для всех приложений,вызывающих функции из библиотеки.Во-вторых, в дополнение к exe-файлу вместе с приложением необходимо устанавливать один илинесколько dll-файлов, что в некоторой степени усложняет процесс установки и сопровожденияприложения.
Может также возникнуть ситуация, при которой приложение не находит свою DLLбиблиотеку, несмотря на то, что нужная библиотека есть на диске.В-третьих, без тщательного планирования состава функций, включаемых в DLL-библиотеку,экономии памяти может и не получиться. В частности, возможна такая ситуация, когда иприложение, и DLL-библиотека пользуются одними и теми же функциями стандартнойбиблиотеки компилятора в варианте статической компоновки.3.3. Структура DLL-библиотекиDLL-библиотека состоит из нескольких специфических функций и произвольного наборафункций, выполняющих ту работу, для которой разрабатывалась данная библиотека.
Как мы ужеговорили, DLL-библиотека может иметь (а может и не иметь) сегмент данных и ресурсы.В заголовке загрузочного модуля DLL-библиотеки описаны экспортируемые точки входа,соответствующие всем или некоторым определенным в ней функциям. Приложения могутвызывать только те функции DLL-библиотеки, которые экспортируются ей.Функция LibEntryДо тех пор, пока ни одно из приложений не затребовало функцию из DLL-библиотеки, самабиблиотека находится в файле на диске. В оперативной памяти ее нет. Однако как толькоприложение затребует функцию из DLL-библиотеки или загрузит библиотеку в память явнымобразом, начнется процесс инициализации библиотеки.
Этот процесс выполняется только одинраз, так как в памяти может находиться только одна копия DLL-библиотеки.В процессе инициализации после загрузки библиотеки в память Windows вызывает функциюLibEntry , которая должна быть определена в каждой DLL-библиотеке. Можно считать, чтофункция LibEntry является точкой входа библиотеки, получающей управление при загрузкебиблиотеки в память.Задачей функции LibEntry является инициализация локальной области памяти, если онаопределена для DLL-библиотеки.Функция LibEntry должна быть дальней функцией, составленной на языке ассемблера, так как онаполучает параметры через регистры процессора. Перечислим и опишем параметры, передаваемыефункции LibEntry при загрузке DLL-библиотеки в память.РегистрыОписаниеDSСелектор, указывающий на сегмент данных DLL-библиотеки.Отметим, что хотя сразу после загрузки DLL-библиотека имеетсобственный сегмент данных, в нем не определена областьлокальных данных.
Для определения области локальных данныхфункция LibEntry должна вызвать функцию LocalInitDIИдентификатор DLL-библиотеки, присвоенный ей операционнойсистемой Windows после загрузки. По своему назначениюаналогичен идентификатору приложения hInstance, передаваемомуобычному приложению через соответствующий параметр функцииWinMain. Идентификатор DLL-библиотеки используетсяфункциями библиотеки при создании таких, например, объектов,как окна (если окно создается функцией, расположенной в DLLбиблиотеке, при его создании следует использовать неидентификатор приложения hInstance, вызвавшего функцию, аидентификатор DLL-библиотеки)CXТребуемый размер локальной области данных, указанный воператоре HEAPSIZE файла определения модуля DLL-библиотеки.При вызове функции инициализации локальной области памятиLocalInit в качестве значения параметра uStartAddr следуетиспользовать нуль, а в качестве значения параметра uEndAddr содержимое регистра CXES:SIДальний указатель на строку параметров, которая может бытьуказана при явной загрузке DLL-библиотеки (позже мы рассмотримразличные способы загрузки DLL-библиотеки).
Обычно этотпараметр не используетсяВам не надо определять функцию LibEntry самостоятельно, так как при создании файла DLLбиблиотеки редактор связей, входящий в систему разработки, включит уже имеющийся встандартной библиотеке модуль.
Этот стандартный модуль выполняет всю необходимую работупо инициализации локальной области памяти DLL-библиотеки (с помощью функции LocalInit) изатем вызывает функцию LibMain, которая будет описана в следующем разделе.Иногда для DLL-библиотеки может потребоваться нестандартная инициализация. Только в этомслучае вам придется разработать функцию LibEntry самостоятельно. За основу вы можете взятьфайл libentry.asm из SDK, который содержит исходный текст упомянутой выше функции.Функция LibMainФункция LibMain должна присутствовать в каждой стандартной DLL-библиотеке. Эту функциювам надо определить самостоятельно, причем вы можете воспользоваться языкомпрограммирования С.По своему назначению функция LibMain напоминает функцию WinMain обычного приложенияWindows. Функция WinMain получает управление при запуске приложения, а функция LibMain при загрузке DLL-библиотеки в память.
Так же как и функция WinMain, функция LibMain имеетпараметры, которые можно использовать для инициализации библиотеки.Приведем прототип функции LibMain :int FAR PASCAL LibMain(HINSTANCE hInstance,WORD wDataSegment,WORD wHeapSize,LPSTR lpszCmdLine);Параметр hInstance при вызове функции содержит идентификатор DLL-библиотеки. Это ни чтоиное, как содержимое регистра DI перед вызовом функции LibEntry.Через параметр wDataSegment передается селектор, соответствующий сегменту данных DLLбиблиотеки. Если DLL-библиотека не имеет сегмента данных, этот параметр содержитидентификатор DLL-библиотеки (точнее, идентификатор модуля DLL-библиотеки).
Значениепараметра wDataSegment соответствует содержимому регистра DS перед вызовом функцииLibEntry.Параметр wHeapSize содержит размер в байтах локальной области данных DLL-библиотеки. ЕслиDLL-библиотека не имеет сегмента данных, в этом параметре находится нулевое значение.И, наконец, через параметр lpszCmdLine передается указатель на командную строку, которуюможно передать DLL-библиотеке при ее явной загрузке в память.Если инициализация DLL-библиотеки выполнена успешно, функция LibMain должна возвратитьненулевое значение. Если в процессе инициализации произошла ошибка, следует возвратить нуль.В этом случае функция LibEntry также возвратит нуль.
Это приведет к тому, что Windowsвыгрузит библиотеку из памяти.Разрабатывая процедуру инициализации DLL-библиотеки, учтите, что функция LibMainвызывается только один раз, во время загрузки библиотеки в память. Не следует думать, что длякаждого приложения, использующего одну и ту же DLL-библиотеку, будет вызвана функцияLibMain. При попытке повторной загрузки уже загруженной ранее DLL-библиотеки будетувеличено содержимое счетчика использования библиотеки, но и только.Если для каждого приложения в локальной области данных DLL-библиотеки необходимовыделять отдельные структуры данных, вам придется разработать собственную процедурурегистрации приложения.Например, можно создать специальную функцию регистрации LibRegister, которая возвращаетприложению указатель на выделенную для него в локальной области структуру данных.