Delphi как загрузить dll

Обновлено: 07.07.2024

Что такое DLL - знает, как минимум, большинство пользователей PC, тем более программисты, к которым Вы, скорее всего и относитесь, раз читаете эту статью. В этой статье я постараюсь пробежаться по всем общим вопросам, касающимся DLL.

Что конкретно мы рассмотрим:

  1. Как обычно, из области "Hello World", мы создадим свою первую DLL.
  2. Научимся пользоваться функциями этой DLL из своих программ.
  3. Научимся просматривать функции, которые экспортирует определенная DLL.
  4. Может, что нибудь еще.

Процесс создания DLL

  1. Запускаем Delphi (Я использую Delphi 6).
  2. Далее: File -> New ->Other

На закладке New дважды щелкаем по объекту DLL Wizard. Откроется новый проект. Сохраните его, например, с именем MyFirstDLL.

Чистый модуль имеет примерно такое содержание:

Теперь напишем всего лишь одну функцию, которая вызовет ShowMessage() из модуля Dialogs. Следовательно, перед началом оформления процедуры допишем в раздел Uses модуль Dialogs. Что, примерно, должно у вас получится:

Как видите, тут нет ничего очень сложного. Единственное скажу, что можно вызывать функции как по имени, так и по индексу (номеру), для этого надо писать так:

Использование функций DLL

Первый шаг сделан, дело за малым. Как нам использовать эту функцию?

Есть, как минимум, два способа загрузки:

Создаем новый проект, бросаем на форму одну кнопку и по событию OnClick этой кнопки и пишем следующее:

Но это еще не все! В разделе implementation проекта запишите:

Теперь рассмотрим способ с динамической загрузкой. Для этого метода используют функцию LoadLibrary( ), а в конце, для выгрузки - FreeLibrary( ).

Посмотрите на примере:

После успешной загрузки DLL функцией LoadLibrary( ), с помощью GetProcAddress( ) найдем адрес нашей функции, по которому и будем вызывать нашу процедуру из DLL. В конце, обязательно, надо сделать FreeLibrary( ). Это настолько важно, что весь код после успешной загрузки, вполть до FreeLibrary( ) я заключил в блок try finally. Это гарантирует выполнение FreeLibrary, даже если при выполнении действий внутри блока try except, возникнет непредвиденная ошибка в виде исключения (Exception).

Дело в том, что успешные вызовы LoadLibrary и FreeLibrary обязательно должны быть парными. И вот почему. Система, для каждой загружаемой процессом библиотеки, ведет внутри себя счетчик, который увеличивается на 1 при каждом успешном вызове LoadLibrary. Соответственно, при выполнени FreeLibrary, она уменьшает этот счетчик, и если он становится равным нулю, то это означает что данная библиотека более не нужна данному процессу, и ее можно смело удалить из памяти.

Если-же правило парности не соблюдать, то это может привести либо к преждевременной выгрузке (при лишнем FreeLibrary) библиотеки из памяти, либо к ее "застревании" там (при недостатке FreeLibrary).

При соблюдении же этого правила, можно не заботиться о возможной вложенности вызовов LoadLibrary / FreeLibrary.

Просмотр функций определенной DLL

Теперь посмотрим, как можно извлечь все имена функций из файлов PE формата, к которым и относится DLL. Структуру PE формата мы тут рассматривать не будем, следовательно, и исходник будет без пояснений.

Итак, создайте новый проект, бросьте на форму ListBox, в нём мы будем показывать имена функций.

Вот весь проект:

Если вы захотите сами разобраться в коде и у вас что-то не будет получаться, то на нашем форуме вам обязательно помогут, заходите!

Прицепляем наш Viewer ко всем DLL

У нас есть готовая DLL с функцией, есть просмотрщик функций. Осталось добавить некой функциональности для удобства дальнейшей работы. Давайте сделаем это. В проводнике открываем любую папку. Идем в Сервис -> Свойства папки. Переходим на закладку "Типы файлов". В списке ищем формат DLL. Если такого нет, то жмем кнопку "Создать" и в поле "Расширение" пишем - DLL. Жмем ОК. Находим созданный нами тип - DLL. Выделяем его и жмем "Дополнительно". Далее "Создать", в поле "Действии" пишем то, что будет отображаться в контекстном меню, например DLL Viewer. Через обзор ищем нашу программку.

Теперь при клике правой кнопкой мыши по файлу формата DLL в меню будет наш DLL Viewer. Выбираем его и смотрим все функции!

В связи с бурным развитием технологий программирования, все больше людей сталкиваются с проблемой наращивания возможностей своих программ. Данная статья посвящена именно этому вопросу, а именно - программирование DLL в Borland Delphi. Кроме того, так как мы затронем вопросы по использованию библиотек DLL, то попутно коснемся импортирования функций из чужих DLL (в том числе и системных, т.е. WinAPI).

Области применения DLL

Итак, зачем же нужны библиотеки DLL и где они используются. Перечислим лишь некоторые из областей их применения:

  • Отдельные библиотеки , содержащие полезные для программистов дополнительные функции. Например, функции для работы со строками, или же - сложные библиотеки для преобразования изображений.
  • Хранилища ресурсов . В DLL можно хранить не только программы и функции, но и всевозможные ресурсы - иконки, рисунки, строковые массивы, меню, и т.д.
  • Библиотеки поддержки . В качестве примера можно привести библиотеки таких известных пакетов, как: DirectX , ICQAPI (API для ICQ), OpenGL и т.д.
  • Части программы . Например, в DLL можно хранить окна программы (формы), и т.п.
  • Плагины (Plugins). - Вот где настоящий простор для мыслей программиста! Плагины - дополнения к программе, расширяющие ее возможности. Например, в этой статье мы рассмотрим теорию создания плагина для собственной программы.
  • Разделяемый ресурс . DLL ( Dynamic Link Library ) может быть использована сразу несколькими программами или процессами (т.н. sharing - разделяемый ресурс)

Краткое описание функций и приемов для работы с DLL

Итак, какие же приемы и функции необходимо использовать, чтобы работать с DLL? Разберем два метода импортирования функций из библиотеки:

1 способ . Привязка DLL к программе. Это наиболее простой и легкий метод для использования функций, импортируемых из DLL. Однако (и на это следует обратить внимание) этот способ имеет очень весомый недостаток - если библиотека, которую использует программа, не будет найдена, то программа просто не запустится, выдавая ошибку и сообщая о том, что ресурс DLL не найден. А поиск библиотеки будет вестись: в текущем каталоге, в каталоге программы, в каталоге WINDOWS\SYSTEM, и т.д.
Итак, для начала - общая форма этого приема:

implementation
.
function FunctionName(Par1: Par1Type; Par2: Par2Type; . ): ReturnType; stdcall ; external 'DLLNAME.DLL' name 'FunctionName' index FuncIndex;
// или (если не функция, а процедура):
procedure ProcedureName(Par1: Par1Type; Par2: Par2Type; . ); stdcall ; external 'DLLNAME.DLL' name 'ProcedureName' index ProcIndex;

Здесь: FunctionName (либо ProcedureName ) - имя функции (или процедуры), которое будет использоваться в Вашей программе;
Par1, Par2, . - имена параметров функции или процедуры;
Par1Type, Par2Type, . - типы параметров функции или процедуры (например, Integer );
ReturnType - тип возвращаемого значения (только для функции);
stdcall - директива, которая должна точно совпадать с используемой в самой DLL;
external 'DLLNAME.DLL' - директива, указывающая имя внешней DLL, из которой будет импортирована данная функция или процедура (в данном случае - DLLNAME.DLL );
name 'FunctionName' ('ProcedureName') - директива, указывающая точное имя функции в самой DLL. Это необязательная директива, которая позволяет использовать в программе функцию, имеющую название, отличное от истинного (которое она имеет в библиотеке);
index FunctionIndex (ProcedureIndex) - директива, указывающая порядковый номер функции или процедуры в DLL. Это также необязательная директива.

2 способ . Динамическая загрузка DLL. Это гораздо более сложный, но и более элегантный метод. Он лишен недостатка первого метода. Единственное, что неприятно - объем кода, необходимого для осуществления этого приема, причем сложность в том, что функция, импортируемая из DLL достуна лишь тогда, когда эта DLL загружена и находится в памяти. С примером можно ознакомиться ниже, а пока - краткое описание используемых этим методом функций WinAPI:

LoadLibrary (LibFileName: PChar ) - загрузка указанной библиотеки LibFileName в память. При успешном завершении функция возвращает дескриптор ( THandle ) DLL в памяти.
GetProcAddress (Module: THandle ; ProcName: PChar ) - считывает адpес экспоpтиpованной библиотечной функции. При успешном завершении функция возвращает дескриптор ( TFarProc ) функции в загруженной DLL.
FreeLibrary (LibModule: THandle ) - делает недействительным LibModule и освобождает связанную с ним память. Следует заметить, что после вызова этой процедуры функции данной библиотеки больше недоступны.

Практика и примеры

Ну а теперь пора привести пару примеров использования вышеперечисленных методов и приемов:

Пример 1. Привязка DLL к программе

function GetSimpleText(LangRus: Boolean): PChar; stdcall; external 'MYDLL.DLL';

Теперь то же самое, но вторым способом - с динамической загрузкой:

Пример 2. Динамическая загрузка DLL

var
Form1: TForm1;
GetSimpleText: function (LangRus: Boolean): PChar;
LibHandle: THandle;

ПРИМЕЧАНИЕ : Следует воздерживаться от использования типа string в библиотечных функциях, т.к. при его использовании существуют проблемы с "разделением памяти". Подробней об этом можно прочитать (правда, на английском) в тексте пустого проекта DLL, который создает Delphi (File -> New -> DLL). Так что лучше используйте PChar, а затем при необходимости конвертируйте его в string функцией StrPas.

Ну а теперь разберем непосредственно саму библиотеку DLL:

Пример 3. Исходник проекта MYDLL.DPR

library mydll;

uses SysUtils, Classes;

Размещение в DLL ресурсов и форм

В DLL можно размещать не только функции, но и курсоры, рисунки, иконки, меню, текстовые строки. На этом мы останавливаться не будем. Замечу лишь, что для загрузки ресурса нужно загрузить DLL, а затем, получив ее дескриптор, - загружать сам ресурс соотвествующей функцией (LoadIcon, LoadCursor, и т.д.). В этом разделе мы лишь немного затронем размещение в библиотеках DLL окон приложения (т.е. форм в Дельфи).

Для этого нужно создать новую DLL и добавить в нее новую форму (File -> New -> DLL, а затем - File -> New Form). Далее, если форма представляет собой диалоговое окно (модальную форму (bsDialog)), то добавляем в DLL следующую функцию (допустим, форма называется Form1, а ее класс - TForm1):

Пример 4. Размещение формы в DLL

function ShowMyDialog(Msg: PChar): Boolean; stdcall ;

Если же нужно разместить в DLL немодальную форму, то необходимо сделать две функции - открытия и закрытия формы. При этом нужно заставить DLL запомнить дескриптор этой формы.

Создание плагинов

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

Т.е., например, чтобы создать плагин к графическому редактору, который бы выполнял преобразование изображений, Вам нужно предусмотреть как минимум две функции в плагине (и, соответственно, вызвать эти функции в программе) - функция, которая бы возвращала имя плагина (и/или его тип), чтобы добавить этот плагин в меню (или в тулбар), плюс главная функция - передачи и приема изображения. Т.е. сначала программа ищет плагины, потом для каждого найденного вызывает его опозновательную функцию со строго определенным именем (например, GetPluginName) и добавляет нужный пункт в меню, затем, если пользователь выбрал этот пункт - вызывает вторую функцию, которой передает входное изображение (либо имя файла, содержащего это изображение), а эта функция, в свою очередь, обрабатывает изображение и возвращает его в новом виде (или имя файла с новым изображением). Вот и вся сущность плагина. :-)

что первую часть статьи они могут посмотреть в архиве рассылки, выпуск номер 13.

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

вам необходимо загрузить DLL в оперативную память. Загрузка библиотеки может быть осуществлена

одним из двух способов: статическая загрузка и динамическая загрузка.

Оба метода имеют как преимущества, так и недостатки.

Статическая загрузка означает, что динамическая библиотека загружается автоматически

при запуске на выполнение использующего ее приложения. Для того чтобы использовать такой способ загрузки,

вам необходимо воспользоваться ключевым словом external при описании экспортируемой из

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

и Вы сможете использовать любые экспортируемые из нее подпрограммы точно так же,

как если бы они были описаны внутри модулей приложения.

Это наиболее легкий способ использования кода, помещенного в DLL .

Недостаток метода заключается в том, что если файл библиотеки, на который

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

Смысл динамического метода заключается в том, что вы загружаете библиотеку не при старте приложения,

а в тот момент, когда вам это действительно необходимо. Сами посудите, ведь если функция, описанная

в динамической библиотеке, используется только при 10% запусков программы, то совершенно нет

смысла использовать статический метод загрузки. Выгрузка библиотеки из памяти в данном случае

также осуществляется под вашим контролем. Еще одно преимущества такого способа

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

А какие же у этого способа имеются недостатки? Основной, как мне кажется, - это то, что использование

данного метода является более хлопотным, чем рассмотренная выше статическая загрузка.

Сначала вам необходимо воспользоваться функцией Windows API LoadLibrary .

Для получения указателя на экспортируемой процедуры или функции должна

использоваться функция GetProcAddress. После завершения использования библиотеки DLL

должна быть выгружена с применением FreeLibrary.

Вызов процедур и функций, загруженных из DLL.

Способ вызова процедур и функций зависит от того, каким образом вы загрузили динамическую библиотеку,

в которой эти подпрограммы находятся.

Вызов функций и процедур из статически загруженных DLL достаточно прост. Первоначально в приложении

должно содержаться описание экспортируемой функции (процедуры). После этого вы можете их использовать

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

Для импорта функции или процедуры, содержащейся в DLL , необходимо использовать

модификатор external в их объявлении. К примеру, для рассмотренной нами выше процедуры HelloWorld

в вызывающем приложении должна быть помещена следующая строка:

procedure SayHello(AForm : TForm); external myfirstdll.dll';

Ключевое слово external сообщает компилятору, что данная процедура может быть найдена в

динамической библиотеке (в нашем случае - myfirstdll.dll).

Далее вызов этой процедуры выглядит следующим образом:

При импорте функции и процедур будьте особенно внимательны при написании их имен и интерфейсов!

Дело в том, что в процессе компиляции приложения не производится проверки на правильность имен объектов,

экспортируемых из DLL, осуществляться не будет, и если вы неправильно описали какую-нибудь функцию,

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

Импорт из DLL может проводиться по имени процедуры (функции), порядковому номеру или

с присвоением другого имени.

В первом случае вы просто объявляете имя процедуры и библиотеку, из которой ее импортируете

(мы это рассмотрели чуть выше). Импорт по порядковому номеру требует от вас указание этого самого номера:

procedure HelloWorld(AForm : TForm); external myfirstdll.dll index 15 ;

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

которое было указано для нее в самой DLL. Т.е. приведенная выше запись означает,

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

пятнадцатой, и при этом в рамках вашего приложения этой процедуре дается имя SayHello.

Если вы по каким-то причинам не применяете описанный выше способ импорта,

но тем не менее хотите изменить имя импортируемой функции (процедуры), то можно воспользоваться третьим методом:

procedure CoolProcedure; external myfirstdll.dll name 'DoSomethingReallyCool' ;

Здесь импортируемой процедуре CoolProcedure дается имя DoSomethingReallyCool.

Вызов процедур и функций, импортируемых из динамически загружаемых библиотек

несколько более сложен, чем рассмотренный нами выше способ. В данном случае требуется объявить

указатель на функцию или процедуру, которую вы собираетесь использовать.

Помните процедуру HelloWorld? Давайте посмотрим, что необходимо сделать для того,

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

необходимо объявить тип, который описывал бы эту процедуру:

THelloWorld = procedure (AForm : TForm);

Теперь вы должны загрузить динамическую библиотеку, с помощью GetProcAddress получить

указатель на процедуру, вызвать эту процедуру на выполнение, и, наконец, выгрузить DLL из памяти.

Ниже приведен код, демонстрирующий, как это можно сделать:

DLLInstance := LoadLibrary( 'myfirstdll.dll' );

@HelloWorld := GetProcAddress(DLLInstance, 'HelloWorld' );

Как уже говорилось выше, одним из недостатков статической загрузки DLL является невозможность

продолжения работы приложения при отсутствии одной или нескольких библиотек. В случае с динамической

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

вываливалась» самостоятельно. По возвращаемому функциями LoadLibrary и GetProcAddress значениям можно

определить, успешно ли прошла загрузка библиотеки и найдена ли в ней необходимая приложению процедура.

Приведенный ниже код демонстрирует это.

procedure TForm1.DynamicLoadBtnClick(Sender: TObject);

THelloWorld = procedure (AForm : TForm);

DLLInstance := LoadLibrary( 'myfirstdll.dll' );

if DLLInstance = 0 then begin

MessageDlg( 'Невозможно загрузить DLL' , mtError, [mbOK], 0 );

@HelloWorld := GetProcAddress(DLLInstance, 'HelloWorld' );

if @HelloWorld nil then

MessageDlg( 'Не найдена искомая процедура!.' , mtError, [mbOK], 0 );

В DLL можно хранить не только код, но и формы.

Причем создание и помещение форм в динамическую библиотеку не слишком сильно отличается от работы

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

содержащую формы, а затем мы поговорим об использовании технологии MDI в DLL.

Разработку DLL, содержащую форму, я продемонстрирую на примере.

Итак, во-первых, создадим новый проект динамической библиотеки.

Для этого выберем пункт меню File|New, а затем дважды щелкнем на иконку DLL .

После этого вы увидите примерно следующий код:

Сохраните полученный проект. Назовем его DllForms.dpr.

Теперь следует создать новую форму. Это можно сделать по-разному.

Например, выбрав пункт меню File|New Form. Добавьте на форму какие-нибудь компоненты.

Назовем форму DllForm и сохраним получившийся модуль под именем DllFormUnit.pas .

Вернемся к главному модулю проекта и поместим в него функцию ShowForm, в задачу которой будет входить

создание формы и ее вывод на экран. Используйте для этого приведенный ниже код.

function ShowForm : Integer; stdcall ;

Обращаю внимание, что для того, чтобы проект был скомпилирован без ошибок, необходимо добавить в секцию uses модуль Forms .

Экспортируем нашу функцию с использованием ключевого слова exports :

Компилируем проект и получаем файл dllforms.dll. Эти простые шаги - все,

что необходимо сделать для сОбратите внимание, что функция ShowForm объявлена с использованием ключевого слова stdcall .

Оно сигнализирует компилятору использовать при экспорте функции соглашение

по стандартному вызову (standard call calling convention). Экспорт функции таким образом создает

возможность использования разработанной DLL не только в приложениях, созданных в Delphi.

Соглашение по вызову (Calling conventions) определяет, каким образом передаются аргументы при вызове функции.

Существует пять основных соглашений: stdcall, cdecl, pascal, register и safecall.

Подробнее об этом можно узнать, посмотрев раздел " Calling Conventions " в файле помощи Delphi.

Также обратите внимание, что значение, возвращаемое функцией ShowForm ,

соответствует значению ShowModal. Таким образом вы можете передавать некоторую информацию

о состоянии формы вызывающему приложению.

Ниже представлено два листинга, первый из которых содержит полный код файла

проекта DLL (модуль с формой здесь не приводится), а второй - модуль вызывающего приложения,

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

DllFormUnit in 'DllFormUnit.pas' ;

function ShowForm : Integer; stdcall ;

Windows, Messages, SysUtils, Classes, Graphics,

Controls, Forms, Dialogs, StdCtrls;

TForm1 = class (TForm)

procedure Button1Click(Sender: TObject);

function ShowForm : Integer; stdcall ;

procedure TForm1.Button1Click(Sender: TObject);

Прошу заметить, что при экспорте функции также было использовано ключевое слово stdcall.

Следует обратить особое внимание на работу с дочерними формами в DLL. Если, к примеру,

в вызывающем приложении главная форма имеет значение свойства FormStyle, равным MDIForm,

в котором будет говориться, что нет ни одной активной MDI-формы.

В тот момент, когда вы пытаетесь показать ваше дочернее окно, VCL проверяет корректность

свойства FormStyle главной формы приложения. Однако в нашем случае все вроде бы верно.

Так в чем же дело? Проблема в том, что при проведении такой проверки, рассматривается объект Application,

принадлежащий не вызывающему приложению, а собственно динамической библиотеке.

Ну, и естественно, поскольку в DLL нет главной формы, проверка выдает ошибку.

Для того чтобы избежать такой ситуации, надо назначить объекту Application динамической библиотеки

объект Application вызывающего приложения. Естественно, это заработает только в том случае,

когда вызывающая программа - VCL-приложение. Кроме того, перед выгрузкой библиотеки из памяти

необходимо вернуть значение объекта Application библиотеки в первоначальное состояние.

Это позволит менеджеру памяти очистить оперативную память, занимаемую библиотекой.

Следовательно, вам нужно сохранить указатель на «родной» для библиотеки объект Application

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

Итак, вернемся немного назад и перечислим шаги, необходимые нам для работы с помещенным

в DLL MDIChild-формами.

В динамической библиотеке создаем глобальную переменную типа TApplication.

Сохраняем указатель на объект Application DLL в глобальной переменной.

Объекту Application динамической библиотеки ставим в соответствие указатель на Application

Создаем MDIChild-форму и работаем с ней.

Возвращаем в первоначальное состояние значение объекта Application динамической библиотеки

и выгружаем DLL из памяти.

Первый шаг прост. Просто помещаем следующий код в верхней части модуля DLL:

Затем создаем процедуру, которая будет изменять значение объекта Application и создавать дочернюю форму.

Процедура может выглядеть примерно так:

procedure ShowMDIChild(MainApp : TApplication);

if not Assigned(DllApp) then begin

Все, что нам теперь необходимо сделать, - это предусмотреть возвращение значения объекта Application

в исходное состояние. Делаем это с помощью процедуры MyDllProc:

procedure MyDLLProc(Reason: Integer);

if Reason = DLL_PROCESS_DETACH then

if Assigned(DllApp) then

Использование динамически подключаемых библиотек не так сложно, как это может показаться на первый взгляд.

DLL предоставляют широчайшие возможности для оптимизации работы приложений,

а также работы самих программистов. Используйте DLL и, возможно, ваша жизнь станет легче!

Использование динамически подключаемых библиотек не так сложно, как это может показаться на первый взгляд.

DLL предоставляют широчайшие возможности для оптимизации работы приложений,

а также работы самих программистов. Используйте DLL и, возможно, ваша жизнь станет легче!

Добавить комментарий

Не использовать не нормативную лексику.

Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.

В данном разделе будет показано, как в DLL библиотеке создать форму во время выполнения и как создать форму в режиме конструктора и подключения ее к DLL библиотеке.

Динамическая загрузка, которая известна также как динамическое подключение во время выполнения (runtime dynamic linking), является более универсальным и более сложным способом загрузтси библиотек DLL. Динамическое подключение позволяет загружать и выгружать библиотеку DLL всякий раз, когда это необходимо, не создавая при этом модуль импорта.

Чтобы динамически загрузить DLL, потребуется вылолнить следующие действия:

  1. Объявить процедурную переменную или процедурный тип, описывающий подпрограмму, которую вы хотите вызвать.
  2. Вызвать функцию LoadLibrary для загрузки библиотеки DLL.
  3. Вызвать функцию GetProcAddress для получения указателя на подпрограмму в библиотеке DLL.
  4. Вызвать подпрограмму.
  5. Вызвать функцию FreeLibrary для выгрузки библиотеки DLL.

Теперь мы попробуем создать новую библиотеку DLL, которая будет содержать формы VCL и экспортировать перегруженные подпрограммы. Для начала создайте новую библиотеку DLL (назовите се, скажем, FormLib, хотя делать это необязательно), а затем создайте и экспортируйте подпрограмму, которая будет динамически генерировать и отображать пустую форму. Вспомните, что вы должны добавить модуль Forms в список uses библиотеки DLL, чтобы иметь возможность работать с формами VCL.

Листинг 1. Подпрограмма из библиотеки DLL, которая создает и отображает пустую форму

Теперь, когда у вас есть библиотека DLL, добавьте новый проект VCL Forms в проектную группу и поместите на форму кнопку.



Для динамической загрузки файла FormLib.dll мы воспользуемся обработчиком события OnClick кнопки.

Первое, что потребуется сделать — это объявить процедурную переменную, имеющую тот же список параметров, что и подпрограмма, которую ны хотите вызвать. Поскольку подпрограмма ShowDLLForrm в файле FormLib.dll не имеет параметров, вы должны объявить переменную процедурного типа. Ниже показано объявление процедурной переменной, которую мы собираемся использовать:

Чтобы загрузить библиотеку DLL, вы должны вызвать функцию LoadLibrary и передать имя файла библиотеки DLL в качестве параметра lpLibFileName. Если функция будет выполнена успешно, она вернет дескриптор библиотеки DLL. Вы должны сохранить результат выполнения этой функции, поскольку выгрузить библиотеку DLL или найти в ней подпрограммы без ее дескриптора невозможно. Далее показан фрагмент кода для вызова функции LoadLibrary:

Прежде чем продолжить работу далее, давайте напишем блок trу-finally, код которого будет отвечать за выгрузку библиотеки DLL даже в случае возникновения ошибок. Чтобы выгрузить библиотеку DLL из оперативной памяти, вызовите функцию FreeLibrary и передайте дескриптор библиотеки DLL в качестве параметра hLibModule;

Единственное, что нам осталось сделать — это вызвать функцию GetProccAddress в блоке try, чтобы получить адрес подпрограммы ShowDLLForm. Функция GetProcAddress принимает два параметра: дескриптор DLL и имя подпрограммы, которую вы хотите найти.

Прежде чем вызывать подпрограмму, на которую указывает переменная DLLRoutine, вы должны проверить, является ли действительным адрес, на который указывает переменная DLLRoutine, так как функция GetProcAddress вернет значение nil, если не сможет найти указанную подпрограмму в библиотеке DLL.

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

Листинг 2. Вызов процедуры из динамически загружаемой библиотеки DLL

Теперь я покажу, как в текущем проекте создать форму в режиме конструктора, присоединить ее к DLL файлу и вызвать из главной формы.

В окне Project Manager правой кнопкой мыши щелкнуть по FormLib.dll и вполнить команды AddNew, Form.


На созданной форме расположить две метки Label1 и Label2.


В Label1 будет выводиться название DLL файла в котором находится форма Form2. В Label2 будет выводится названия EXE файла из которого вызывается DLL библиотека.

Изменения, которые необходимо внести в библиотеку FormLib показаны в листинге 3.

Листинг 3. Подключение библиотеки DLL к главному приложению, для получения названия EXE файла.

Чтобы получить имя EXE файла внутри DLL библиотеки, вы должны передать дескриптор Application.Handle (или дескриптор главной формы) вызывающего приложения библиотеке DLL, и присвоить его свойству Application.Handle библиотеки DLL до того, как создавать в ней форму.

Легко заметить, что код в листинге 3 демонстрирует также способ экспорта перегруженных подпрограмм. При экспорте перегруженной подпрограммы необходимо включить список ее параметров в список exports и с помощью директивы name изменить ее имя.

Чтобы вызвать перегруженную подпрограмму ShowDLLForm, которая принимает параметр THandle, вы должны создать другую процедурную переменную с тем же списком параметров и вызвать ее по имени, которое определяет директива name (в данном случае это ShowDLLFormEx]. Этот код представлен в листинге 4.

На главной форме расположить еще одну кнопку Button2.

Листинг 4. Правильный подход к отображению формы VCL, находящейся в библиотеке DLL.

Результат выполнения этого обновленного кода для отображения форм VCL, находящихся в библиотеке DLL, показан на рисунке ниже.


Используемая литература: Внутренний мир Borland Delphi 2006. Иван Хладни.

Читайте также: