Нижегородский файловый портал
RSS - каналы
Главное меню
Категории каталога
Мои статьи [5]
Школа покера [5]
Софт [40]
Радиолюбителям и электрикам [8]
Интернет [167]
Система [89]
Комплектующие ПК [47]
Безопасность [56]
Программирование [18]
Веб-дизайнеру [5]
Игры [6]
Полезные советы [24]
Кулинария [1]
Телефония [10]
Мобильник [17]
Планшеты [14]
Медицина [5]
Работа [4]
Домашнему мастеру [0]
Строительство и ремонт [19]
Для сада и огорода [2]
Юмор и приколы [12]
Интересное [114]
Пластики [3]
Разное [238]
Мини-чат
Правила мини-чата



Мини-чат в окне
Погода в Нижнем
Яндекс.Погода
Главная » Статьи » Программирование

Создание простого приложения с плагинами

В этой статье описываются принципы и решения, применяемые при проектировании приложений, которые будут использовать внешние, динамически подключаемые, модули. Эта статья более ориентирована на тех, кто хочет использовать механизмы подключения/отключения функциональности приложения, наподобие механизма Adobe Photoshop или Far, а не просто многократного использования кода в разных приложениях.

Динамически подключаемые модули (DLL) - это модули, которые содержат функции и данные. Эти модули загружаются во время выполнения программы, использующей эти модули (хоста). В ОС Windows модули содержат внутренние и экспортируемые функции (в UNIX подобных системах все функции являются экспортируемыми). Экспортируемые функции доступны для вызова хостом, а внутренние нет. Хотя данные тоже могут быть экспортируемыми, но обычно используются экспортируемые функции для доступа данным.

Некоторые, особенно начинающие разработчики ПО, и не представляют, что при создании приложения, уже используют внешние модули. Хотя при разработке MFC приложений этот факт более очевиден. Просто компилятор сам вставляет код, который загружает системные библиотеки, иначе любое Windows приложение было бы на 20-30 Мб больше.

Итак, перейдем непосредственно к созданию механизма для использования в Ваших приложениях плагинов.

Создайте новое DLL приложение (Builder  и Visual C позволяют выбрать тип при создании нового проекта).

Каждая библиотека имеет точку входа (но можно ее и не описывать), как функция main() в обычном приложении. Вот обычное ее описание:

HINSTANCE hDllInstance=NULL;
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)

hDllInstance = (HINSTANCE)hModule;

return TRUE;
}

При присоединении к вызвавшему ее процессу, в эту функцию передается instance. Т.е. Вы сможете использовать ресурсы библиотеки. Используйте в этой функции только простые задачи по инициализации! Эта функция очень уязвимое место в модуле.

Для связи хоста и модуля предлагаю использовать две функции: первая функция будет взаимодействовать с хостом при инициализации (определять версию, совместимость с текущей версией хоста, передавать текст для пункта в меню и т.д.), а вторая будет обслуживать запросы хоста и реагировать на переданные данные: анализировать, обрабатывать и возвращать результат соей работы.

Другой подход в написании плагинов - использовать несколько различных функций, для каждого из действий, но лично мне нравится первый.

Например, один из возможных вариантов этих двух функций:

#ifdef __cplusplus
extern "C" {
#endif

__declspec(dllexport) void GetPluginInfo(PluginInfo* pPluginInfo,
DWORD *pdwResult);
__declspec(dllexport) void PluginHandler(DWORD dwCode,
HostInfo *pHostInfo,
DWORD *pdwResult);

#ifdef __cplusplus
}
#endif

Ключевые слова __declspec( dllexport ) обозначают, что функции являются экспортируемыми.

Этот блок определений можно помещать в самом исходном файле, после секции подключения заголовочных файлов.

Вот пример структуры, передаваемой при инициализации:

struct PluginInfo {
// вид выполняемой плагином операции
// (если предусматривается несколько типов плагинов)
DWORD m_dwPluginType;
// передадим пункт меню для нашего плагина
char * m_pcMenuString;
unused[64];
};

А вот пример функции, которая находится в плагине и заполняет эту структуру:

void GetPluginInfo(PluginInfo* pPluginInfo, DWORD *pdwResult) {

pPluginInfo->m_dwPluginType=5;
pPluginInfo->m_pcMenuString="/Мой плагин";
*pdwResult=0;
}

Функция-обработчик в плагине:

void PluginHandler(DWORD dwCode,HostInfo *pHostInfo,DWORD *pdwResult); 

Первый параметр будет определять цель, с которой вызвана функция, второй параметр определяет входные данные, причем для каждого кода могут быть инициализированы различные члены структуры. Ну а третий параметр - результат работы.

void PluginHandler(DWORD dwCode,HostInfo *pHostInfo,DWORD *pdwResult) {

switch(dwCode) {
case 1:
//первое действие
*pdwResult=1;
break;
case 2:
//второе действие
*pdwResult=1;
break;
default: *pdwResult=0;
}
}

Если плагин не знает переданного кода операции, он просто вернет код "Не поддерживается" и не выполнит некорректных действий.

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

Загрузим библиотеку хостом:

// приходится создавать тип для каждой экспортируемой функции
typedef void (*GetPluginInfoType)(PluginInfo*);
typedef void (*PluginHandlerType)(HostInfo*);

HMODULE hLib=LoadLibrary("MyLib.dll");
if (hLib==NULL) {
// тут обрабатываем ошибку, если библиотека не загрузилась
return FALSE;
}

GetPluginInfoType GetPluginInfo;
GetPluginInfo=(GetPluginInfoType)GetProcAddress(m_hInstance,"GetPluginInfo");
if (GetPluginInfo==NULL) {
FreeLibrary(hLib);
return FALSE;
}
DWORD dwResult;
PluginInfo PluginNfo;
memset(&PluginNfo,0,sizeof(PluginInfo));
GetPluginInfo(&PluginNfo,&dwResult);
// тут анализируем заполненную в плагине структуру
// (создаем меню для плагина, резервируем память и т.д.)

Итак, плагин загружен и готов к работе, ожидаем когда пользователь выберет пункт меню.

Помещаем в обработчик меню следующий код:

PluginHandlerType PluginHandler;
// получаем адрес функции обработчика в плагине
PluginHandler=(PluginHandlerType)GetProcAddress(m_hInstance,"PluginHandler");
if (PluginHandler==NULL) return FALSE;
// подготавливаем структуры с данными,
// которые необходимо передать в плагин для обработки
HostInfo HostNfo;
memset(&HostNfo,0,sizeof(HostInfo));
// если в плагинах будут создаваться окна,
// то необходимо передать HWND главного окна в качестве родительского
HostInfo.m_hHostWnd=theApp->m_pMainWnd->GetSafeHwnd();
// передаем адреса функций, реализованных в хосте
HostInfo.IPShowProgress=::ShowProgress;
DWORD dwResult;
try { // желательно поставить обработчик исключений
// вызываем функцию-обработчик в плагине
PluginHandler(1,&HostNfo,&dwResult);
} catch(...)
{
AfxMessageBox("В модуле произошла необрабатываемая ошибка.");
ASSERT(0);
return FALSE;
}
// не забывайте выгружать библиотеки по завершении работы хостом
FreeLibrary(hLib);

Вот собственно и все описание простого примера использования плагинов в своих программах.

Я хочу добавить несколько советов для разработчиков:

  • если планируется использовать много плагинов, то разумно будет сначала получить первоначальную информацию от плагина, а затем его выгрузить из памяти. И подгружать его в случае надобности.
  • если пользователь вводит некоторые параметры в диалогах плагинов, то разумно разработать механизм централизованного хранения последних введенных параметров.
  • делайте возможность помещения плагинов в произвольные папки внутри папки плагинов. Код рекурсивного поиска плагинов приведен ниже.
  • Задайте своим плагинам отличное от "dll" расширение. Т.к. сами плагины могут использовать внешние dll библиотеки.

А вот пример функции, которая рекурсивно находит все файлы в папке с плагинами:

// Массив со всеми файлами, включая путь относительно папки с плагинами
CStringArray PluginsArray;
// задается путь к папке с плагинами
CString sPlugInsPath="Plugins\\";
void GetPlugInFiles(CString sPath) {
if (PluginsArray.GetSize()>=512) return;
CString sStr;
CString sCurFullPath=sPlugInsPath;
sCurFullPath+=sPath;
sCurFullPath+="*";
WIN32_FIND_DATA FindData;
HANDLE hFindFiles=FindFirstFile(sCurFullPath,&FindData);
if (hFindFiles==INVALID_HANDLE_VALUE) return;
for(;;) {
if ((strcmp(FindData.cFileName,".")!=0) && (strcmp(FindData.cFileName,"..")!=0)) {
if (FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) {
sStr=sPath;
sStr+=FindData.cFileName;
sStr+="\\";
GetPlugInFiles(sStr);
}
else {
char *ptr=strrchr(FindData.cFileName,'.');
if (ptr) {
if (strlen(ptr)==4) {
if (ptr[1]=='x' && ptr[2]=='x' && ptr[3]=='x') {
CString sPath1=sPath;
sPath1+=FindData.cFileName;
PluginsArray.Add(sPath1);
}
}
}
}
}
if (!FindNextFile(hFindFiles,&FindData)) break;
}
FindClose(hFindFiles);
}


// так вызывается функция. После такого вызова массив
// PluginsArray будет заполнен
GetPlugInFiles("");
Добавил: Админ-21NN | Просмотров: 1344 | Рейтинг: 5.0/1


Обратите Ваше внимание на другие статьи:

Уважаемые пользователи, пожалуйста, оставляйте комментарии! Нам очень важно Ваше мнение!
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.

    
Меню пользователя
Аватар гостя

Приветствуем Вас, Гость

Логин:
Пароль:
Поиск по сайту
Поиск по названию
Поиск по тегам
Горячие темы форума
Зарабатываем деньги
Детская игра Подарки...
Тест скорости подклю...
кое что о Windows
Кто ты, человек?
Новая валюта портала
Все о сексе
"Что мешает нам...
Культура
Афоризмы
Лучшие 13 анекдотов ...
как защитить свой ко...
поговорим о софте
восстановление данны...
Я ненавижу Дом-2
Волга-Телеком
Кулинария "Кокт...
Стол заказов
Жалобы
С Днем Победы!!!
Прикольные картинки
С праздником Пасхи !...
Статистика
Новых за месяц: 149
Новых за неделю: 42
Новых вчера: 6
Новых сегодня: 3
Всего: 5314
Из них:
Администраторов: 3
$$$-Модераторов: 1
Модераторов: 3
Прокураторов: 3
-----------------
далее:
Проверенных: 247
Пользователей: 2884
Новичков: 1880
Заблокированных: 100
-----------------
Из всех пользователей:
Мужчин и парней: 4153
Женщин и девушек: 1160
Именинники
Поздравляем с Днем рожденья:

amidala(33), cino(26), avinb(35), vic2011(52), Aleksty(58), cerick(46), AmirZager(17), rider(30), kirikxxx(33), shadochron(34), salkovs1(63), maxmin(30), varvar06(35), rena7271(46)
Режим ON-LINE
Онлайн всего: 4
Гостей: 4
Пользователей: 0

Сейчас на портале:
Наша кнопочка
Нижегородский файловый портал

HTML-код кнопки:
Реклама
Размещение рекламы

Яндекс.Метрика
Регистрация сайта в каталогах, раскрутка и оптимизация сайта, контекстная реклама Ремонт холодильников в Нижнем Новгороде

Copyright © BankRemStroy © 2009-2017