Створення форми для динамічного завантаження DLL в CBuilder

Для того щоб використовувати нашу DLL в проекті CBuilder, нам потрібна форма, за допомогою якої користувач викликав би ці функції, а також для перегляду результатів На рис 104 показана форма, яку ми будемо використовувати в нашому проекті динамічної DLL Як бачите, ми побудували цю форму, використавши три кнопки, мітку і поле для введення даних,

посилаються в функції в DLL

Перший крок при завантаженні інформації з DLL в точності такий же, як і при отриманні ресурсів з файлу, тобто нам потрібно отримати посилання на бібліотеку за допомогою функції API LoadLibrary Для цього додайте наступний код в конструктор форми:

__fastcall TForm1::TForm1(TComponent* Owner)

{

hLibHandle = LoadLibrary(&quotvcdlldll&quot)

}

Аналогічно, нам потрібно переконатися, що посилання на бібліотеку звільнена, коли ми закінчили з нею працювати Це робиться в обробнику події OnClose форми, FormClose Створіть обробник і додайте в нього наступний код:

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &ampAction)

{

if ( hLibHandle ) FreeLibrary( hLibHandle )

}

Рис 104 Форма для динамічного завантаження DLL

Принаймні, до цієї точки процес, якому ми слідували, повністю збігається з тим, що ми робили при завантаженні рядків з DLL Це місце в процесі, де два приклади розходяться Давайте подивимося на це, додавши обробник для натискання на першу кнопку (Викликати функцію 1) у форму Ось код, який потрібно додати в обробник Button1Click:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

if ( hLibHandle )

{

/ / Намагаємося завантажити функцію з бібліотеки pfivFunc pFunc = (pfivFunc) GetProcAddress (hLibHandle,

&quotCallFunction1&quot) if ( pFunc ) (*pFunc)()

}

}

Вам потрібно додати ще один рядок у файл, щоб він скомпилірувався Додайте наступний рядок прямо над функцією, а потім ми поговоримо про те, що ж відбувається в цьому фрагменті коду:

typedef int (*pfivFunc)(void)

Підемо по порядку Цей рядок (typedef) – Спосіб опису нового типу в C або C + + Хоча вона і виглядає абсолютно по-іншому, ніж звичайні опису типів, які ви коли-небудь бачили в своєму коді, це насправді просто окремий спеціальний випадок оператора typedef

Основна форма оператора typedef, Яка просто описує новий тип як більш просту рядок для компілятора, виглядає так: typedef опис-старого-типу опис-нового-типу Ось простий приклад, щоб ви зрозуміли, як це працює Уявіть, що ви хочете описати новий тип, IntBool, Для представлення логічного (булевского) значення у вигляді цілого Цей тип не відрізняється від цілого, але ми хочемо, щоб програміст, який читає код, розумів, що дана змінна повинна зберігати тільки Булевського значення (true, істина, або false, неправда) в цілому поданні Тоді ми напишемо:

typedef int IntBool

Тепер далі в коді ми можемо використовувати новий тип для опису змінних: IntBool bMyInteger

Для компілятора немає різниці у використанні типів int і IntBool Він просто зіставляє один іншому У нашому типі pfivFunc (Див вище) ми насправді написали, що тип pfivFunc представляє функцію, у якої немає параметрів (ключове слово void в дужках) і яка повертає ціле значення Загальна форма оператора typedef по відношенню до функцій така:

return-type (*function-name)(parameter types)

де return-type – Це тип повернення функції (int, void, bool і т д), а function-name– Імя визначення typedef Це імя, яке ми будемо використовувати як новий тип, точно так само, як ми використовували раніше IntBool Parameter types – Список коректних типів C + + (включаючи нові типи) Якщо у вашої функції три параметри, скажімо ціле, речовий і посилання (reference) на обєкт класу Foo, То це виглядатиме так: (int, float, Foo&amp)

Тепер, коли ця дивина з оператором typedef в коді позаду, весь код ясний як божий день Ми використовуємо функцію API GetProcAddress для отримання покажчика на функцію, що знаходиться в бібліотек е DLL, на яку у нас є посилання Цей покажчик буде типу FARPROC, Що є просто покажчик на функцію Нам же потрібно викликати цю функцію з параметрами і перевіряти повертається значення, так що потрібно пояснити компілятору, що у функції є такі параметри Так як у нас є чудовий оператор typedef, Що описує в точності прототип нашої функції, то ми перетворюючої ем повернутий покажчик типу FARPROC в один з цих нових типів покажчиків на функції і потім побічно викликаємо нашу функцію, застосовуючи оператор дозволу покажчика (зірочку) і передаючи їй аргументи (якщо вони є, звичайно)

Дуже важливо, щоб типи повністю відповідали кількості і типу параметрів, які ви передаєте таким чином в функцію Помилка в даному місці може призвести до жахливих наслідків, включаючи фатальний збій операційної системи Windows

Як ви памятаєте, перша функція (CallFunction1) В бібліотеці просто відображала вікно повідомлення Функція GetProcAddress витягує адреса функції CallFunction1 з DLL, знаходячи зовнішнє (експортувати) імя в DLL, що збігається з імям, яке ми передаємо Ось чому так важливо використовувати модифікатор extern &quotC&quot у визначенні DLL Інакше нам би довелося викликати збочене (mangled) (іноді його називають мякше, декороване, decorated) імя в DLL, і в результаті ми викликали б що-небудь виду «CallFunction1 @ iv1» А в нашому випадку ми звертаємося до функції просто по імені

Вам, напевно, цікаво, чи можете ви використовувати функцію GetProcAddress для доступу до функцій у вашій програмі і використання їх, наприклад, для реалізації мови сценаріїв у вашій програмі Так, ви можете так робити Якщо функції доступні по імені і ви знаєте це імя, можете викликати їх з будь-якої програми, на яку у вас є посилання Теоретично, якщо вам вдалося добути посилання на екземпляр запущеної програми, ви можете також запускати функції, що містяться в ній

Для другої кнопки нам потрібна функція, що має один аргумент – рядок (const char *) І повертає ціле значення Для визначення типу, який представляє такі функції, нам потрібно додати наступний рядок у вихідний файл:

typedef int (*pficsFunc)(const char *)

Відповідно, ми тепер можемо додати обробник для натискання на другу кнопку на формі, що викликає другу функцію в DLL (CallFunction2) Створіть обробник для другої кнопки і помістіть в нього наступний код:

void __fastcall TForm1::Button2Click(TObject *Sender)

{

if ( hLibHandle )

{

/ / Намагаємося завантажити функцію з бібліотеки pficsFunc pFunc = (pficsFunc) GetProcAddress (hLibHandle,

&quotCallFunction2&quot)

if ( pFunc )

(*pFunc)(Edit1-&gtTextc_str())

}

}

Процес завантаження відрізняється небагатьом Ми просто витягаємо функцію CallFunction1 з DLL і потім побічно її викликаємо через повернутий покажчик В даному випадку нам потрібно послати функції рядок для відображення, так що ми використовуємо для цього текст, що знаходиться у полі введення

Варто зауважити, що даний код добре захищений Є кілька проблем, які можуть виникнути в цьому коді, так що він робить все, щоб запобігти (у разі будь-яких помилок) поява катастрофічних порушень в системі По-перше, ми перевіряємо посилання на DLL, не дорівнює чи вона NULL Це може привести до серйозних проблем у функціїGetProcAddress Коли ми перевірили, що посилання коректна (в силу нашого розуміння), наступним ми перевіряємо

покажчик, повернутий функцією API GetProcAddress Якщо функція не може знайти запитаний

ную функцію в DLL або не може виконати запит з якоїсь іншої причини, то повернення від функції API буде дорівнює NULL (більшість функцій API, які повертають посилання або покажчики, повернуть вам NULL в разі будь-якої помилки) У такому випадку нам не хотілося б викликати побічно функцію, так як це призведе до порушення захисту Windows або навіть гірше

Третя (остання) кнопка викликає функцію, яка має один параметр-ціле і нічого не повертає (void) Прототип функції в операторі typedef C + + виглядає так:

typedef void (*pfviFunc)(int)

Після чого ми можемо використовувати цей тип для передачі даних у функцію, написавши код для обробника натискання на третю кнопку Додайте цей обробник і внесіть до нього такий код:

void __fastcall TForm1::Button3Click(TObject *Sender)

{

if ( hLibHandle )

{

/ / Намагаємося завантажити функцію з бібліотеки pfviFunc pFunc = (pfviFunc) GetProcAddress (hLibHandle,

&quotCallFunction3&quot) if ( pFunc )

(*pFunc)(atoi(Edit1-&gtTextc_str()))

}

}

Знову ж, ми здобуваємо функцію з DLL, передаючи їй на цей раз цілий аргумент Хоча я і попереджав, що не треба передавати властивість Text у функцію, в даному випадку це робити можна Поганою ідеєю є передача властивості Text у вигляді рядка функції, яка може змінити рядок У цьому випадку може вийти непередбачуваний результат У разі ж функції atoi, Яка обіцяє не змінювати переданий аргумент, це є відносно безпечним маневром

Тепер, коли ви скомпілюєте і зберете додаток, зможете побачити результати вашого наполегливої ​​праці По-перше, переконайтеся, що файл VCDLLDLL знаходиться в тому ж каталозі, що і програма (project1exe) Тоді можна бути впевненим, що Windows знайде цей файл і завантажить його Якщо ви не покладете файл в той же каталог, що і виконуваний файл, то Windows застосує простий процес для пошуку цього файлу У першу чергу система буде шукати цей файл в каталогах Windows і Windows \ System (для Windows NT це, відповідно, WinNT, WinNT \ System і WinNT \ System32) Якщо файлу там немає, то Windows буде шукати в кожному каталозі, заданому в змінної оточення path (у Windows NT є ​​спеціальна змінна, яка задає шлях для пошуку DLL) І нарешті, якщо файл не буде знайдений, функція LoadLibrary поверне NULL, що означає, що DLL не знайдено

Якщо ви насправді поклали DLL в таке місце, де Windows зможе його знайти, форма завантажиться і відобразить бажані кнопки Введіть небудь текст в поле введення в нижній частині форми і натисніть на другу кнопку (Викликати функцію2) Ви побачите вікно повідомлення з текстом, який ви ввели, показане на рис 105 Це означає, що функція була знайдена і що текст був коректно переданий функції в DLL

Рис 105 Форма додатка «динамічне завантаження DLL» під час виконання

На цьому ми завершуємо дискусію про роботу з динамічними бібліотеками Ця дуже потужна технологія дозволяє вам винести частину вашого коду, що містить загальні функції, в зовнішнє поділювану бібліотеку, яку можна довантажити при бажанні під час виконання У такого підходу є два очевидні переваги По-перше, код буде лежати в одному і тому ж місці, навіть якщо його використовують різні програми Це економить дисковий простір і память По-друге, використання DLL для зберігання загальних функцій програми дозволить вам змінювати ці функції без необхідності змінювати сам додаток Якщо в якийсь функції виявлена помилка, то ви просто поширюєте серед користувачів DLL, що містить цю функцію, замість того щоб розсилати всім все додаток цілком

Джерело: Теллес М – Borland C + + Builder Бібліотека програміста – 1998

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


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

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

Ваш отзыв

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

*

*