Використання DLL

Практично неможливо створити додаток Windows, в якому не
використовувалися б бібліотеки DLL. У DLL містяться всі функції Win32 API і
незліченну кількість інших функцій операційних систем Win32.


Взагалі кажучи, DLL – це просто набори функцій, зібрані в бібліотеки.
Однак, на відміну від своїх статичних родичів (файлів. Lib), бібліотеки
DLL не приєднані безпосередньо до виконуваних файлів за допомогою редактора
зв'язків. У виконуваний файл занесена тільки інформація про їхнє місцезнаходження. У
момент виконання програми завантажується вся бібліотека цілком. Завдяки цьому
різні процеси можуть користуватися спільно одними і тими ж бібліотеками,
знаходяться в пам'яті. Такий підхід дозволяє скоротити обсяг пам'яті,
необхідний для декількох додатків, що використовують багато спільних бібліотек, а
також контролювати розміри ЕХЕ-файлів.


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


Найчастіше проект підключається до DLL статично, або неявно, на етапі
компонування. Завантаженням DLL при виконанні програми управляє операційна
система. Однак, DLL можна завантажити і явно, або динамічно, в ході роботи
додатки.


Бібліотеки імпортування


При статичному підключенні DLL ім'я. Lib-файлу визначається серед інших
параметрів редактора зв'язків у командному рядку або на вкладці "Link" діалогового
вікна "Project Settings" середовища Developer Studio. Однак. Lib-файл, використовуваний
при неявному підключенні DLL, – Це не звичайна статична бібліотека.
Такі. Lib-файли називаються бібліотеками імпортування (import
libraries). У них міститься не сам код бібліотеки, а тільки посилання на всі
функції, що експортуються з файлу DLL, в якому все і зберігається. У результаті
бібліотеки імпортування, як правило, мають менший розмір, ніж DLL-файли. До
способам їх створення повернемося пізніше. А зараз розглянемо інші питання,
стосуються неявного підключення динамічних бібліотек.


Узгодження інтерфейсів


При використанні власних бібліотек або бібліотек незалежних
розробників доведеться звернути увагу на узгодження виклику функції з її
прототипом.


Якби світ був досконалий, то програмістам не довелося б турбуватися про
узгодженні інтерфейсів функцій при підключенні бібліотек – всі вони були б
однаковими. Проте світ далекий від досконалості, і багато великі програми
написані за допомогою різних бібліотек без C + +.


За умовчанням в Visual C + + інтерфейси функцій узгоджуються за правилами C + +. Це
значить, що параметри заносяться в стек справа наліво, викликає програма
відповідає за їх видалення з стека при виході з функції і розширенні її імені.
Розширення імен (name mangling) дозволяє редактору зв'язків розрізняти
перевантажені функції, тобто функції з однаковими іменами, але різними списками
аргументів. Проте в старій бібліотеці З функції з розширеними іменами
відсутні.


Хоча всі інші правила виклику функції в С ідентичні правилам виклику
функції в C + +, в бібліотеках З імена функцій не розширюються. До них тільки
додається попереду символ підкреслення (_).


Якщо необхідно підключити бібліотеку на С до додатка на C + +, всі функції
з цієї бібліотеки доведеться оголосити як зовнішні у форматі С:

 extern "С" int MyOldCFunction (int myParam);

Оголошення функцій бібліотеки звичайно містяться у файлі заголовка цієї
бібліотеки, хоча заголовки більшості бібліотек С не розраховані на застосування в
проектах на C + +. У цьому випадку необхідно створити копію файлу заголовка і
включити до неї модифікатор extern "C" до оголошення всіх використовуваних функцій
бібліотеки. Модифікатор extern "C" можна застосувати і до цілого блоку, до якого
за допомогою директиви # tinclude підключений файл старого заголовка С. Таким чином,
замість модифікації кожної функції окремо можна обійтися всього трьома
рядками:

 extern "С"
{
#include "MyCLib.h"
}

У програмах для старих версій Windows використовувалися також угоди про
виклику функцій мови PASCAL для функцій Windows API. У нових програмах слід
використовувати модифікатор winapi, що перетворюється в _stdcall. Хоча це і не
стандартний інтерфейс функцій З або C + +, але саме він використовується для звернень
до функцій Windows API. Проте зазвичай все це вже враховано у стандартних
заголовках Windows.


Завантаження неявно підключається DLL


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



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


Якщо потрібна бібліотека знайдена, вона поміщається в оперативну пам'ять процесу,
де і залишається до його закінчення. Тепер додаток може звертатися до функцій,
містяться в DLL.


Динамічне завантаження і вивантаження DLL


Замість того, щоб Windows виконувала динамічне зв'язування з DLL при першій
завантаженні програми в оперативну пам'ять, можна пов'язати програму з модулем
бібліотеки під час виконання програми (при такому способі в процесі створення
додатки не потрібно використовувати бібліотеку імпорту). Зокрема, можна
визначити, яка з бібліотек DLL доступна користувачеві, або дозволити
користувачеві вибрати, яка з бібліотек буде завантажуватися. Таким чином можна
використовувати різні DLL, в яких реалізовані одні й ті ж функції, що виконують
різні дії. Наприклад, додаток, призначений для незалежної
передачі даних, зможе в ході виконання прийняти рішення, завантажувати чи DLL для
протоколу TCP / IP або для іншого протоколу.


Завантаження звичайної DLL


Перше, що необхідно зробити при динамічній завантаженні DLL, – це помістити
модуль бібліотеки в пам'ять процесу. Дана операція виконується за допомогою
функції ::LoadLibrary, Що має єдиний аргумент – ім'я завантажуваного
модуля. Відповідний фрагмент програми повинен виглядати так:

    HINSTANCE hMyDll;
::
if ((hMyDll =:: LoadLibrary ("MyDLL "))== NULL) {/ * не вдалося завантажити DLL * /}
else {/ * програма має право користуватися функціями DLL через hMyDll * /}

Стандартним розширенням файлу бібліотеки Windows вважає. Dll, якщо не
вказати інше розширення. Якщо в імені файлу вказаний і шлях, то лише він буде
використовуватися для пошуку файлу. В іншому випадку Windows буде шукати файл за
тією ж схемою, що й у випадку неявно підключених DLL, починаючи з каталогу, з
якого завантажується exe-файл, і продовжуючи у відповідності зі значенням PATH.


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


Якщо файл виявлений і бібліотека успішно завантажилася, функція
::LoadLibrary повертає її дескриптор, який використовується для доступу
до функцій бібліотеки.


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

 / / Тип PFN_MyFunction буде оголошувати покажчик на функцію,
/ / Приймаючу покажчик на символьний буфер і видає значення типу int
typedef int (WINAPI *PFN_MyFunction)(char *);
::
PFN_MyFunction pfnMyFunction;

Потім слід отримати дескриптор бібліотеки, за допомогою якого і
визначити адреси функцій, наприклад адресу функції з ім'ям MyFunction:

    hMyDll=::LoadLibrary("MyDLL");
pfnMyFunction = (PFN_MyFunction):: GetProcAddress (hMyDll, "MyFunction");
::
int iCode=(*pfnMyFunction)("Hello");

Адреса функції визначається за допомогою функції ::GetProcAddress, Їй
слід передати ім'я бібліотеки і ім'я функції. Остання повинна передаватися в
тому вигляді, в якому експортується з DLL.


Можна також послатися на функцію за порядковим номером, за яким вона
експортується (при цьому для створення бібліотеки повинен використовуватися def-файл,
про це буде розказано далі):

    pfnMyFunction=(PFN_MyFunction)::GetProcAddress(hMyDll,
MAKEINTRESOURCE(1));

Після завершення роботи з бібліотекою динамічної компоновки, її можна
вивантажити з пам'яті процесу за допомогою функції::FreeLibrary:

    ::FreeLibrary(hMyDll);

Завантаження MFC-розширень динамічних бібліотек


При завантаженні MFC-розширень для DLL (докладно про які розповідається далі)
замість функцій LoadLibraryі FreeLibrary використовуються функції
AfxLoadLibrary і AfxFreeLibrary. Останні майже ідентичні
функцій Win32 API. Вони лише гарантують додатково, що структури MFC,
початкові розширенням DLL, не були зіпсований іншими потоками.


Ресурси DLL


Динамічне завантаження застосовна і до ресурсів DLL, використовуваним MFC для
завантаження стандартних ресурсів програми. Для цього спочатку необхідно викликати
функцію LoadLibrary і розмістити DLL в пам'яті. Потім за допомогою функції
AfxSetResourceHandle потрібно підготувати вікно програми до прийому ресурсів
з знову завантаженої бібліотеки. В іншому випадку ресурси будуть завантажуватися з
файлів, підключених до виконуваного файлу процесу. Такий підхід зручний, якщо
потрібно використовувати різні набори ресурсів, наприклад для різних мов.


Зауваження. За допомогою функції LoadLibrary можна також завантажувати в
пам'ять виконувані файли (не запускати їх на виконання!). Дескриптор
виконуваного модуля може потім використовуватися при зверненні до функцій
FindResource і LoadResource для пошуку і завантаження ресурсів
додатки. Вивантажують модулі з пам'яті також за допомогою функції
FreeLibrary.


Приклад звичайної DLL і способів завантаження


Наведемо вихідний код динамічно підключається бібліотеки, яка називається
MyDLL і містить одну функцію MyFunction, яка просто виводить повідомлення.


Спочатку в заголовному файлі визначається макроконтстанта EXPORT.
Використання цього ключового слова при визначенні деякої функції
динамічно підключається бібліотеці дозволяє повідомити компонувальник, що ця
функція доступна для використання іншими програмами, в результаті чого він
заносить її до бібліотеки імпорту. Крім цього, така функція, точно так само, як і
віконна процедура, повинна визначатися за допомогою константи CALLBACK:

MyDLL.h
#define EXPORT extern "C" __declspec (dllexport)
EXPORT int CALLBACK MyFunction(char *str);

Файл бібліотеки також дещо відрізняється від звичайних файлів на мові C для
Windows. У ньому замість функції WinMain є функція DllMain. Ця
функція використовується для виконання ініціалізації, про що буде розказано пізніше.
Для того, щоб бібліотека залишилася після її завантаження в пам'яті, і можна було
викликати її функції, необхідно, щоб її повертаним значенням було TRUE:

MyDLL.c
#include <windows.h>
#include "MyDLL.h"

int WINAPI DllMain (HINSTANCE hInstance, DWORD fdReason, PVOID pvReserved)
{
return TRUE;
}
EXPORT int CALLBACK MyFunction(char *str)
{
MessageBox (NULL, str, "Function from DLL", MB_OK);
return 1;
}


Після трансляції і компонування цих файлів з'являється два файли – MyDLL.dll
(Сама Колективна бібліотека) і MyDLL.lib (її бібліотека
імпорту).


Приклад неявного підключення DLL додатком


Наведемо тепер вихідний код простого застосування, яке використовує функцію
MyFunction з бібліотеки MyDLL.dll:

    #include <windows.h>
#include "MyDLL.h"

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
int iCode=MyFunction("Hello");
return 0;
}


Ця програма виглядає як звичайна програм для Windows, ніж вона по суті і
є. Тим не менш, слід звернути увагу, що у вихідний її текст
крім виклику функції MyFunction з DLL-бібліотеки включений і заголовний файл
цієї бібліотеки MyDLL.h. Також необхідно на етапі компонування програми
підключити до нього бібліотеку імпорту MyDLL.lib (процес неявного
підключення DLL до виконуваного модулю).


Надзвичайно важливо розуміти, що сам код функції MyFunction не включається до
файл MyApp.exe. Замість цього там просто є посилання на файл MyDLL.dll і
посилання на функцію MyFunction, яка знаходиться в цьому файлі. Файл MyApp.exe
вимагає запуску файлу MyDLL.dll.


Заголовний файл MyDLL.h включений у файл із вихідним текстом програми MyApp.c
точно так само, як туди включений файл windows.h. Включення бібліотеки імпорту
MyDLL.lib для компонування аналогічно включенню туди всіх бібліотек імпорту
Windows. Коли програма MyApp.exe працює, вона підключається до бібліотеки
MyDLL.dll точно так само, як до всіх стандартних динамічно підключається
бібліотекам Windows.


Приклад динамічного завантаження DLL додатком


Наведемо тепер повністю вихідний код простого додатка, яке
використовує функцію MyFunction з бібліотеки MyDLL.dll, використовуючи динамічну
завантаження бібліотеки:

    #include <windows.h>
typedef int (WINAPI *PFN_MyFunction)(char *);

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HINSTANCE hMyDll;
if ((hMyDll = LoadLibrary ("MyDLL "))== NULL) return 1;

PFN_MyFunction pfnMyFunction;
pfnMyFunction = (PFN_MyFunction) GetProcAddress (hMyDll, "MyFunction");
int iCode=(*pfnMyFunction)("Hello");

FreeLibrary(hMyDll);
return 0;
}


Створення DLL


Тепер, познайомившись з принципами роботи бібліотек DLL в додатках,
розглянемо способи їх створення. При розробці додатку функції, до яких
звертається кілька процесів, бажано розміщувати в DLL. Це дозволяє більш
раціонально використовувати пам'ять в Windows.


Простіше за все створити новий проект DLL за допомогою майстра AppWizard, який
автоматично виконує багато операцій. Для простих DLL, таких як
розглянуті в цьому розділі, необхідно вибрати тип проекту Win32 Dynamic-Link
Library. Нового проекту будуть присвоєні всі необхідні параметри для створення
бібліотеки DLL. Файли вихідних текстів доведеться додавати до проекту вручну.


Якщо ж планується в повній мірі використовувати функціональні можливості
MFC, такі як документи та подання, або мають намір створити сервер
автоматизації OLE, краще вибрати тип проекту MFC AppWizard (dll). У цьому випадку,
крім присвоєння проекту параметрів для підключення динамічних бібліотек,
майстер виконає деяку додаткову роботу. У проект будуть додані
необхідні посилання на бібліотеки MFC і файли початкових текстів, що містять
опис і реалізацію в бібліотеці DLL об'єкта класу програми, похідного
від CWinApp.


Іноді зручно спочатку створити проект типу MFC AppWizard (dll) в якості
тестового додатку, а потім – бібліотеку DLL у вигляді його складової частини. У
результаті DLL у разі необхідності буде створюватися автоматично.


Функція DllMain


Більшість бібліотек DLL – просто колекції практично незалежних одна від
друга функцій, що експортуються в програми і використовуються в них. Окрім функцій,
призначених для експортування, у кожній бібліотеці DLL є функція
DllMain. Ця функція призначена для ініціалізації та очищення DLL. Вона
прийшла на зміну функцій LibMain і WEP, Що застосовувався в попередніх
версіях Windows. Структура найпростішої функції DllMain може виглядати,
наприклад, так:

 BOOL WINAPI DllMain (HANDLE hInst, DWORD dwReason, LPVOID IpReserved)
{
BOOL bAllWentWell=TRUE;
switch (dwReason)
{
case DLL_PROCESS_ATTACH: / / Ініціалізація процесу.
break;
case DLL_THREAD_ATTACH: / / Ініціалізація потоку.
break;
case DLL_THREAD_DETACH: / / Очищення структур потоку.
break;
case DLL_PROCESS_DETACH: / / Очищення структур процесу.
break;
}
if(bAllWentWell) return TRUE;
else return FALSE;
}

Функція DllMain викликається в декількох випадках. Причина її виклику
визначається параметром dwReason, який може приймати одне з наступних
значень.


При першому завантаженні бібліотеки DLL процесом викликається функція
DllMain з dwReason, рівним DLL_PROCESS_ATTACH. Кожного разу при створенні
процесом нового потоку DllMainO викликається з dwReason, рівним DLL_THREAD_ATTACH
(Крім першого потоку, тому що в цьому випадку dwReason дорівнює
DLL_PROCESS_ATTACH).


Після закінчення роботи процесу з DLL функція DllMain викликається з
параметром dwReason, рівним DLL_PROCESS_DETACH. При знищенні потоку (крім
першого) dwReason буде дорівнює DLL_THREAD_DETACH.


Всі операції з ініціалізації та очищення для процесів і потоків, в яких
потребує DLL, необхідно виконувати на підставі значення dwReason, як було
показано в попередньому прикладі. Ініціалізація процесів звичайно обмежується
виділенням ресурсів, спільно використовуваних потоками, зокрема завантаженням
поділюваних файлів і ініціалізацією бібліотек. Ініціалізація потоків застосовується
для налаштування режимів, властивих тільки даному потоку, наприклад для
ініціалізації локальної пам'яті.


До складу DLL можуть входити ресурси, які не належать зухвалому цю
бібліотеку з додатком. Якщо функції DLL працюють з ресурсами DLL, було б,
очевидно, корисно зберегти де-небудь у затишному місці дескриптор hInst і
використовувати його при завантаженні ресурсів з DLL. Покажчик IpReserved
зарезервований для внутрішнього використання Windows. Отже, додаток
не повинно претендувати на нього. Можна лише перевірити його значення. Якщо
бібліотека DLL була завантажена динамічно, воно буде дорівнює NULL. При статичній
завантаженні цей покажчик буде ненульовим.


У разі успішного завершення функція DllMain повинна повертати TRUE.
У разі виникнення помилки повертається FALSE, і подальші дії
припиняються.


Зауваження. Якщо не написати власної функції DllMain (), компілятор
підключить стандартну версію, яка просто повертає TRUE.


Експорт функцій з DLL


Щоб додаток міг звертатися до функцій динамічної бібліотеки, кожна
з них повинні займати рядок у таблиці експортованих функцій DLL. Є два
способу занести функцію в цю таблицю на етапі компіляції.


Метод __declspec (dllexport)


Можна експортувати функцію з DLL, поставивши на початку її опису
модифікатор __declspec (dllexport). Крім того, до складу MFC входить декілька
макросів, що визначають __declspec (dllexport), в тому числі AFX_CLASS_EXPORT,
AFX_DATA_EXPORT і AFX_API_EXPORT.


Метод __declspec застосовується не так часто, як другий метод, який працює з
файлами визначення модуля (. def), і дозволяє краще управляти процесом
експортування.


Файли визначення модуля


Синтаксис файлів з розширенням. Def в Visual C + + досить прямолінійний,
головним чином тому, що складні параметри, що використовувалися в попередніх версіях
Windows, в Win32 більше не застосовуються. Як стане ясно з наступного простого
прикладу,. def-файл містить ім'я і опис бібліотеки, а також список
експортованих функцій:

MyDLL.def
LIBRARY "MyDLL"
DESCRIPTION "MyDLL – приклад DLL-бібліотеки"

EXPORTS
MyFunction @1


У рядку експорту функції можна вказати її порядковий номер, поставивши перед
ним символ @. Цей номер буде потім використовуватися при зверненні до
GetProcAddress (). Насправді компілятор присвоює порядкові номери
всім експортуються об'єктів. Однак спосіб, яким він це робить, почасти
непередбачуваний, якщо не привласнити ці номери явно.


У рядку експорту можна використовувати параметр NONAME. Він забороняє
компілятору включати ім'я функції в таблицю експортування DLL:

        MyFunction     @1 NONAME

Іноді це дозволяє заощадити багато місця у файлі DLL. Програми,
використовують бібліотеку імпортування для неявного підключення DLL, не
"Помітять" різниці, оскільки при неявному підключенні порядкові номери
використовуються автоматично. Додатки, що завантажуються бібліотеки DLL динамічно,
буде потрібно передавати в GetProcAddress порядковий номер, а не ім'я
функції.


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

 # Define EXPORT extern "C" __declspec (dllexport)
EXPORT int CALLBACK MyFunction(char *str);
a таким:
extern "C" int CALLBACK MyFunction(char *str);

Експорт класів


Створення. Def-файла для експортування навіть простих класів з динамічної
бібліотеки може виявитися досить складною справою. Знадобиться явно
експортувати кожну функцію, яка може бути використана зовнішнім
додатком.


Якщо поглянути на реалізований в класі файл розподілу пам'яті, в ньому
можна помітити деякі досить незвичайні функції. Виявляється, тут є
неявні конструктори і деструктори, функції, оголошені в макросах MFC, в
Зокрема _DECLARE_MESSAGE_MAP, а також функції, які написані
програмістом.


Хоча можна експортувати кожну з цих функцій окремо, є більш
простий спосіб. Якщо в оголошенні класу скористатися макромодіфікатором
AFX_CLASS_EXPORT, компілятор сам подбає про експортуванні необхідних
функцій, які дозволяють додатком використовувати клас, що міститься в DLL.


Пам'ять DLL


На відміну від статичних бібліотек, які, по суті, стають частиною
коду програми, бібліотеки динамічного компонування в 16-розрядних версіях
Windows працювали з пам'яттю трохи інакше. Під управлінням Win 16 пам'ять DLL
розміщувалася поза адресного простору завдання. Розміщення динамічних бібліотек
в глобальній пам'яті забезпечувало можливість спільного використання їх
різними завданнями.


У Win32 бібліотека DLL розташовується в області пам'яті завантажує її
процесу. Кожному процесу надається окрема копія "глобальної" пам'яті
DLL, яка реініціалізіруется кожен раз, коли її завантажує новий процес.
Це означає, що динамічна бібліотека не може використовуватися спільно, в
загальної пам'яті, як це було в Winl6.


І все ж, виконавши ряд хитромудрих маніпуляцій над сегментом даних DLL,
можна створити загальну область пам'яті для всіх процесів, що використовують дану
бібліотеку.


Припустимо, є масив цілих чисел, який повинен використовуватися всіма
процесами, які завантажили дану DLL. Це можна запрограмувати таким
чином:

    #pragma data_seg(".myseg")
int sharedlnts[10] ;
/ / Інші змінні загального користування
#pragma data_seg()
# Pragma comment (lib, "msvcrt" "-SECTION:. Myseg, rws");

Всі змінні, оголошені між директивами # pragma data_seg (), розміщуються
в сегменті. myseg. Директива # pragma comment () – не звичайний коментар. Вона
дає вказівку бібліотеці виконує системи С помітити новий розділ як
дозволений для читання, запису і спільного доступу.


Повна компіляція DLL


Якщо проект динамічної бібліотеки створений за допомогою AppWizard і. Def-файл
модифікований відповідним чином – цього достатньо. Якщо ж файли проекту
створюються вручну або іншими способами без допомоги AppWizard, в командний рядок
редактора зв'язків слід включити параметр / DLL. У результаті замість автономного
виконуваного файлу буде створена бібліотека DLL.


При виборі. Def-файлі є рядок LIBRART, вказувати явно параметр / DLL в
командного рядку редактора зв'язків не потрібно.


Для MFC передбачено ряд особливих режимів, що стосуються використання
динамічної бібліотекою бібліотек MFC. Цьому питанню присвячено наступний
розділ.


DLL і MFC


Програміст не зобов'язаний використовувати MFC при створенні динамічних бібліотек.
Проте використання MFC відкриває ряд дуже важливих можливостей.


Є два рівні використання структури MFC в DLL. Перший з них – це
звичайна динамічна бібліотека на основі MFC, MFC DLL (regular MFC DLL).
Вона може використовувати MFC, але не може передавати покажчики на об'єкти MFC
між DLL і додатками. Другий рівень реалізований в динамічних
розширеннях MFC
(MFC extensions DLL). Використання цього виду динамічних
бібліотек вимагає деяких додаткових зусиль з налаштування, але дозволяє
вільно обмінюватися покажчиками на об'єкти MFC між DLL та програмою.


Звичайні MFC DLL


Звичайні MFC DLL дозволяють застосовувати MFC в динамічних бібліотеках. При цьому
додатки, які звертаються до таких бібліотекам, не обов'язково повинні бути
побудовані на основі MFC. У звичайних DLL можна використовувати MFC будь-яким способом, в
тому числі створюючи в DLL нові класи на базі класів MFC і експортуючи їх у
додатки.


Проте звичайні DLL не можуть обмінюватися з додатками покажчиками на
класи, похідні від MFC.


Якщо додатку необхідно обмінюватися з DLL покажчиками на об'єкти класів
MFC або їх похідних, потрібно використовувати розширення DLL, описане в наступному
розділі.


Архітектура звичайних DLL розрахована на використання іншими середовищами
програмування, такими як Visual Basic і PowerBuilder.


При створенні звичайної бібліотеки MFC DLL за допомогою AppWizard вибирається новий
проект типу MFC AppWizard (dll). У першому діалоговому вікні майстра
додатків необхідно вибрати один з режимів для звичайних динамічних
бібліотек: "Regular DLL with MFC statistically linked" або "Regular DLL using
shared MFC DLL ". Перший передбачає статичне, а другий – динамічне
підключення бібліотек MFC. Згодом режим підключення MFC до DLL можна буде
змінити за допомогою комбінованого списку на вкладці "General" діалогового вікна
“Project settings”.


Управління інформацією про стан MFC


У кожному модулі процесу MFC міститься інформація про його стан. Таким
чином, інформація про стан DLL відмінна від інформації про стан викликав
її застосування. Тому будь-які експортуються з бібліотеки функції, звернення до
яким виходить безпосередньо з додатків, повинні повідомляти MFC, яку
інформацію стану використовувати. У звичайній MFC DLL, що використовує динамічні
бібліотеки MFC, перед викликом будь підпрограми MFC на початку експортованої
функції потрібно помістити наступний рядок:

    AFX_MANAGE_STATE(AfxGetStaticModuleState()) ;

Даний оператор визначає використання відповідної інформації про
стані під час виконання функції, що звернулася до даної підпрограмі.


Динамічні розширення MFC


MFC дозволяє створювати такі бібліотеки DLL, які сприймаються
додатками не як набір окремих функцій, а як розширення MFC. За допомогою
даного виду DLL можна створювати нові класи, похідні від класів MFC, і
використовувати їх у своїх додатках.


Щоб забезпечити можливість вільного обміну покажчиками на об'єкти MFC
між додатком і DLL, потрібно створити динамічне розширення MFC. DLL цього
типу підключаються до динамічних бібліотек MFC так само, як і будь-які додатки,
використовують динамічне розширення MFC.


Щоб створити нове динамічне розширення MFC, простіше за все,
скориставшись майстром додатку, присвоїти проекту тип MFC AppWizard
(dll)
і на кроці 1 включити режим "MFC Extension DLL". У результаті новому
проекту будуть присвоєні всі необхідні атрибути динамічного розширення MFC.
Крім того, буде створена функція DllMain для DLL, що виконує ряд
специфічних операцій з ініціалізації розширення DLL. Слід звернути
увагу, що динамічні бібліотеки даного типу не містять і не повинні
містити об'єктів, похідних від CWinApp.


Ініціалізація динамічних розширень


Щоб "вписатися" у структуру MFC, динамічні розширення MFC вимагають
додаткової обслуговування. Відповідні операції виконуються
функцією DllMain. Розглянемо приклад цієї функції, створений майстром
AppWizard.

    static AFX_EXTENSION_MODULE MyExtDLL = { NULL, NULL } ;
extern "C" int APIENTRY
DllMain (HINSTANCE hinstance, DWORD dwReason, LPVOID IpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACED("MYEXT.DLL Initializing!
") ;
// Extension DLL one-time initialization
AfxInitExtensionModule(MyExtDLL, hinstance) ;

// Insert this DLL into the resource chain
new CDynLinkLibrary(MyExtDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACED("MYEXT.DLL Terminating!
") ;
}
return 1; // ok
}


Найважливішою частиною цієї функції є виклик
AfxInitExtensionModule. Це ініціалізація динамічної бібліотеки,
дозволяє їй коректно працювати у складі структури MFC. Аргументами даної
функції є переданий в DllMain дескриптор бібліотеки DLL і структура
AFX_EXTENSION_MODULE, що містить інформацію про підключається до MFC динамічної
бібліотеці.


Немає необхідності ініціалізувати структуру AFX_EXTENSION_MODULE явно.
Однак оголосити її потрібно обов'язково. Ініціалізацією ж займеться конструктор
CDynLinkLibrary. У DLL необхідно створити клас CDynLinkLibrary.
Його конструктор не тільки буде ініціалізувати структуру AFX_EXTENSION_MODULE,
але і додасть нову бібліотеку в список DLL, з якими може працювати MFC.


Завантаження динамічних розширень MFC


Починаючи з версії 4.0 MFC дозволяє динамічно завантажувати і вивантажувати DLL, в
тому числі і розширення. Для коректного виконання цих операцій над створюваної
DLL в її функцію DllMain в момент відключення від процесу необхідно
додати виклик AfxTermExtensionModule. Останньою функції в якості
параметр передається вже використовувалася вище структура AFX_EXTENSION_MODULE.
Для цього в текст DllMain потрібно додати наступні рядки.

    if(dwReason == DLL_PROCESS_DETACH)
{
AfxTermExtensionModule(MyExtDLL);
}

Крім того, слід пам'ятати, що нова бібліотека DLL є динамічним
розширенням і повинна завантажуватися і розвантажуватися динамічно, за допомогою функцій
AfxLoadLibrary і AfxFreeLibrary, А не LoadLibrary і
FreeLibrary.


Експорт функцій з динамічних розширень


Розглянемо тепер, як здійснюється експортування в додаток функцій і
класів з динамічного розширення. Хоча додати в DEF-файл все розширені
імена можна і вручну, краще використовувати модифікатори для оголошень
експортованих класів і функцій, такі як AFX_EXT_CLASS і
AFX_EXT_API, наприклад:

    class AFX_EXT_CLASS CMyClass : public CObject
(
// Your class declaration
}
void AFX_EXT_API MyFunc() ;

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


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

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

Ваш отзыв

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

*

*