Большой архив статей, книг, документации по программированию, вебдизайну, компьютерной графике, сетям, операционным системам и многому другому
 
<Добавить в Избранное>    <Сделать стартовой>    <Реклама на сайте>    <Контакты>
  Главная Документация Новости ИТ Программы Книги Games   Обои   Экспорт RSS E-Books
 
Поиск по сайту

TOP-10 программ
Symantec Norton Ghost 9.0
Partition Magic 8.0.2 Pro
Xilisoft 3GP Video Converter v3.1.7.0616b
Norton AntiVirus 2005
Xilisoft 3GP Video Converter v2.1.52.831b
Антивирус Касперского Personal 5.0.303 beta 2
RAR Password Cracker 4.12
ABBYY PDF Transformer v1.00.820
MP3 To Ringtone Gold v3.02
Mobtime Cell Phone Manager v5.3.1
 
Наши сервисы
Рассылка новостей. Подпишитесь на рассылку сейчас и вы всегда будете в курсе последних событий в мире информационных технологий.
Новостные информеры. Поставьте наши информеры к себе и у вас на сайте появится дополнительный постоянно обновляемый раздел.
Добавление статей. Если вы являетесь автором статьи или обзора на тему ИТ присылайте материал нам, мы с удовольствием опубликуем его у себя на сайте.
 
 

   Программирование -> C / C++ -> Создание простого приложения с плагинами


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

Автор: Покрашенко Александр

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

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

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

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

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

Каждая библиотека имеет точку входа (но можно ее и не описывать), как функция 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));
HostInfo.m_hHostWnd=theApp->m_pMainWnd->GetSafeHwnd(); // если в плагинах будут создаваться окна, то необходимо передать HWND главного окна в качестве родительского
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);

}


GetPlugInFiles(""); // так вызывается функция. После такого вызова массив PluginsArray будет заполнен

 

Покрашенко Александр
Владивосток
bestx@mail.ru



 
Популярные книги

Системное администрирование на 100 % (+CD)

Подробнее

Дизайн помещений и интерьеров в 3ds max 7 (+CD)

Подробнее

Самоучитель Macromedia Flash 5

Подробнее

 

 
Новости ИТ
01.12.2008  Buffalo выпустил миниатюрные USB-накопители
01.12.2008  VENTO TA-U1 - стильный корпус представлен Asus
01.12.2008  Fujitsu-Siemens выпускает в продажу внешний ускоритель для ноутбуков
01.12.2008  Оригинальные чехлы для ноутбуков от Choiix
01.12.2008  Опубликован код драйвера для беспроводных карт Atheros
01.12.2008  Лучший блог 2008
01.12.2008  Linux запустили на Apple iPhone
01.12.2008  LG KC780
01.12.2008  MSI дополнит линейку Wind-нетбуков двумя моделями
01.12.2008  Nikon D3X - 24,5 млн пикселей для профессионалов
01.12.2008  Киловаттник HIPER M1000 с КПД выше 85%
01.12.2008  AMD впервые снизила цены линейки Radeon HD 4800
01.12.2008  Чистильщики: Wise Registry Cleaner v.3.8.2
01.12.2008  Антивирусы: RemoveIT Pro v4 SE (30.11.2008)
01.12.2008  Корпус ASUS Vento TA-U1 можно поставить вместо новогодней ёлки
01.12.2008  Диагностика: PC Wizard 2008 v.1.871
01.12.2008  Диагностика: NextSensor v.2.7.6.0 Build 1130
01.12.2008  Тестовые приложения: PassMark BurnInTest v.6.0 Build 1000 Beta 15
01.12.2008  Неофициальные драйверы для модемов Motorola
01.12.2008  Драйверы и утилиты для сетевых хранилищ D-Link
 
Полезно

 
Copyright © CompDoc.Ru
При цитировании и перепечатке ссылка на www.compdoc.ru обязательна. Карта сайта.
 
Rambler's Top100