Смотрим вглубь DLL в Delphi

Не раз приходилось получать письма с просьбой рассказать о создании и использовании 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.
Kwork.ru - услуги фрилансеров от 500 руб.
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (10 оценок, среднее: 5,00 из 5)
Загрузка...
Добавить комментарий