Загрузка DLL в delphi приложении. Создание и использование DLL в Delphi (привязка DLL к программе, динамическая загрузка) Исключительные ситуации в DLL

Работа с DLL библиотеками

DLL- Dynamic Link Library иначе динамически подключаемая библиотека, которая позволяет многократно применять одни и те же функции в разных программах. На самом деле довольно удобное средство, тем более что однажды написанная библиотека может использоваться во многих программах. В сегодняшнем уроке мы научимся работать с dll и конечно же создавать их!

Ну что ж начнём!

Для начала создадим нашу первую Dynamic Link Library! Отправляемся в Delphi и сразу же лезем в меню File -> New ->Other.

Выбираем в списке Dynamic-Link Library (в версиях младше 2009 Delphi пункт называется DLL Wizard).

В результате у нас появляется лишь окно с кодом, заметьте никакой формы у нас здесь нет!

Теперь начинается самое интересное. Напишем наши первые процедуры в библиотеке.

library Project2; //Вы, наверное уже заметили, что вместо program //при создании dll используется слово library. //Означающее библиотека. uses SysUtils, dialogs, Classes; // Внимание! Не забудьте указать эти модули, // иначе код работать не будет {$R *.res} {В ЭТУ ЧАСТЬ ПОМЕЩАЕТСЯ КОД DLL} Procedure FirstCall; stdcall; export; //Stdcall – При этом операторе параметры помещаются в стек //справа налево, и выравниваются на стандартное значение //Экспорт в принципе можно опустить, используется для уточнения //экспорта процедуры или функции. Begin ShowMessage("Моя первая процедура в dll"); //Вызываем сообщение на экран End; Procedure DoubleCall; stdcall; export; Begin ShowMessage(" Моявтораяпроцедура"); //Вызываем сообщение на экран End; Exports FirstCall, DoubleCall; //В Exports содержится список экспортируемых элементов. //Которые в дальнейшем будут импортироваться какой-нибудь программой. begin End.

На этом мы пока остановимся т.к. для простого примера этого будет вполне достаточно. Сейчас сохраняем наш проект, лично я сохранил его под именем Project2.dll и нажимаем комбинацию клавиш CTRL+F9 для компиляции библиотеки. В папке, куда вы сохранили dpr файл обязан появится файл с расширением dll, эта и есть наша только что созданная библиотека. У меня она называется Project2.dll

Займёмся теперь вызовом процедур из данной библиотеки. Создаём по стандартной схеме новое приложение. Перед нами ничего необычного просто форма. Сохраняем новое приложение в какую-нибудь папку. И в эту же папку копируем только что созданную dll библиотеку. Т.е. в данном примере Project2.dll

Теперь вам предстоит выбирать, каким способом вызывать функции из библиотеки. Всего существует два метода вызова.

Способ № 1

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

Идеально подходит для работы только с одной библиотекой.

Ну что поехали...

После ключевого слова implementation прописываем следующий код:

Здесь, как вы уже наверное догадались, мы сообщаем программе названия наших процедур и говорим, что они находятся в dll библиотеке, в моем случае с именем Project2.dll

Теперь для того, что бы вызвать данные процедуры нам необходимо лишь вставить их названия в любое место кода, что мы сейчас и сделаем. Кидаем на форму 2 компонента Button с закладки Standart и создаем на каждой обработчик событий OnClick

OnClick первойкнопки:

OnClick второйкнопки:

Вот и все!

Способ № 2:

Сложнее чем первый, но у него есть свои плюсы, а самое главное, что он идеально подходит для плагинов.

Для применения данного метода, первым делом объявляем несколько глобальных переменных:

Затем после ключевого слова implementation напишем процедуру которая будет загружать нашу библиотеку:

Procedure LoadMyLibrary(FileName: String); Begin LibHandle:= LoadLibrary(PWideChar(FileName)); //Загружаем библиотеку! // Внимание! PChar для версий ниже 2009 Delphi If LibHandle = 0 then begin MessageBox(0," Невозможнозагрузитьбиблиотеку",0,0); Exit; End; FirstCall:= GetProcAddress(LibHandle,"FirstCall"); //Получаем указатель на объект //1-ий параметр ссылка на модуль библиотеки //2-ой параметр имя объекта в dll DoubleCall:= GetProcAddress(LibHandle,"DoubleCall"); If @FirstCall = nil then begin //Проверяем на наличие этой функции в библиотеке. MessageBox(0,"Невозможно загрузить библиотеку",0,0); Exit; End; If @DoubleCall = nil then begin //Проверяем на наличие этой функции в библиотеке. MessageBox(0,"Невозможно загрузить библиотеку",0,0); Exit; End; End

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

Procedure TForm1.FormCreate(Sender: TObject); Begin LoadMyLibrary("Project2.dll"); End;

Теперь опять же, для того, что бы вызвать необходимые процедуры из нашей библиотеки нам необходимо лишь вставить их названия в любое место кода. Для этого кидаем на форму 2 компонента Button с закладки Standart и создаем на каждой обработчик событий OnClick

OnClick первойкнопки:

Procedure TForm1.Button1Click(Sender: TObject); Begin FirstCall; // Имя процедуры, которая находится в dll End;

OnClick второйкнопки:

Procedure TForm1.Button2Click(Sender: TObject); Begin DoubleCall; // Имя процедуры, которая находится в dll End;

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

Вот и все! Второй способ получился довольно громоздкий, но его плюс в уточнении хранящегося объекта в библиотеке.



Введение

В связи с бурным развитием технологий программирования, все больше людей сталкиваются с проблемой наращивания возможностей своих программ. Данная статья посвящена именно этому вопросу, а именно - программирование 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, и т.д. Итак, для начала - общая форма этого приема:


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


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


Примечание:

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

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

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


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

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

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

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


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

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

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

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

Эпилог

В этой статье отображены основные стороны использования и создания библиотек DLL в Borland Delphi. Если у Вас есть вопросы - скидывайте их мне на E-mail:

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

Хочется привести отрывок из «Уроков по Дельфи» о структуре DLL в памяти:

DLL - библиотека, в отличие от приложения не имеет ни стека, ни очереди сообщений. Функции, помещенные в DLL, выполняются в контексте вызвавшего приложения, пользуясь его стеком. Но эти же функции используют сегмент данных, принадлежащий библиотеке, а не копии приложения. В силу такой организации DLL, экономия памяти достигается за счет того, что все запущенные приложения используют один модуль DLL, не включая те или иные стандартные функции в состав своих модулей. Часто, в виде DLL создаются отдельные наборы функций, объединенные по тем или иным логическим признакам, аналогично тому, как концептуально происходит планирование модулей (в смысле unit) в Pascal. Отличие заключается в том, что функции из модулей Pascal компонуются статически - на этапе линковки, а функции из DLL компонуются динамически, то есть в run-time.

Создание DLL

Структура DLL мало чем отличается от обычной структуры модуля в Object Pascal. Начинаться DLL должна со слова Library, за которым помещается название библиотеки. Функции и процедуры, которые DLL будет предоставлять другим пользователям (экспортировать), перечисляются после директивы exports.

Для каждой процедуры или функции можно указать ее номер с помощью директивы Index. Если номер будет отсутствовать, то компилятор проведет автоматическую индексацию. Вместо номера процедуры можно использовать уникальное имя, которое задается с помощью директивы name. Если же не указано ни имени функции, ни ее номера, то Дельфи воспримет это как экспорт по имени, которое будет совпадать с названием функции.

Библиотека может содержать также и код инициализации, который будет выполнен при ее загрузке. Он помещается между begin и end. Вот общая структура DLL:

Library First_Dll; uses <используемые модули>; <объявления и описания функций> exports <экспортируемые функции> <описание процедур и функций> begin <инициализационная часть> end.

Приведу примеры описания экспортируемых функций в разделе exports:

Exports Function1 index 2; Fucntion2 name "My_sqr"; Function3;

Как Вы уже догадались, имя функции может не совпадать с именем для экспорта!!!

Использование DLL

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

Импортировать подпрограмму можно по ее имени и номеру. Поиск подпрограммы по номеру происходит быстрее, но всегда это удобно.

Ниже приведены примеры импорта функций из нашей First_DLL, которая была рассмотрена в примере:

{ импорт по специфицированному имени } Function ImportByName; external "First_DLL" name "My_sqr"; { импорт по индексу } Function ImportByOrdinal; external "First_DLL" index 2; { импорт по оригинальному имени } Function Fucntion3; external "First_DLL";

Мы рассмотрели статический метод использования DLL. Но в некоторых случаях заранее не известно какая именно потребуется библиотека, поэтому следует воспользоваться динамическим методом:

Uses WinTypes, WinProcs, ... ; type TMyProc = procedure ; var Handle: THandle; MyImportProc: TMyProc; begin Handle:=LoadLibrary("FirstDLL"); if Handle>=32 then { если <=32 - ошибка! } begin @MyImportProc:=GetProcAddress(Handle,"My_sqr"); if MyImportProc<>nil then ...... {здесь используем полученную функцию} end; FreeLibrary(Handle); end;

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

Нажмите в меню File -> New и выберите DLL. Сохраните готовый шаблон, как предлагается, под именем Project1.dpr.

Ниже приведен его полный код:

Library Project1; uses SysUtils,Classes; Function Function1(x,y:integer):integer; export; bgin result:=x+y; end; Function Function2(x,y:real):real; export; var t:real; begin t:=exp(y*ln(x)); result:=t; end; exports Function1 index 1, Function2 name "My_sqr"; begin end.

Здесь две функции, первая вычисляет сумму двух чисел и экспортируется по номеру, а вторая вычисляет x в степени y и экспортируется по имени.

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

Unit demo; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} function ImportSumm(x,y:integer):integer; external "Project1" index 1; //импорт по номеру function ImportSqr(x,y:real):real; external "Project1" name "My_sqr"; //импорт по имени procedure TForm1.Button1Click(Sender: TObject); var t:real; begin //Узнаем сколько же будет два в третьей степени t:=ImportSqr(2,3); Showmessage(FloatTostr(t)); end; procedure TForm1.Button2Click(Sender: TObject); var t:integer; begin //Узнаем сколько же будет 10+10 t:=ImportSumm(10,10); Showmessage(IntTostr(t)); end; end.

Программист Delphi, MySQL. Образование: высшее. Специальность: программное обеспечение информационных технологий.

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

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

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

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

Начнем с самого простого - написание своей первой DLL, которая будет содержать всего лишь одну функцию, которая выводит сообщение "Hello World".

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

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

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

Library MyFirstDLL; uses SysUtils, Classes; {$R *.res} begin end.

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

Library MyFirstDLL; uses Dialogs; procedure MyFirstFunc; stdcall; begin ShowMessage("Hello World"); end; exports MyFirstFunc; begin end.

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

Exports MyFirstFunc index 1;

Если что не понятно в этом коде, то все таки постарайтесь разобраться сначала сами. Думаю, что с этим проблем не будет... Но если что, то forum.! Идем дальше, как же можно теперь пользоваться этой (MyFirstFunc) функцией из других проектов?

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

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

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

  1. Статический
  2. Динамический

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

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

Procedure TForm1.Button1Click(Sender: TObject); begin MyProc(); end;

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

Implementation procedure MyProc(); stdcall; external "MyFirstDLL.dll" name "MyFirstFunc";

Готово! Компилируйте проект и жмите на кнопку! Если показалось ваше сообщение, то все ок!

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

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

Procedure TForm1.Button1Click(Sender: TObject); type TMyFunc = procedure; var DLLInstance: THandle; MyFunc: TMyFunc; begin DLLInstance:= LoadLibrary(PChar("MyFirstDLL.dll")); if (DLLInstance = 0) then begin MessageDlg("Невозможно загрузить DLL", mtError, , 0); Exit; end; try @MyFunc:= GetProcAddress(DLLInstance, "MyFirstFunc"); if Assigned(@MyFunc) then MyFunc() else MessageDlg("Не найдена искомая процедура!.", mtError, , 0); finally FreeLibrary(DLLInstance); end; end;

После успешной загрузки 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, в нём мы будем показывать имена функций.

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

Unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) lb: TListBox; procedure FormCreate(Sender: TObject); private { Private declarations } cmdline: String; ImageBase: DWord; DosHeader: PImageDosHeader; PeHeader: PImageNtHeaders; PExport: PImageExportDirectory; pname: PDWord; name: PChar; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); procedure FatalOsError; begin ShowMessage(SysErrorMessage(GetLastError())); Abort; end; Var i: Integer; begin try if (ParamCount() IMAGE_DOS_SIGNATURE) then FatalOsError; PEHeader:= PImageNtHeaders(DWord(ImageBase) + DWord(DosHeader^._lfanew)); if (PEHeader^.Signature IMAGE_NT_SIGNATURE) then FatalOsError; PExport:= PImageExportDirectory(ImageBase + DWord(PEHeader^.OptionalHeader.DataDirectory.VirtualAddress)); pname:= PDWord(ImageBase + DWord(PExport^.AddressOfNames)); For i:= 0 to PExport^.NumberOfNames - 1 do begin name:= PChar(PDWord(DWord(ImageBase) + PDword(pname)^)); lb.Items.Add(name); inc(pname); end; finally FreeLibrary(ImageBase); end; except Application.ShowMainForm:= False; Application.Terminate; end; end; end.

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

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

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

Все готово!

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

Это все, спасибо за внимание!

Чтобы создать новую библиотеку DLL в Delphi, выберите команду меню File > New > Other . В панели Items Categories окна New Items выберите узел Delphi Projects , после чего дважды щелкните на элементе Dynamic-link Library в правой панели окна.

Мастер DLL Wizard создает главный файл исходного кода библиотеки DLL, который выглядит практически так же, как и исходный код, сгенерированный для обычного приложения. Единственное отличие состоит в том. что этот файл начинается с зарезервированного слова library , а не program .

Library Project1; { Important note about DLL memory management: ShareMem must be the first unit in your library"s USES clause AND your project"s (select Project-View Source) USES clause if your DLL exports any procedures or functions that pass strings as parameters or function results. This applies to all strings passed to and from your DLL--even those that are nested in records and classes. ShareMem is the interface unit to the BORLNDMM.DLL shared memory manager, which must be deployed along with your DLL. To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters. } { Важное замечание относительно управления памятью библиотеки DLL: модуль ShareMem должен быть первым модулем в операторе uses вашей библиотеки и в операторе uses вашего проекта (выберите команду меню Project -> View Source (Проект -> Показать исходный код)), если ваша библиотека DLL экспортирует какие-либо процедуры или функции, передающие строки в качестве параметров или результатов выполнения функций. Это относится ко всем строкам, передаваемым или получаемым из вашей библиотеки DLL, и даже к тем строкам, которые вложены в записи и классы. Модуль ShareMem является модулем интерфейса для администратора общей памяти BORLNDMM.DLL, который вы должны развертывать вместе со своей библиотекой DLL. Чтобы не использовать BORLNDMM.DLL, передавайте строковую информацию с помощью параметров PChar или ShortString. } uses SysUtils, Classes; {$R *.res} begin end.

Все, что вам нужно сделать сейчас - это добавить подпрограмму перед блоком begin-end , вот и все. После этого вы получите внутреннюю подпрограмму, которую можно будет использовать в библиотеке DLL, но не во внешних приложениях. Если вы хотите вызывать подпрограмму из других приложении и других библиотек DLL, ее потребуетсл экспортировать. Чтобы экспортировать подпрограмму по имени, добавьте ее в список exports . Список exports имеет такой же синтаксис, что и список uses , за исключением того, что в списке exports любой элемент является подпрограммой, а не модулем.

Список exports обычно ставится непосредственно перед блоком begin-end . Взгляните на листинг 1, в котором представлен исходный код простой библиотеки FirstLib.dll , экспортирующей одну функцию.

Листинг 1. Простая библиотека DLL

Library FirstLib; function Max3(Num1, Num2, Num3: Integer): Integer; stdcall; begin Result:= Num1; if Num2 > Result then Result:= Num2; if Num3 > Result then Result:= Num3; end; { Экспортируем функцию Max3 } exports Max3; begin end.

Когда вы добавите подпрограмму в список exports , вы тем самым экспортируете подпрограмму по ее имени. Вы можете также экспортировать подпрограмму под другим именем с помощью директивы name или посредством порядкового значения, используя директиву index . Однако применять директиву index не рекомендуется.

Ниже показан пример экспортирования директивы посредством порядкового значения или под другим именем:

Exports Max3 name "MyMax3Function", SaySomething index 1;

Статическая загрузка является самым простым из двух возможных способов загрузки библиотеки DLL. Статическую загрузку называют еще динамическим подключением во время загрузки (load-time dynamic linking ), потому что используемые библиотеки DLL автоматически загружаются во время запуска приложения.

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

Когда вы импортируете подпрограммы из библиотеки DLL, вы должны помечать их директивой external , указывая вслед за ней имя библиотеки DLL, в которой содержится реализация подпрограммы. Далее показан пример импорта функции МахЗ из библиотеки FirstLib.dll :

Function Max3(Num1, Num2, Num3: Integer): Integer; stdcall; external "FirstLib.dll";

При желании можете даже переименовать подпрограмму во время ее импорта. Для этого необходимо объявить подпрограмму под другим именем, а в конце объявления указать первоначальное имя с помощью директивы name :

Function Max(Num1, Num2, Num3: Integer): Integer; stdcall; external "FirstLib.dll" name "Max3";

Вы можете также импортировать функцию из библиотеки DLL. Для этого нужно создать модуль импорта и написать стандартный заголовок подпрограммы в разделе interface , а ее внешнюю реализацию - в разделе implementation этого модуля. В листинге 2 показан полный код модуль импорта библиотеки FirstLib.dll .

Листинг 2. Модуль импорта библиотеки FirstLib.dll.

Unit FirstLibInf; interface function Max3(Num1, Num2, Num3: Integer): Integer; stdcall; implementation const FirstLib = "FirstLib.dll"; {Компилятору сообщается о том, что реализация функции Max3 находится в библиотеке FirstLib.dll} function Max3; external FirstLib; end.

После того как вы создадите библиотеку DLL и ее модуль импорта, протестируйте библиотеку DLL, чтобы убедиться в том. что подпрограмма работает нормально. Поскольку запустить саму библиотеку DLL нельзя, нужно создать тестовое приложение, которое будет обращаться к библиотеке DLL. Быстрее всего сделать это можно, если создать проектную группу, добавив новый проект в текущий проект. Для этого потребуется щелкнуть правой кнопкой мыши на элементе ProjectGroup1 в окне Project Manager (Диспетчер проекта) и выбрать в контекстном меню команду Add New Project , как показано на рис. 1.

Листинг 3. Проверка подпрограммы Max3, импортированной из библиотеки FirstLib.dll.

Unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, FirstLibInf, StdCtrls; type TMainForm = class(TForm) Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Label1: TLabel; Label2: TLabel; Label3: TLabel; Max3Button: TButton; procedure Max3ButtonClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.Max3ButtonClick(Sender: TObject); var LargestNumber: Integer; begin LargestNumber:= Max3(StrToInt(Edit1.Text), StrToInt(Edit2.Text), StrToInt(Edit3.Text)); MessageDlg(Format("Наибольшее число: %d.", ), mtInformation, , 0); end; end.

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