Розробка DLL в Borland Delphi h2>
Якщо
ваш комп'ютер працює під управлінням операційної системи Windows, то ви не
можете не знати про існування динамічних під'єднуваних бібліотек (dynamic
link libraries - DLL). Достатньо поглянути на список файлів, розташованих у
системному каталозі Windows. Часом кількість використовуваних операційної
системою динамічних бібліотек досягає декількох сотень. Таким чином, мені
здається, не варто заперечувати той факт, що DLL є невід'ємною частиною
функціонування операційних систем сімейства Microsoft Windows. Однак для
вас може бути неочевидна необхідність використання динамічних бібліотек
при розробці додатків. У межах цієї статті ми поговоримо про принципи
функціонування DLL і їх використання в процесі створення ваших власних
програм. p>
Для
Спершу давайте з'ясуємо, що собою представляє динамічна підключається
бібліотека. Отже, DLL - це один або кілька логічно закінчених фрагментів
коду, які зберігаються у файлі з розширенням. dll. Цей код може бути запущений на
виконання в процесі функціонування будь-якої іншої програми (такі
програми називаються викликають по відношенню до бібліотеки), але сама DLL НЕ
є виконуваний файл. p>
Існує
два типи динамічних бібліотек - виконуються і бібліотеки ресурсів. Однак це
не означає, що в одному файлі не може знаходитися і код деякої функції і
будь-які ресурси. Просто іноді буває зручно рознести реалізацію виконуваних
процедур і використовуються додатком ресурси в різні файли. p>
Отже,
процедури та функції, що містяться у динамічній бібліотеці, можна розділити на
два типи: ті, які можуть бути викликані з інших додатків. Розглянемо
наступний приклад: p>
Screen.Cursors [myCursor]
: = LoadCursor (HInstance, MYCURSOR'); p>
LoadCursor
- Функція Windows API, яка викликається додатком з динамічної
бібліотеки User 32.dll. До речі, прикладом збережених у динамічній бібліотеці
ресурсів можуть бути такі стандартні діалоги Windows, як діалог відкриття
файлу, діалог друку або настройки принтера. Ці діалоги знаходяться у файлі
Comctl32.dll. Однак багато прикладні розробники використовують функції виклику
форм цих діалогів, абсолютно не замислюючись, де зберігається їх опис. p>
Другий
тип процедур - це ті, які використовуються тільки всередині самого файлу
бібліотеки. p>
Аргументи на користь використання DLL h2>
Отже,
перш ніж перейти до обговорення структури динамічних бібліотек, необхідно
поговорити про ті переваги, які надає їх використання
розробнику. По-перше, це повторне використання коду. Думаю, ні
необхідності пояснювати зручність використання один раз розроблених процедур і
функцій при створенні декількох додатків? Крім того, надалі ви зможете
продати деякі зі своїх бібліотек, не розкриваючи вихідних кодів. А чому тоді
це краще компонентів, запитаєте ви? А тим, що функції, що зберігаються в
бібліотеці, можуть бути викликані на виконання з програм, розроблених не на
Object Pascal, а, наприклад, з використанням C + + Builder, Visual Basic, Visual
C + + і т.д. Такий підхід накладає деякі обмеження на принцип розробки
бібліотеки, але це можливо. Звучить заманливо? Мені здається, навіть дуже. Але це
ще не все. p>
По-друге,
використання DLL надає можливість використання один завантаженого в
оперативну пам'ять коду декількома додатками. Наприклад, якщо ви
розробляєте програмне забезпечення для великого підприємства, то цілком
можливо, що різних створених вами додатках будуть використовуватися одні
і ті ж функції, процедури, форми та інші ресурси. Природно, що при
виділення загальних для декількох додатків даних в DLL може призвести до
економії як дискового простору, так і оперативної пам'яті, іноді дуже
навіть істотного. p>
По-третє,
варто поговорити ось про що. Лише кілька років тому при розробці
програмного забезпечення ви могли абсолютно не хвилюватися щодо
розповсюдження ваших продуктів де-небудь, крім вашої країни. Я хочу сказати,
що проблема перекладу на інші мови тексту на елементи керування (пункти
меню, кнопки, списки, що випадають, підказки), повідомлень про помилки і т.д. НЕ
стояла так гостро, як зараз. Однак, з повсюдним впровадженням Інтернету у вас
з'явилася можливість битрой передачі готових програмних продуктів практично
в будь-яку точку світу. І що будуть робити з вашою програмою де-небудь у
Об'єднаних Арабських Еміратах, якщо крім як по-російськи, вона з користувачем
спілкуватися не вміє? Ви самі можете оцінити цей ефект, якщо хоч раз на екрані
вашого комп'ютера замість з дитинства знайомого російської мови з'являється
«Арабська в'язь» (наприклад, через «збій» шрифтів). Отже, вже зараз ви повинні
планувати можливість розповсюдження ваших програм в інших країнах
(якщо, звичайно, у вас є бажання отримати як можна більше прибутку).
Відповідно, постає питання швидкого перекладу інтерфейсу вашої програми на
інші мови. Одним із шляхів може бути створення ресурсів інтерфейсів
усередині DLL. Наприклад, можна створити один додаток, що у залежності від
версії динамічної бібліотеки буде виводити повідомлення на різних мовах. p>
Природно,
вище наведені лише деякі з аргументів на користь використання динамічно
підключаються бібліотек при розробці додатків. Отже, сподіваюся, ви все ще
зацікавлені в тому, щоб дізнатися, як власне, DLL створюються. Якщо так, то
вперед. p>
Основи розробки DLL h2>
Розробка
динамічних бібліотек не є якимсь надскладний процес,
доступний лише обраним. Якщо ви досить добре знайомі з розробкою
додатків на Object Pascal, то вам не складе особливих зусиль навчитися
працювати з механізмом DLL. Отже, розглянемо ті особливості створення DLL,
які вам необхідно знати, а в завершенні статті розробимо свою власну
бібліотеку. p>
Як
і будь-який інший модуль, модуль динамічної бібліотеки має фіксований
формат. Погляньте на лістинг, представлений нижче. P>
library MyFirstDLL; p>
uses p>
SysUtils, p>
Classes, p>
Forms, p>
Windows; p>
procedure HelloWorld (AForm: TForm); p>
begin p>
MessageBox (AForm.Handle, Hello
world !', p>
DLL Message Box ', MB_OK or
MB_ICONEXCLAMATION); p>
end; p>
exports p>
HelloWorld; p>
begin p>
end. p>
Перше,
на що слід звернути увагу, це ключове слово library, що знаходиться
вгорі сторінки. Library визначає цей модуль як модуль бібліотеки DLL.
Далі йде назва бібліотеки. У нашому прикладі ми маємо справу з динамічним
бібліотекою, яка містить єдину процедуру: HelloWorld. Причому зверніть
увагу, що дана процедура за структурою нічим не відрізняється від тих, які
ви ставите в модулі своїх додатків. Ключове слово exports сигналізує
компілятору про те, що перераховані нижче функції та/або процедури повинні бути
доступні з викликають додатків (тобто вони ніби «експортуються» з
бібліотеки). Детальніше про механізм експорту ми поговоримо трохи пізніше. P>
І,
нарешті, в кінці модуля можна побачити ключові слова begin і end. Всередині
даного блоку ви можете помістити код, який повинен виконуватися в процесі
завантаження бібліотеки. Досить часто цей блок залишається порожнім. P>
Як
вже говорилося вище, всі процедури та функції, що поміщаються в DLL, можуть бути
розділені на дві групи: що експортуються (викликаються з інших додатків) і
локальні. Природно, всередині бібліотеки також можуть бути описані класи,
які в свою чергу містять методи, але в рамках цієї статті я не буду на
цьому зупинятися. p>
Опис
та реалізація процедур і функцій, що викликаються в межах поточної DLL, нічим не
відрізняються від їх аналогів у звичайних проектах-додатках. Їх специфіка
полягає лише в тому, що викликає програма не буде мати до них доступу.
Вона просто не буде нічого знати про їх існування, так само, як одні класи
нічого не знають про ті методи, які описані в секції private інших класів. p>
В
доповнення до процедур і функцій, DLL може містити глобальні дані,
доступ до яких дозволений для всіх процедур і функцій в бібліотеці. Для
16-бітних програм ці дані існували в єдиному екземплярі
незалежно від кількості завантажених в оперативну пам'ять програм, які використовують
поточну бібліотеку. Іншими словами, якщо одна програма змінює значення
глобальної змінної a на 100, то для всіх інших програм a буде
значення 100. Для 32-бітових додатків це не так. Тепер для кожного
програми створюється окрема копія глобальної області даних. p>
Експорт функцій з DLL h2>
Як
вже говорилося вище, для експорту процедур і функцій з DLL, необхідно
використовувати ключове слово export. Ще раз зверніть увагу на представлений
вище лістинг бібліотеки MiFirstDll. Оскільки процедура HelloWorld визначена
як експортована, то вона може бути викликана на виконання з інших бібліотек
або додатків. Існують наступні способи експорту процедур і функцій:
експорт по імені і експорт за порядковим номером. p>
Найбільш
розповсюджений спосіб експорту - на ім'я. Погляньмо на наведений нижче
текст: p>
exports p>
SayHello, p>
DoSomething, p>
DoSomethingReallyCool; p>
Слід
звернути увагу на те, що Delphi автоматично призначає порядковий номер
кожної експортованої функції (процедури) незалежно від того, ви визначаєте
його явно чи ні. Явне визначення індексу дозволяє вам особисто керувати
порядковим номером експортується функції або процедури. p>
Для
того, щоб визначити чи виконується ваш кодек в DLL або в зухвалій
додатку, можна скористатися глобальної змінної IsLibrary. Вона
приймає значення Істина в тому випадку, якщо код викликається з бібліотеки і
ЛОЖЬ у разі виконання процедури або функції з викликає програми. P>
Крім
цього, в постачання Delphi входить дуже корисна утиліта tdump, яка
надає дані про те, яка інформація експортується з вказаної DLL. p>
Використання DLLProc h2>
Вище
я вже говорив про те, що код ініціалізації динамічної бібліотеки може бути
поміщений в блок begin ... end. Однак крім цього найчастіше необхідно
передбачити деякі дії, що виконуються в процесі вивантаження DLL з
оперативної пам'яті. На відміну від інших типів модулів, модуль DLL не має ні
секції initialization, ні секції finalization. Наприклад, ви можете динамічно
виділити пам'ять в головному блоці, проте не зрозуміло, де ця пам'ять повинна бути
звільнена. Для вирішення цієї проблеми існує DLLProc - спеціальна
процедура, яка викликається в певні моменти функціонування DLL. p>
Для
Спершу слід сказати про саму причини існування DLLProc. Динамічна
бібліотека отримує повідомлення від EN-US '> Windows в моменти свого завантаження і
вивантаження з оперативної пам'яті, а також у тих випадках, коли який-небудь
черговий процес, який використовує функції та/або ресурси, що зберігаються в бібліотеці,
завантажується в пам'ять. Така ситуація можливо в тому випадку, коли бібліотека
необхідна для функціонування декількох style = "mso-spacerun:
yes "> љ додатків. А для того, щоб ви мали можливість вказувати,
що саме має відбуватися в такі моменти, необхідно описати спеціальну
процедуру, яка і буде відповідальна за такі дії. Наприклад, вона може
виглядати наступним чином: p>
procedure MyFirstDLLProc (Reason: Integer); p>
begin p>
if Reason = DLL_PROCESS_DETACH then p>
(DLL is unloading. Cleanup code
here.) p>
end; p>
Однак
системі зовсім не очевидно, що саме процедура MyFirstDllProc
відповідальна за обробку розглянутих вище ситуацій. Тому ви повинні
поставити у відповідність адресу нашої процедури глобальної змінної DLLProc.
Це необхідно зробити в блоці begin ... end приблизно так: p>
begin p>
DLLProc
: = @ MyDLLProc; p>
(
Що-небудь ще, що має виконуватися в p>
процесі
ініціалізації бібліотеки) p>
end. p>
Нижче
представлений код, що демонструє один з можливих варіантів застосування
DLLProc. P>
library MyFirstDLL; p>
uses p>
SysUtils, p>
Classes, p>
Forms, p>
Windows; p>
var p>
SomeBuffer: Pointer; p>
procedure MyFirstDLLProc (Reason:
Integer); p>
begin p>
if Reason = DLL_PROCESS_DETACH then p>
(DLL is вивантажується
з пам'яті. p>
Звільняємо
пам'ять, виділену під буфер.) p>
FreeMem (SomeBuffer); p>
end; p>
procedure HelloWorld (AForm: TForm); p>
begin p>
MessageBox (AForm.Handle, Hello
world !', p>
DLL Message Box ', MB_OK or
MB_ICONEXCLAMATION); p>
end; p>
(Який-небудь
код, в якому використовується SomeBuffer.) p>
exports p>
HelloWorld; p>
begin p>
(Ставимо
у відповідність змінної p>
DLLProc
адресу нашої процедури.) p>
DLLProc
: = @ MyFirstDLLProc; p>
SomeBuffer
: = AllocMem (1024); p>
end. p>
Як
можна побачити, як ознака того чи іншого події, в результаті
якої викликається процедура MyFirstDll, є значення змінної Reason.
Нижче наведені можливі значення цієї змінної. P>
DLL_PROCESS_DETACH
- Бібліотека вивантажується з пам'яті; використовується один раз; p>
DLL_THREAD_ATTACH
- В оперативну пам'ять завантажується новий процес, що використовує ресурси та/або
код з даної бібліотеки; p>
DLL_THREAD_DETACH
- Один з процесів, що використовують бібліотеку, 'вивантажується' з пам'яті. p>
Завантаження DLL h2>
Перш
ніж почати використання будь-якої процедури або функції, що знаходиться в
динамічної бібліотеці, вам необхідно завантажити DLL в оперативну пам'ять.
Завантаження бібліотеки може бути здійснено одним із двох способів: статична
завантаження і динамічне завантаження. Обидва методи мають як переваги, так і
недоліки. p>
Статична
завантаження означає, що динамічна бібліотека завантажується автоматично при
запуску на виконання використовує її застосування. Для того, щоб використовувати
такий спосіб завантаження, вам необхідно скористатися ключовим словом external
при описі експортованої з динамічної бібліотеки функції або процедури.
DLL автоматично завантажується при старті програми, і Ви зможете використовувати
будь-які експортуються з неї підпрограми точно так само, як якби вони були
описані всередині модулів програми. Це найбільш легкий спосіб використання
коду, який міститься у DLL. Недолік методу полягає в тому, що якщо файл
бібліотеки, на який є посилання в програмі, відсутній, програма
відмовиться завантажуватися. p>
Сенс
динамічного методу полягає в тому, що ви завантажуєте бібліотеку не при
старті програми, а в той момент, коли вам це дійсно необхідно. Самі
поміркуйте, адже якщо функція, описана у динамічній бібліотеці, використовується
тільки за 10% запусків програми, то абсолютно немає сенсу використовувати
статичний метод завантаження. Вивантаження бібліотеки з пам'яті в даному випадку також
здійснюється під вашим контролем. Ще одне переваги такого способу
завантаження DLL - це зменшення (зі зрозумілих причин) часу старту вашого
додатки. А які ж у цього способу є недоліки? Основний, як мені
здається, - це те, що використання даного методу є більш важким,
ніж розглянута вище статичне завантаження. Спочатку потрібно
скористатися функцією Windows API LoadLibrary. Для отримання вказівника на
експортованої процедури або функції повинна використовуватися функція
GetProcAddress. Після завершення використання бібліотеки DLL повинна бути
вивантажено із застосуванням FreeLibrary. p>
Виклик процедур і функцій, завантажених з DLL. h2>
Спосіб
виклику процедур і функцій залежить від того, яким чином ви завантажили
динамічну бібліотеку, в якій ці підпрограми знаходяться. p>
Виклик
функцій і процедур з статично завантажених DLL досить простий.
Спочатку в додатку повинно міститися опис експортується функції
(процедури). Після цього ви можете їх використовувати точно так само, як якщо б
вони були описані в одному з модулів вашого застосування. Для імпорту функції або
процедури, що міститься в DLL, необхідно використовувати модифікатор external в
їх оголошенні. Приміром, для розглянутої нами вище процедури HelloWorld в
що викликає додатку повинна бути поміщена наступний рядок: p>
procedure SayHello (AForm: TForm);
external myfirstdll.dll '; p>
Ключове
слово external повідомляє компілятору, що дана процедура може бути знайдена в
динамічної бібліотеці (у нашому випадку - myfirstdll.dll). Далі виклик цій
процедури виглядає наступним чином: p>
... p>
HelloWorld (self); p>
... p>
При
імпорті функції і процедур будьте особливо уважні при написанні їхніх імен і
інтерфейсів! Справа в тому, що в процесі компіляції програми не проводиться
перевірки на правильність імен об'єктів, що експортуються з DLL, здійснюватиметься
не буде, і якщо ви неправильно описали яку-небудь функцію, то виключення
буде Згенеровано тільки на етапі виконання програми. p>
Імпорт
з DLL може проводитися на ім'я процедури (функції), порядкового номера або з
присвоєнням іншого імені. p>
В
першому випадку ви просто оголошуєте ім'я процедури і бібліотеку, з якої її
імпортуєте (ми це розглянули трохи вище). Імпорт за порядковим номером
вимагає від вас вказівку цього самого номера: p>
procedure HelloWorld (AForm: TForm); p>
external myfirstdll.dll 'index 15; p>
В
цьому випадку ім'я, яке ви даєте процедурою при імпорті не обов'язково має
співпадати з тим, що було вказано для неї в самій DLL. Тобто приведена
вище запис означає, що ви імпортуєте з динамічної бібліотеки
myfirstdll.dll процедуру, яка в ній експортувалася п'ятнадцятий, і при
це в рамках вашого застосування цієї процедури дається ім'я SayHello. p>
Якщо
ви з якихось причин не застосовуєте описаний вище спосіб імпорту, але тим не
менше хочете змінити ім'я імпортованої функції (процедури), то можна
скористатися третім методом: p>
procedure CoolProcedure; p>
external myfirstdll.dll 'name
DoSomethingReallyCool '; p>
Тут імпортованої процедурі
CoolProcedure дається ім'я DoSomethingReallyCool. Виклик
процедур і функцій, що імпортуються з динамічно завантажуваних бібліотек
трохи більш складний, ніж розглянутий нами вище спосіб. У даному випадку
потрібно оголосити покажчик на функцію або процедуру, яку ви збираєтеся
використовувати. Пам'ятайте процедуру HelloWorld? Давайте подивимося, що необхідно
зробити для того, щоб викликати її на виконання у разі динамічного завантаження
DLL. По-перше, вам необхідно оголосити тип, який описував би цю процедуру: p>
type p>
THelloWorld = procedure (AForm:
TForm); p>
Тепер
ви повинні завантажити динамічну бібліотеку, за допомогою GetProcAddress отримати
покажчик на процедуру, викликати цю процедуру на виконання, і, нарешті,
DLL вивантажити з пам'яті. Нижче приведений код, що демонструє, як це можна
зробити: p>
var p>
DLLInstance
: THandle; p>
HelloWorld: THelloWorld; p>
begin p>
(завантажуємо DLL) p>
DLLInstance: =
LoadLibrary (myfirstdll.dll'); p>
(отримуємо покажчик) p>
@ HelloWorld: =
GetProcAddress (DLLInstance, HelloWorld'); p>
(викликаємо процедуру на виконання) p>
HelloWorld (Self); p>
(
вивантажуємо DLL з оперативної пам'яті) p>
FreeLibrary (DLLInstance); p>
end; p>
Як
вже говорилося вище, одним з недоліків статичної завантаження DLL є
неможливість продовження роботи програми при відсутності однієї або декількох
бібліотек. У випадку з динамічної завантаженням у вас з'являється можливість
програмно обробляти такі ситуації і не допускати, щоб програма
«Вивалювалися» самостійно. За повертає функція LoadLibrary і
GetProcAddress значенням можна визначити, чи успішно пройшла завантаження
бібліотеки та знайдено в ній необхідна додатком процедура. Наведений нижче
код демонструє це. p>
procedure TForm1.DynamicLoadBtnClick (Sender: TObject); p>
type p>
THelloWorld = procedure (AForm:
TForm); p>
var p>
DLLInstance: THandle; p>
HelloWorld: THelloWorld; p>
begin p>
DLLInstance: =
LoadLibrary (myfirstdll.dll'); p>
if DLLInstance = 0 then begin p>
MessageDlg (Неможливо завантажити DLL ', mtError, [mbOK], 0); p>
Exit; p>
end; p>
@ HelloWorld: =
GetProcAddress (DLLInstance, HelloWorld'); p>
if @ HelloWorld nil then p>
HelloWorld (Self) p>
else p>
MessageDlg (Не знайдена шукана процедура!. ', mtError, [mbOK], 0); p>
FreeLibrary (DLLInstance); p>
end; p>
В
DLL можна зберігати не тільки код, але і форми. Причому створення і приміщення форм у
динамічну бібліотеку не дуже сильно відрізняється від роботи з формами в
звичайному проекті. Спочатку ми розглянемо, яким чином можна написати
бібліотеку, яка містить форми, а потім ми поговоримо про використання технології
MDI в DLL. P>
Розробку
DLL, що містить форму, я покажу на прикладі. P>
Отже,
по-перше, створимо новий проект динамічної бібліотеки. Для цього виберемо
пункт меню File | New, а потім двічі клацнемо на іконку DLL. Після цього ви
побачите приблизно наступний код: p>
library
Project2; p>
(тут
були коментарі) p>
uses p>
SysUtils, p>
Classes; p>
($ R *. RES) p>
begin p>
end. p>
Збережіть
отриманий проект. Назвемо його DllForms.dpr. P>
Тепер
слід створити нову форму. Це можна зробити по-різному. Наприклад, вибравши
пункт меню File | New Form. Додайте на форму які-небудь компоненти. Назвемо
форму DllForm і збережемо отриманий модуль під ім'ям DllFormUnit.pas. p>
Повернемося
до головного модулю проекту і помістимо в нього функцію ShowForm, завданням якого
входитиме створення форми та її виведення на екран. Використовуйте для цього наведений
нижче код. p>
function ShowForm: Integer; stdcall; p>
var p>
Form: TDLLForm; p>
begin p>
Form: =
TDLLForm.Create (Application); p>
Result: = Form.ShowModal; p>
Form.Free; p>
end; p>
Звертаю
увагу, що для того, щоб проект був скомпільований без помилок, необхідно
додати в секцію uses модуль Forms. p>
Експортуємо
нашу функцію з використанням ключового слова exports: p>
exports p>
ShowForm; p>
Компіліруем
проект і отримуємо файл dllforms.dll. Ці прості кроки - все, що необхідно
зробити для створення динамічної бібліотеки, яка містить форму. Зверніть
увагу, що функція ShowForm оголошена з використанням ключового слова
stdcall. Воно сигналізує компілятору використовувати при експорті функції
угоду за стандартним викликом (standard call calling convention). Експорт
функції таким чином створює можливість використання розробленої DLL НЕ
тільки в програмах, що створені в Delphi. p>
Угода
за викликом (Calling conventions) визначає, яким чином передаються аргументи
при виконанні функції. Існує п'ять основних угод: stdcall, cdecl, pascal,
register і safecall. Докладніше про це можна дізнатися, подивившись розділ "
Calling Conventions "у файлі допомоги Delphi. P>
Також
зверніть увагу, що значення, що повертається функцією ShowForm, відповідає
значенням ShowModal. Таким чином ви можете передавати деяку інформацію про
стані форми викликає додатком. p>
Нижче
представлено два лістингу, перший з яких містить повний код файлу проекту
DLL (модуль з формою тут не наводиться), а другий - модуль що викликає
додатки, в якому використовується тільки що розроблена нами бібліотека. p>
library DllForms; p>
uses p>
SysUtils, p>
Classes, p>
Forms, p>
DllFormUnit in 'DllFormUnit.pas'
(DllForm); p>
($ R *. RES) p>
function ShowForm: Integer;
stdcall; p>
var p>
Form: TDLLForm; p>
begin p>
Form: =
TDLLForm.Create (Application); p>
Result: = Form.ShowModal; p>
Form.Free; p>
end; p>
begin p>
end. p>
unit TestAppUnit; p>
interface p>
uses p>
Windows, Messages, SysUtils,
Classes, Graphics, p>
Controls, Forms, Dialogs, StdCtrls; p>
type p>
TForm1 = class (TForm) p>
Button1: TButton; p>
procedure Button1Click (Sender:
TObject); p>
private p>
(Private declarations) p>
public p>
(Public declarations) p>
end; p>
var p>
Form1: TForm1; p>
function ShowForm: Integer;
stdcall; p>
external dllforms.dll '; p>
implementation p>
($ R *. DFM) p>
procedure
TForm1.Button1Click (Sender: TObject); p>
begin p>
ShowForm; p>
end; p>
end. p>
Прошу
помітити, що при експорті функції також було використано ключове слово
stdcall. p>
Слід
звернути особливу увагу на роботу з дочірніми формами в DLL. Якщо, наприклад,
в зухвалій додатку головна форма має значення властивості FormStyle, рівним
MDIForm, то при спробі дзвінка з DLL MDIChild-форми, на екрані з'явиться
повідомлення про помилку, в якому говориться, що немає жодної активної
MDI-форми. P>
В
той момент, коли ви намагаєтеся показати ваше дочірнє вікно, VCL перевіряє
коректність властивості FormStyle головної форми програми. Однак у нашому випадку
все начебто правильно. Так у чому ж справа? Проблема в тому, що при проведенні такої
перевірки, розглядається об'єкт Application, що належать не викликає
додатку, а власне динамічної бібліотеці. Ну, і природно, оскільки
в DLL немає головної форми, перевірка видає помилку. Для того щоб уникнути такої
ситуації, треба призначити об'єкту Application динамічної бібліотеки об'єкт
Application викликає програми. Природно, це запрацює тільки в тому
випадку, коли викликає програма - VCL-додаток. Крім того, перед
вивантаженням бібліотеки з пам'яті необхідно повернути значення об'єкта Application
бібліотеки в початковий стан. Це дозволить менеджеру пам'яті очистити
оперативну пам'ять, займану бібліотекою. Отже, вам потрібно зберегти
покажчик на «рідної» для бібліотеки об'єкт Application в глобальній
змінної, яка може бути використана при відновленні його значення. p>
Отже,
повернемося трохи назад і перерахуємо кроки, необхідні нам для роботи з
вміщених у DLL MDIChild-формами. p>
В
динамічної бібліотеці створюємо глобальну змінну типу TApplication. p>
Зберігаємо
покажчик на об'єкт Application DLL в глобальній змінній. p>
Об'єкту
Application динамічної бібліотеки ставимо у відповідність вказівник на
Application викликає програми. p>
Створюємо
MDIChild-форму і працюємо з нею. p>
Повертаємо
в первісний стан значення об'єкта Application динамічної бібліотеки
і вивантажуємо DLL з пам'яті. p>
Перший
крок простий. Просто поміщаємо наступний код у верхній частині модуля DLL: p>
var p>
DllApp
: TApplication; p>
Потім
створюємо процедуру, яка буде змінювати значення об'єкта Application і
створювати дочірню форму. Процедура може виглядати приблизно так: p>
procedure ShowMDIChild (MainApp: TApplication); p>
var p>
Child: TMDIChild; p>
begin p>
if not Assigned (DllApp) then begin p>
DllApp: = Application; p>
Application: = MainApp; p>
end; p>
Child: =
TMDIChild.Create (Application.MainForm); p>
Child.Show; p>
end; p>
Все,
що нам тепер необхідно зробити, - це передбачити повернення значення
об'єкта Application в початковий стан. Робимо це за допомогою процедури MyDllProc: p>
procedure MyDLLProc (Reason: Integer); p>
begin p>
if Reason = DLL_PROCESS_DETACH then p>
(
DLL is вивантажується.
Відновлюємо значення покажчика Application) p>
if Assigned (DllApp) then p>
Application: = DllApp; p>
end; p>
Замість
ув'язнення. p>
Використання
динамічно підключаються бібліотек не так складно, як це може здатися на
перший погляд. DLL надають найширші можливості для оптимізації роботи
додатків, а також роботи самих програмістів. Використовуйте DLL і, можливо,
ваше життя стане легше! p>
Список літератури h2>
Для
підготовки даної роботи були використані матеріали з сайту http://www.hostmake.ru/
p>