Создание и использование динамических библиотек (DLL) в Delphi.

Здравствуйте уважаемые коллеги!

В данной статье я постараюсь ответить на вопросы: Что такое DLL? Для чего она нужна? И как создавать и использовать DLL при помощи Delphi.

Что такое DLL?

Dynamic Link Library или сокращенно DLL — это библиотека, которая содержит в себе набор данных или функций для использования в других программах.

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

  • Хранение ресурсов: иконки, звуки, курсоры и т.д. Экономим на размере исполняемых файлов объединяя ресурсы в единую библиотеку.
  • Размещение отдельных модулей программы и форм интерфейса. Получаем возможность частичного обновления приложения, а при динамическом подключении, возможно обновлять модули без перезапуска основной программы.
  • Использование в качестве плагинов (PlugIn). Предоставляем возможность расширять функционал приложения без переписывания основного кода программы.
  • Библиотека может использоваться на разных языках программирования в не зависимости от языка на котором она была написана.

Создание собственной библиотеки DLL.

Для создания библиотеки заходим в меню File -> Other и выбираем Delphi Projects -> Dynamic-link Library.
Откроется текст кода с заготовкой для создания библиотеки:

library Project1;

uses
  System.SysUtils,
  System.Classes;

{$R *.res}

begin
end.

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

Добавим иконку в библиотеку, которую будем использовать потом в основной программе. icon
Как добавлять ресурсы в проект подробно описано в этой статье.

Добавим форму «О программе».
Нажимаем File -> New -> VCL Form. Добавим текст и кнопку «ОК»: about

Добавим функцию, которая будет выводить стандартный MessageBox с вопросом, кнопками «Да», «Нет» и с результатом в виде True или False.

function YesNoDlg(const Question: PChar): boolean; stdcall;
begin
  Result := (MessageBox(0, Question, 'Подтверждение', MB_YESNO + MB_ICONQUESTION) = ID_YES);
end;

Обратите внимание на ключевое слово stdcall. Оно определяет как именно будут передаваться параметры и результаты при вызове функций. Более подробно можно почитать на странице wiki Соглашение о вызове.

Так же добавим процедуру, которая будет использоваться как плагин для основной программы. Которая будет добавлять кнопку «О программе» в основное меню программы для вызова созданного нами окна.
Код процедуры:

procedure PlugIn(Form: TForm); stdcall;
var
  i: integer;
  mi: TMenuItem;
begin
  for i := 0 to Form.ComponentCount - 1 do begin
    if (Form.Components[i].ClassName = 'TMenuItem') and (Form.Components[i].Name = 'miHelp') then begin
      mi := TMenuItem.Create(Form.Components[i]);
      mi.Caption := 'О программе';
      mi.OnClick := fmAbout.onAboutButtonClick;
      TMenuItem(Form.Components[i]).Add(mi);
      Exit;
    end;
  end;
end;

В процедуру в качестве параметра передается форма программы. Мы проверяем присутствует ли на ней пункт меню с именем «miHelp» и если меню нашлось, то добавляем в него свою кнопку.

Теперь укажем какие функции и процедуры можно использовать из нашей библиотеки.
Добавим строку:

exports PlugIn, YesNoDlg;

Скомпилируем функцию Project -> Build или с помощью горячей клавиши Shift+F9.
Если ошибок в коде нет, то в папке проекта должен появиться файл с расширением DLL.

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

В приложении создадим форму Main в которой добавим компонент TMainMenu со следующими кнопками: Программа -> Выход и Помощь. Для последнего зададим имя — miHelp:MainMenu

Перейдем к коду формы.

Функции из библиотеки DLL можно подключать двумя способами:
Статическим — библиотека подключается при запуске программы. Если библиотека или имя функции не найдено, то программа выдаст ошибку и не запустится.
Динамическим — библиотека подключается или непосредственно перед вызовом функции или по определенному событию.

Рассмотрим статический способ подключения:

...
type
  TfmMain = class(TForm)
    MainMenu: TMainMenu;
    miProgram: TMenuItem;
    miExit: TMenuItem;
    miHelp: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure miExitClick(Sender: TObject);
  private
  public
  end;

  procedure PlugIn(Form: TForm); stdcall; external 'SampleDLL.dll';
var
...

Ключевое слово external указывает на то, что данная функция подключается из внешней библиотеки.

На событие onCreate формы добавим вызов процедуры PlugIn:

procedure TfmMain.FormCreate(Sender: TObject);
begin
  PlugIn(Self);
end;

В качестве параметра Form передаем в процедуру текущую форму (ключевое слово Self).

При запуске программы у нас должен появится пункт «О программе» в разделе «Помощь» главного меню.

Переходим к динамическому способу подключения.

Нам понадобятся три функции WinApi:

LoadLibrary
Загружает библиотеку в память компьютера. В качестве результата возвращает указатель на библиотеку в памяти. В случае ошибки вернет 0.

LoadLibrary(
	lpLibFileName: LPCWSTR
): HMODULE;

lpLibFileName — Имя файла библиотеки.

GetProcAddress
Найти функцию в библиотеки по имени. Результатом будет указатель на функцию. Если функция не найдена, то вернет nil.

GetProcAddress(
	hModule: HMODULE; 
	lpProcName: LPCSTR
): FARPROC;

hModule — Указатель на загруженную библиотеку.
lpProcName — Имя функции.

FreeLibrary
Выгружает библиотеку из памяти компьютера. Результатом будет True в случае успешного выполнения и False в случае ошибки.

FreeLibrary(
	hLibModule: HMODULE
): BOOL;

hLibModule — Указатель на загруженную библиотеку.

Теперь с помощью динамического способа получим ресурсы, нашу иконку и на кнопку «Выход» добавим вызов функции YesNoDlg для подтверждения закрытия программы.
На то же событие onCreate добавляем код:

procedure TfmMain.FormCreate(Sender: TObject);
var
  DLLHandle: THandle;
begin
  PlugIn(Self);

  DLLHandle := LoadLibrary('SampleDLL.dll');
  if DLLHandle = 0 then
    raise Exception.Create('Не удалось подключить библиотеку "SampleDLL"!');
  try
    Self.Icon.LoadFromResourceName(DLLHandle, 'my_icon');
  finally
    FreeLibrary(DLLHandle);
  end;
end;

И на событие onClick пункта меню «Выход»:

procedure TfmMain.miExitClick(Sender: TObject);
var
  DLLHandle: THandle;
  Dlg: function(const Question: PChar): boolean; stdcall;
begin
  DLLHandle := LoadLibrary('SampleDLL.dll');
  if DLLHandle = 0 then
    raise Exception.Create('Не удалось подключить библиотеку "SampleDLL"!');
  try
    @Dlg := GetProcAddress(DLLHandle, 'YesNoDlg');
    if not Assigned(@Dlg) then
      raise Exception.Create('Функция с именем "YesNoDlg" не найдена в библиотеке "SampleDLL"!');
    if Dlg('Выйти из программы?') then
      Close;
  finally
    FreeLibrary(DLLHandle);
  end;
end;

Если вы все написали верно, то после запуска программы должна поменяться иконка формы, добавиться кнопка «О программе», при нажатии на которую будет показывать форма About и на нажатие кнопки выход программа будет запрашивать подтверждение: «Выйти из программы?».

Надеюсь вам будет полезен этот небольшой пример использования возможностей DLL библиотек.
Исходники проекта можно скачать тут.

Статья добавлена в Delphi. Добавить ссылку в закладки.

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *