Завдання: створити плагін для перегляду RTF файлів.

Створюємо проект DLL бібліотеки і зберігаємо його під ім'ям ListSimpleBcb.bpr в
окремій папці, змінимо розширення імені плагіна на WLX в опціях проекту.


Модуль Unit1.cpp зберігаємо під ім'ям ListSimple.cpp.


Плагін повинен експортувати з бібліотеки три функції:

ListGetDetectString,
ListLoad,
ListCloseWindow.

ListGetDetectString повинна записати в параметр DetectString рядок, яка
містить RTF.


ListLoad викликає плагін для роботи і передає параметри:

 ListerWin – дескриптор вікна Лістера;
FileToLoad – повне ім'я RTF файлу.

Плагін повинен створити своє вікно в якості дочірньою по відношенню до вікна
Лістера і повернути дескриптор цього вікна. Цю ініціалізацію ми будемо здійснювати
у функції ShowRTF:

HWND ShowRTF(HWND ListerWin, char* FileToLoad);

ListCloseWindow вимагає від плагіна завершення роботи, передаючи дескриптор
вікна плагіна. Це звільнення ресурсів ми будемо робити у функції HideRTF:

void HideRTF(HWND PluginWin);

Треба додати в проект форму, змінити її ім'я на fmMain і зберегти модуль
форми під ім'ям unMain.cpp. Ця форма і є вікно плагіна.


У заголовний файл unMain.h треба додати визначення двох вищенаведених
функції і уда-лити марну глобальну змінну:

extern PACKAGE TfmMain *fmMain;

Зауваження: Плагін НЕ ПОВИНЕН ВИКОРИСТОВУВАТИ ГЛОБАЛЬНІ
ЗМІННІ!


Кинувши на форму компонент RichEdit, треба встановити йому такі властивості:

Align = alClient,
ReadOnly = True,
ScrollBars = ssBoth.

Об'єкт RichEdit1 забезпечує всю роботу з RTF файлом.


У плагінах часто використовується контекстне меню, тому треба кинути на форму
компонент PopupMenu і створити два пункти меню Help і About, призначивши їм клавіші
F1 і F2, відповідно. Необхідно прикріпити контекстне меню об'єкта
RichEdit1, встановивши властивість RichEdit1:

PopupMenu = PopupMenu1.

Вікно плагіна є дочірнім, отже, воно не повинно мати рамку і
заголовок. Для цього треба перевизначити віртуальну функцію CreateParams класу
TCustomForm. Тут, ми окрім налаштування стилів вікна:

 Params.Style = WS_CHILD | WS_MAXIMIZE &! WS_CAPTION &! WS_BORDER;

зарезервуємо пам'ять для зберігання покажчика на нашу форму:

Params.WindowClass.cbWndExtra = sizeof(void *);

Покажчик потрібен функції HideRTF для коректного закриття плагіна. У функції
ShowRTF ми зберігаємо покажчик у WinAPI структурі вікна плагина наступним
викликом:

SetWindowLong(fmMain->Handle, GWL_USERDATA, (LONG)p);

Пізніше ми витягнемо покажчик зі структури, використовуючи дескриптор нашого вікна,
який є параметром HideRTF, викликаючи функцію GetWindowLong:

GetWindowLong(PluginWin, GWL_USERDATA);

Щоб створити дочірнє вікно скористаємося статичної функцією
CreateParentedControl класу TWinControl, тобто в тілі функції функція ShowRTF
скористаємося наступним оператором:

 fmMain = (TfmMain *) (TWinControl:: CreateParentedControl (__classid (TfmMain), ListerWin));

Для роботи плагіна нам знадобляться наступні дані: дескриптори вікон Тотал
Командира і Лістера, і режим роботи плагіна: у звичайному вікні або на одній з двох
панелей Тотал Командира (так званий Quick View – режим швидкого перегляду).
Дескриптори потрібні, щоб уникнути краху по клавішах виходу Alt + X і для
реагування на швидкі клавіші Лістера. Ці дані будемо зберігати,
відповідно, в змінних TotCmdWin, ParentWin і QuickView. Додамо їх
визначення в public розділ класу форми у файлі unMain.h. Дескриптор вікна Тотал
Командира ми можемо знайти на ім'я класу цього вікна (це не VCL клас!) З
допомогою WinAPI функції FindWindow.


Трохи теорій. Особливістю бібліотеки візуальних компонентів Borland VCL
є ис-користування глобального об'єкта "додаток" – Application. Цей
Application має приховане вікно, яке має бути власником всіх вікон
додатки, що забезпечує коректну поведінку всіх вікон програми. І DLL
бібліотека плагіна, і виконуваний EXE файл Тотал Командира мають власний
глобальний об'єкт Application, отже, потрібно їх синхронізація. Для
цього треба привласнити дескриптор прихованого вікна об'єкта Application виконуваного
файлу до дескриптору об'єкта Application бібліотеки. На жаль, Тотал Командир,
хоча він написаний на Delphi і використовує всі зручності і принади VCL, не передає
нам дескриптор даного прихованого вікна. (Може причиною цього є проблема
версії компіляторів Delphi?). Через брак воно-го, замість нього будемо
використовувати для синхронізації дескриптор вікна Лістера, в іншому слу-чаї,
наше модальне вікно (наприклад, вікно About) поведе себе неприродно, воно
з'явиться на панелі завдань Windows. Ця синхронізація є завданням для функції
ShowRTF.


Щоб захистити, Тотал Командир від збоїв плагіна, будемо використовувати подію
OnException об'єк-єкта Application, яке перехоплює необроблене
виняток. Кинемо на форму компонент ApplicationEvents, перейменуємо його на App і
створюємо обробник події OnException, куди введемо виклик функції MessageBox для
видачі повідомлення про збій. Зберігши проект і модуль, можна уда-лити з форми
непотрібний компонент ApplicationEvents. Установка адреси обробника події в
Application.OnException також є завданням для функції ShowRTF.


Все готово для ShowRTF? На жаль, "маленька неточність" у Plugin API Тотал
Командира (включаючи v.6.02) значно ускладнює нашу роботу:


Якщо користувач відкриє вікно плагіна, потім переключиться на Тотал Командир
і закриє Тотал Командир, то ми не отримаємо виклик через ListCloseWindow для
завершення своєї роботи. Замість цього Тотал Командир або Лістер (хто його знає?)
знищує наше вікно викликом WinAPI функції DestroyWindow, а потім, те ж саме
спробує зробити наш об'єкт Application, в кінцевому рахунку плагін вилітає з
порушенням захисту пам'яті (General Protection Fault)!


Якщо ми не будемо робити вищеописану синхронізацію, цієї проблеми вдалося б
уникнути. Що ж, будемо розплачуватися за це і поставимо пастку для перехоплення
повідомлень, яке получа-ет наше ж вікно, подібно до змії, яка пожирає себе
за свій власний хвіст! Не будемо вда-тися до подробиць Windows API (кому
ж він подобатися?), коротенько суть справи така.


Оголошуємо структуру TPlugInfo, де будемо зберігати дані, які потрібні для
закриття плагіна. При ініціалізації виділяємо пам'ять під цю структуру і
заповнюємо її. Визначаємо функцію HookDestroy, який буде перехоплювати
віконні повідомлення, щоб зреагувати на повідомлення WM_DESTROY (знищення
вікна). Викликом SetWindowLong (:, GWL_WNDPROC,:) підміну-ем стандартний
обробник віконних повідомлень на функцію HookDestroy. Схожим викликом
SetWindowLong у функції завершення HideRTF назад відновлюємо колишній
обробник.


Важливо те, що коли функція HookDestroy зловить повідомлення WM_DESTROY, вона
викликає функцію завершення HideRTF точно так само, як це робить функція
ListCloseWindow. У результаті ми завжди встигаємо нормально закрити вікно і прибрати
за собою, що, приміром демонструє free (p) з HideRTF, звільняючи пам'ять,
виділену в ShowRTF.


Залишається створити діалогову форму About, обробники контекстного меню і
обробник RichEdit1KeyDown, який ловить натиснення спеціальних клавіш і передає
їх за допомогою PostMessage у відповідне вікно. Якщо тут не перехоплювати
клавіші Alt + X, це призведе до краху плагіна. Причому, тут доведеться утримувати
фокус введення в RichEdit1, перевіряючи режим робо-ти плагіна, тобто значення
змінної QuickView.


Про налагодженні і тестуванні плагіна.


Лістер має вбудовану можливість перегляду RTF файлів, тому перед
запуском плагіна треба відключити її, знявши позначку у прапора RTF у вікні Configure
Lister.


Щоб використовувати інтегрований відладчик IDE BCB, закрийте Total
Commander, а в BCB через меню "Run / Parameters" відкрийте діалог і в полі Host
Application введіть шлях до Total Commander з допомогою кнопки [Browse]. Тепер
можна запускати плагін з-під BCB.


Посилання для скачування архіву з прикладом тут


PS. Шановний Читач! Програмування – багатоваріантно і я ніяк не
претендую на абсолютну істину. В якості додаткового матеріалу, пропоную
Вам ознайомитися з вихідними текстами плагіна xBaseView – "Перегляд DBF, DB,
MDB, ADO і ODBC баз даних ", який надається з вихідними текстами, і де
застосовується аналогічний підхід.


PPS. Прошу Читача-Поліглота допомогти мені перекласти статтю на англійську мову,
щоб надіслати її Крістіану Гіслер, авторові знаменитого файл менеджера Тотал
Командир, з примарною надією, що він зробить якісь кроки назустріч нам –
VCL програмістам.

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


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

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

Ваш отзыв

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

*

*