Використання і створення DLL в Delphi, Delphi, Програмування, статті

Карих Микола, Майстри Delphi

Введення

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

Області застосування DLL

Отже, навіщо ж потрібні бібліотеки DLL і де вони використовуються? .. Перерахуємо лише деякі з областей їх застосування:

Короткий опис функцій і прийомів для роботи з 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 до програми


{… Тут йде заголовок файлу і визначення форми TForm1 і її примірника Form1}
implementation
{Визначаємо зовнішню бібліотечну функцію}
function GetSimpleText(LangRus: Boolean): PChar; stdcall; external ‘MYDLL.DLL’;
procedure Button1Click(Sender: TObject);
begin
  {І використовуємо її}
  ShowMessage(StrPas(GetSimpleText(True)));
  ShowMessage(StrPas(GetSimpleText(False)));
  {ShowMessage – показує діалогове вікно з вказаною написом; StrPas – перетворить рядок PChar в string}
end;

Тепер те ж саме, але другим способом – з динамічною завантаженням:






Приклад 2. Динамічне завантаження DLL


{… Тут йде заголовок файлу і визначення форми TForm1 і її примірника Form1}
var
      Form1: TForm1;
      GetSimpleText: function(LangRus: Boolean): PChar;
      LibHandle: THandle;
procedure Button1Click(Sender: TObject);
begin
  {“Чистимо” адреса функції від “бруду”}
  @GetSimpleText := nil;
  {Намагаємося завантажити бібліотеку}
  LibHandle := LoadLibrary(‘MYDLL.DLL’);
  {Якщо все OK}
  if LibHandle >= 32 then begin
    {… То намагаємося отримати адресу функції в бібліотеці}
    @GetSimpleText := GetProcAddress(LibHandle,’GetSimpleText’);
    {Якщо і тут все OK}
    if @GetSimpleText <> nil then
      {… То викликаємо цю функцію і показуємо результат}
      ShowMessage(StrPas(GetSimpleText(True)));
  end;
  {І не забуваємо звільнити пам’ять і вивантажити DLL}
  FreeLibrary(LibHandle);
end;

ПРИМІТКА: Слід утримуватися від використання типу string в бібліотечних функціях, тому що при його використанні існують проблеми з “поділом пам’яті “. Детальніше про це можна прочитати (правда, англійською) у тексті порожнього проекту DLL, який створює Delphi (File -> New -> DLL). Так що краще використовуйте PChar, а потім при необхідності конвертуйте його в string функцією StrPas.

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






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


library mydll;
uses SysUtils, Classes;
{Визначаємо функцію як stdcall}
function GetSimpleText(LangRus: Boolean): PChar; stdcall;
begin
  {В залежності від LangRus повертаємо російську (True) або англійську (False) фразу}
  if LangRus then
Result: = PChar (‘Здрастуй, світ!’)
  else
    Result := PChar(‘Hello, world!’);
end;
{Директива exports вказує, які функції будуть експортовані цієї DLL}
exports GetSimpleText;
begin
end.

Розміщення в 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;

exports ShowMyDialog;
function ShowMyDialog(Msg: PChar): Boolean;
begin
  {Створюємо екземпляр Form1 форми TForm1}
  Form1 := TForm1.Create(Application);
  {У Label1 виводимо Msg}
  Form1.Label1.Caption := StrPas(Msg);
  {Повертаємо True тільки якщо натиснута OK (ModalResult = mrOk)}
  Result := (Form1.ShowModal = mrOk);
  {Звільняємо пам’ять}
  Form1.Free;
end;

Якщо ж потрібно розмістити в DLL немодальному форму, то необхідно зробити дві функції – відкриття та закриття форми. При цьому потрібно змусити DLL запам’ятати дескриптор цієї форми.

Створення плагінів

Тут ми не будемо детально розглядати плагіни, тому що вже наведені вище приклади допоможуть Вам легко розібратися в левової частини програмування DLL. Нагадаю лише, що плагін – доповнення до програми, що розширює її можливості. При цьому сама програма обов’язково повинна передбачати наявність таких доповнень і дозволяти їм виконувати своє призначення.

Тобто, наприклад, щоб створити плагін до графічного редактору, який би виконував перетворення зображень, Вам потрібно передбачити як мінімум дві функції в плагіні (і, відповідно, викликати ці функції в програмі) – функція, яка б повертала ім’я плагина (і / або його тип), щоб додати цей плагін в меню (або в тулбар), плюс головна функція – передачі і прийому зображення. Тобто спочатку програма шукає плагіни, потім для кожного знайденого викликає його опозновательную функцію зі строго певним ім’ям (наприклад, GetPluginName) і додає потрібний пункт в меню, потім, якщо користувач вибрав цей пункт – викликає другу функцію, якої передає вхідне зображення (або ім’я файлу, що містить це зображення), а ця функція, в свою чергу, обробляє зображення і повертає його в новому вигляді (або ім’я файлу з новим зображенням). Ось і вся сутність плагіна … 🙂

Епілог

У цій статті відображені основні сторони використання і створення бібліотек DLL в Borland Delphi. Якщо у Вас є питання – скидайте їх мені на E-mail:
snick@mailru.com, А ще краще – пишіть в конференції цього сайту (Delphi. Загальні питання), щоб і інші користувачі змогли побачити Ваше питання і спробувати на нього відповісти!

  
Увага! Передрук даної статті або її частини без узгодження з автором. Якщо ви хочете мати цю статтю на своєму сайті або видати в друкованому вигляді, зв’яжіться з автором.
Автор статті:  Карих Микола

Стаття була написана для сайту: Дельфі.Вокруг та навколо.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*