Особенности разработки DLL

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

DLL не в состоянии поставлять в программу переменные, константы и типы. В результате DLL не могут экспортировать в программу объекты – для этого в Object PASCAL используются пакеты. DLL подключаются к программе в момент ее исполнения, т.е. динамически. Значительным преимуществом DLL является то, что если несколько программ используют одну и ту же DLL , в памяти будет лишь один экземпляр разделяемого программами кода. При необходимости DLL могут динамически загружаться и выгружаться из памяти программным путем.

Для создания DLL в Object Pascal введено зарезервированное слово Library, которым должен начинаться текст библиотеки. За словом Library следует ее идентификатор, но в отличие от модуля работа с DLL определяется именем DLL-файла, а не идентификатором, следующим за Library.

Структура текста DLL повторяет структуру обычной программы с тем исключением, что операторы блока реализации в DLL исполняются только один раз в момент загрузки библиотеки в память. Каждое очередное обращение с требованием загрузить библиотеку наращивает на единицу счетчик ссылок, но не приводит к выполнению операторов исполняемой части, что экономит ресурсы компьютера.

Сразу за заголовком списываемой в библиотеке экспортируемой процедуры или функции должно следовать зарезервированное слово Export, которое позволяет DLL- подпрограмме использовать сегмент стека вызвавшей ее программы.

В разделе описаний DLL могут объявляться типы (в том числе и классы), константы и переменные, но они остаются скрытыми от вызывающей программы и могут использоваться только внутри DLL. В разделе описаний помимо стандартных для обычной программы объявлений используется специальный раздел объявления экспортируемых подпрограмм. Этот раздел начинается зарезервированным словом Exports, за которым через запятую перечисляются имена и индексы экспортируемых подпрограмм, описанных до того и помеченных директивой Export:

Library Lib;

Function Funct1 (x : real) : Real; Export;

begin

end;

Procedure Proc1 ; Export;

begin

end;

Exports

Funct1 index 1, Proc1 index2;

begin

end.

Индекс присваивается процедуре автоматически по порядку ее появления в списках Exports: первая получает индекс 0, следующая 1 и т.д. Можно явно указать индекс добавив за ее именем в списке Exports слово index и целое число без знака в диапазоне от 1 до 32767. Можно определить внешнее имя экспортируемой процедуры отличное от ее идентификатора. Для этого в списке Exports добавляется слово name и внешнее имя в кавычках:

Exports

Funct1 index 1 name ‘myFunct’;

Внешние имена критичны к регистру букв.

Вызывающая программа может ссылаться или на имя экспортируемой подпрограммы, или на ее индекс. При вызове по имени программа просматривает имена в таблице имен в поисках нужного. Так как имена могут состоять из длинных наборов символов и самих имен в таблице может быть много, процесс поиска имени существенно медленнее, чем процесс поиска индекса. Поэтому желательно ссылаться не на имя, а на индекс. Затраты времени на поиск процедуры по имени можно заметно сократить, если указать в списке Exports слово resident:

Exports

Funct1 index 1 name ‘MyFunct’ resident;

Т.к. в этом случае информация о процедуре сохраняется в памяти во все время использования библиотеки.

Для использования процедур из DLL необходимо описать их как внешние, добавив за словом External имя библиотеки и если надо индекс или имя :

Procedure Proc1 ; External ‘MyDLL1’;

Function Func1 (x: real) : real; External ‘MyDLL1’ index1;

Function MyFunct (x:real): real ; External ‘MyDLL1’ name ‘Funct1’;

Описанный способ связывания DLL с основной программой называется статическим. При этом библиотеки загружаются в память одновременно с загрузкой самой программы.

Программа может загружать и высвобождать DLL (динамически) с помощью функций: LoadLibrary, GetProcAddress и FreeLibrary.

begin //Процедура с динамической загрузкой DLL

var : DLLHandler:LongInt;

Handler := LoadLibrary(‘MyDLL1.dll’);

if DLLHandler = 0 then

begin

ShowMessage (‘DLL не найдена’);

Halt(1);

end;

{ определяем адреса процедур функций от первой до третьей по индексу,четвертой– по имени. При вызове по индексу младшее слово PChar содержит индекс, делаем приведение типов: }

@ Funct1 := GetProcAddress (Handler, Pchar (LongInt(1))) ; //Настройка дескриптора DLL

@ SubC := GetProcAddress (Handler, Pchar (LongInt(2))) ;

@ MulC := GetProcAddress (Handler, Pchar (LongInt(3))) ;

@ Proc1 := GetProcAddress (Handler,’Proc1’) ;

FreeLibrary (DLLHandler) // Освобождаем библиотеку

end;