Використання DLL, Додатки, DLL, COM, ActiveX, C / C + +, статті

Практично неможливо створити додаток 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>

*

*