![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
ZVano |
|
||||||||||||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 259 Регистрация: 11.12.2006 Где: Украина, Кривой Р ог Репутация: нет Всего: 4 |
Пишу движек плагинистого приложения.
Он уже работает, но гложут смутные сомнения - не напортачил ли я при проектировании... Нужна оценка профессионалов, которые уже сталкивались с подобной задачей. Идеология такова:
Во главе системы стоит заголовочный файл "ZIMPluginInterfaces.h", который описывает базовые интерфейсы плагинов и менеджера плагинов. Менеджер плагинов - объект из DLL, который является наследником "IPluginsManager" либо "IPluginsManager_1"либо "IPluginsManager_2" и т.д. Плагин - объект из DLL, который является наследником "IPlugin" либо "IPlugin_1"либо "IPlugin_2" и т.д. Содержимое "ZIMPluginInterfaces.h":
Менеджер плагинов в DLL "ZIMPluginsManager.dll"
Заголовок реализации внутри "ZIMPluginsManager.dll"
Программа-оболочка, которая хочет использовать систему плагинов обязана выполнить код, подобный следующему:
Ну и пример плагина "PluginShell". "PluginDriveSchoolDb.cpp" - главный файл плагина "PluginShell". Объявлена точка входа в DLL "DllEntryPoint" и функция получения интерфейса плагина "GetPluginInterface".
"TPluginShell.H" - заголовочный файл реализации плагина. Доступен только внутри плагина "TPluginShell". Реализовывает публичный интерфейс "IPluginShell_1", который объявлен в публичном заголовочном файле "IPluginShell.h"
"IPluginShell.h" - публичній заголовочній файл плагина "PluginShell". Все другие плагины, которые хотят использовать "PluginShell", обязаны заинклудить "IPluginShell.h"
PS: Если найдутся желающие поковырять код - пишите. Выложу весь проект и напишу пару тестовых плагинов. Это сообщение отредактировал(а) ZVano - 16.9.2011, 12:16 -------------------- НЕ ФЛУДИМ. Пользуемся кнопками "+" или "-" для выражения своего отношения к теме или сообщению. Гуглим "Как правильно задавать вопросы" |
||||||||||||||||
|
|||||||||||||||||
borisbn |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 4875 Регистрация: 6.2.2010 Где: Ростов-на-Дону Репутация: 22 Всего: 135 |
1. Интерфейсам нужно явно указать выравнивание.
2. Всем функциям нужно указать явный тип вызова
3. Зачем в IPlugin все переменные в public ? Они реально все нужны в основной программе ? 4. Я бы добавил ф-цию получения displayName плагина. Т.е. Система работает с плагином по pluginName, а сообщения пользователю выдаёт из displayName. Ну, это так... на первый взгляд -------------------- Женщины отличаются от программистов тем, что у них чары состоят из стрингов |
||||
|
|||||
ZVano |
|
||||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 259 Регистрация: 11.12.2006 Где: Украина, Кривой Р ог Репутация: нет Всего: 4 |
Спасибо за комментарий.
Не понимаю зачем. Ушел курить маны... __stdcall нужен даже несмотря на то, что IPlugin_Init является методом структуры? Всегда считал, что помечать __stdcall нужно только экспортные функции DLL. ![]()
У базовых интерфейсов плагинов все открыто чтобы иметь прямой доступ к полям отовсюду. Запрячеш поле, а потом окажется что к нему нужен доступ, а переделывать базовые интерфейсы... ахтунг. Осознаю, что некорректно написаный плагин (или специально сделанный) может нарушить работу всей системы плагинов, но не уверен что от этого следует защищаться. 1. Текущий вариант
2. Максимально защищаясь можно сделать так:
Нужно хорошо подумать, может все же следует переделать на вариант 2.
Не понимаю. Зачем пользователю вообще видеть имя плагина? Или пользователь - программист, который пользуется чужими плагинами? -------------------- НЕ ФЛУДИМ. Пользуемся кнопками "+" или "-" для выражения своего отношения к теме или сообщению. Гуглим "Как правильно задавать вопросы" |
||||||||
|
|||||||||
mes |
|
|||
любитель ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 7954 Регистрация: 14.1.2006 Репутация: 144 Всего: 250 |
чтоб например в меню выбирать, или отключать.. |
|||
|
||||
newbee |
|
|||
![]() Бревно ![]() ![]() Профиль Группа: Участник Сообщений: 703 Регистрация: 24.8.2011 Репутация: 4 Всего: 19 |
Здравствуйте, мои дорогие любители избушек на курьих ножках.
Раньше я тоже была поглощена идеей поработить мир написать убер систему плагинов и пришла к следующему выводу: на С++ сделать одновременно и универсальную, и удобную систему не получится. Что значит универсально? Это возможность плагином внесения любых (читай максимально широких) изменений в ход работы программы и других плагинов. Удобно - значит не париться с магическими идентификаторами плагинов и кодами действий при посылании сообщений другим участникам системы, в идеале - получение полного C++-API любой подсистемы из любой другой. Таскать за каждым плагином заголовочные файлы - не вариант: система из уже полусотни компонент, особенно разных авторов, будет едва ли подъемной, а сопровождать ее и обновлять часть ее подсистем станет просто нереально, а встанет задача поддержки плагином нескольких версий другого - ващще жопа. В рантайме экспортировать все необходимые функции по одной - уже несколько проще, но опять же трудоемко, прощай ООП-интерфейс, прощай name mangling, привет геммор с выяснением типов и количеством аргументов (и привет, бусткодер). Намного проще расширять программу с помощью встроенного скриптового языка, для краткости назовем его lisp. Из основной программы даем лиспу доступ ко всему необходимому, далее наращиваем функционал системы уже на лиспе, на нем же реализуем систему плагинов, благодаря динамичной природе которого лишенную недостатка строгого следования API и ABI между подсистемами. И на нем же пишем плагины, а какие-то критичные части всегда можно вынести в код на С++. Плюсы очевидны: писать на лиспе намного проще и быстрее, чем на С++; подсистемы общаются между собой на нормальном языке; а не каком-то жалком обрубке вроде системы сообщений; большинство плагинов будут написаны на одном только лиспе, а значит мы получаем офигенскую кроссплатформенность. Все на правах ИМХи и ващще мне уже пора бежать. Чао! Это сообщение отредактировал(а) newbee - 16.9.2011, 21:21 -------------------- You're face to face With man who sold the world |
|||
|
||||
borisbn |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 4875 Регистрация: 6.2.2010 Где: Ростов-на-Дону Репутация: 22 Всего: 135 |
Смотри... ты даёшь мне эти h-ники, чтобы я сделал како-то плагин. У тебя в проекте главного приложения (билдер, как я понимаю) по-умолчанию выставлено выравнивание структур на 8 байт. Я создаю проект для плагина в студии, и у меня выравнивание 4 байта. Интерфейсы, которые я создам, на бинарном уровне не будут соответствовать тем, которые ты ожидаешь. Эпик фэйл.
![]() Абсолютно то же, что и по п. 1
Скорее наоборот, создатель плагина не может быть уверен, что заполненные им переменные не изменятся от вызова к вызову, что основная программа не будет их менять. почему не const char *actionName ? Теперь следующее - ты достаточно широко проработал систему подключения плагинов, но обычно плагины должны выполнять какие-то действия над данными, имеющимися в основной программе. Например в фотошопе плагинам передаётся редактируемая картинка, в браузерах - HTML, в плеерах - видеопоток и т.д. У тебя же я этого не нашёл. И ещё. Из своего опыта. Плагинам иногда необходимо сохранять своё состояние или считывать некие настройки. Они это могут делать сами (тупо открывать ini-шник с заранее известным именем и читать/писать его), но может создаться ситуация, когда потребуется подключить 2 экземпляра одного и того же плагина или 2 разных плагина решат назвать ini-шник config.ini.... Чтобы избежать этого необходимо, чтобы основная программа предоставляла плагинам интерфейс сохранения/считывания настроек. Типа такого
Добавлено через 9 минут и 49 секунд И ещё. Но это уже не относится к системе, а скорее к правилу хорошего тона (что тоже не маловажно). Обычно принято public-члены классов объявлять в начале описания класса, а private (или protected) - в конце. Людям, которым ты отдаёшь класс для использования, не интересно какие защищённые переменные есть в классе, им интересно как им использовать этот класс, т.е. public-члены. -------------------- Женщины отличаются от программистов тем, что у них чары состоят из стрингов |
||||||||
|
|||||||||
ZVano |
|
||||||||||||||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 259 Регистрация: 11.12.2006 Где: Украина, Кривой Р ог Репутация: нет Всего: 4 |
Благодарю за пояснения. Мое упущение. Надо будет добавить const всем константным параметрам, пока не плагинов немного.
Плагины взаимодействуют друг с другом напрямую через метод IPlugin_MessageParse.
Т.е. применительно к фотошопу плагин, обрабатывающий картинку, обязан: 1. быть версии "IPlugin_2" или выше 2. содержать объявление наследника от TPluginMessage, который содержит все необходимые поля для выполнения действия (либо осуществления коммуникации, если подразумевается обратная связь). Пример ниже. 3. Реализовать в методе "MessageParse" распознавание кода fMsgCode и передаци управления функции, которая обслуживает сообщения данного типа. Напомню интефейс базового сообщения класса TPluginMessage. Наследник обязан назначить свой код "DWORD fMsgCode". Код уникален в пределах плагина, который обслуживает сообщение.
Пример реализации коммуникации между плагином IPhotoshopEditor - редактор изображения (формочка с рисунком) и IEffectDegradation - эффект размытия изображения. Загрузка программы: 1. Оболочка грузит все плагины, зарегестрированые в секции "Автозагрузка" конфигурационного файла. >>Среди них плагин IEffectDegradation. 1.1. Дошла очередь до загрузки IEffectDegradation. 1.1.1. Оболочка загружает DLL с плагином, и получает интерфейс IEffectDegradation вызвав функцию DWORD GetPluginInterface(IPlugin ** out_Plugin); из DLL. 1.1.2. Оболочка приводит "IPlugin ** out_Plugin" к IPlugin_2 (т.к. out_Plugin->IPluginVersion == 2) 1.1.3. Оболочка вызывает метод IEffectDegradation->"virtual DWORD IPlugin_Init(void) = 0;", который достался IPlugin_2 от IPlugin_1. >>Выполняется инициализация IEffectDegradation
После инизиализации плагинов главное меню оболочки будет содержать пункты меню всех плагинов автозагрузки, которые поддерживают работу с оболочкой и попросили ее (оболочку) зарегистрировать свое меню. 2. Работа приложения. 2.1. Пользователь клоцает мышей по пункту меню. Допустим, это будет пункт "Загрузить изображение", который принадлежит плагину IPhotoshopEditor. 2.1.1. Оболочка ищет ассоциацию "Пункт меню" == "Плагин\Действие". 2.1.2. Оболочка вызывает метод IPhotoshopEditor->IPlugin_DoAction("Название действия, загруженное ранее из конфигурационного файла"); 2.1.2.1. Плагин IPhotoshopEditor показывает диалог выбора файлов 2.1.2.2. Плагин IPhotoshopEditor создает новое окно редактора и открывает выбранный пользователем файл. 2.1.2. Возврат управления плагину "Оболочка". 2.2. Пользователь клоцает мышей по пункту "Применить эффект "Размытие"". 2.2.1. Оболочка ассоциирует пункт меню с плагином "IEffectDegradation", действием "Размыть изображение". 2.2.2. Оболочка вызывает метод IEffectDegradation->IPlugin_DoAction("Размыть изображение"); 2.2.3. Плагин IEffectDegradation выполняет действие. 2.2.3.1. IEffectDegradation запрашивает у плагина "Оболочка" интерфейс плагина, которому принадлежит текущее активное окно (пусть интерфейс будет в переменной IPlugin * iPluginActive). 2.2.3.2. IEffectDegradation проверяет, умеет ли он работать с данным плагином. Естественно, с IPhotoshopEditor он умеет работать т.к. он писалься для него. 2.2.3.3. IEffectDegradation приводит интерфейс iPluginActive к IPhotoshopEditor нужной версии. 2.2.3.4. IEffectDegradation создает переменную структуры TPhotoshopEditor_MsgGetImage << TPluginMessage, которая объявлена в публичном файле плагина IPhotoshopEditor. "IPhotoshopEditor.h"
Пусть переменная будет TPhotoshopEditor_MsgGetImage msgGetImage; 2.2.3.5. IEffectDegradation вызывает метод IPhotoshopEditor->IPlugin_MessageParse(&(TPluginMessage *)msgGetImage) 2.2.3.5.1. IPhotoshopEditor читает fMsgCode переданного сообщения. Он равен 13. 2.2.3.5.2. IPhotoshopEditor приводит TPluginMessage * message к TPhotoshopEditor_MsgGetImage. 2.2.3.5.3. IPhotoshopEditor записывает в fImage адрес изображения, которое он редактирует. И возвращает управление. 2.2.3.6. IEffectDegradation выполняет размытие изображения, адрес которого получит из msgGetImage->fImage Финита. Работа выполнена - плагины состыковались, изображение размыто. Добавлено через 2 минуты и 57 секунд
Может быть и так, но я попробую ![]() Добавлено через 4 минуты и 6 секунд
Согласен на 100%. Прийму к сведению. Добавлено через 8 минут и 25 секунд
config.ini будет для каждого плагина свой и пути будут относительно папки с файлом DLL плагина. Поэтому пересечение исключено. Настройки будут через объект класса IOpteoned, который будет содержать методы (Load, Save, Cancel) и форму с настройками для включения в ее как закладки в плагин "МенеджерНастроек". Но это пока только в планах. -------------------- НЕ ФЛУДИМ. Пользуемся кнопками "+" или "-" для выражения своего отношения к теме или сообщению. Гуглим "Как правильно задавать вопросы" |
||||||||||||||||||
|
|||||||||||||||||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 60 Всего: 223 |
Ваша система плагинов очень напоминает попытку создать с нуля COM. Видимо следующей стадией будут библиотеки типов и ActiveX
![]() Может не стоит изобретать велосипед, а воспользоваться готовым? |
|||
|
||||
ZVano |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 259 Регистрация: 11.12.2006 Где: Украина, Кривой Р ог Репутация: нет Всего: 4 |
Честно говоря, даже не подозревал об этом ![]() С удовольствием. Вот только не нашел я готовых движков. Тут спрашивал форумчан. -------------------- НЕ ФЛУДИМ. Пользуемся кнопками "+" или "-" для выражения своего отношения к теме или сообщению. Гуглим "Как правильно задавать вопросы" |
|||
|
||||
borisbn |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 4875 Регистрация: 6.2.2010 Где: Ростов-на-Дону Репутация: 22 Всего: 135 |
За исключением одного маленького плюсика (весьма сомнительного, к слову) - кроссплатформенная. -------------------- Женщины отличаются от программистов тем, что у них чары состоят из стрингов |
|||
|
||||
ZVano |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 259 Регистрация: 11.12.2006 Где: Украина, Кривой Р ог Репутация: нет Всего: 4 |
Кстати, именно кроссплатформенности и хотелось добиться. Но знаний для этого у меня не хватает. Есть мысль замутить опенсорсный движек и разместить его на googlecode.com -------------------- НЕ ФЛУДИМ. Пользуемся кнопками "+" или "-" для выражения своего отношения к теме или сообщению. Гуглим "Как правильно задавать вопросы" |
|||
|
||||
newbee |
|
|||
![]() Бревно ![]() ![]() Профиль Группа: Участник Сообщений: 703 Регистрация: 24.8.2011 Репутация: 4 Всего: 19 |
ZVano, тогда быстро, решительно выбрасывай дебилдер и начинай компилировать Gcc! Если гуевая часть заявляется в базовом функционале (извини, я мельком читала твои простыни), используй Qt.
-------------------- You're face to face With man who sold the world |
|||
|
||||
borisbn |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 4875 Регистрация: 6.2.2010 Где: Ростов-на-Дону Репутация: 22 Всего: 135 |
Что это за HMODULE, DWORD, MAX_PATH в интерфейсе ? +100500. ещё +100500. итого: +2001000 ![]() -------------------- Женщины отличаются от программистов тем, что у них чары состоят из стрингов |
|||
|
||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 60 Всего: 223 |
||||
|
||||
ZVano |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 259 Регистрация: 11.12.2006 Где: Украина, Кривой Р ог Репутация: нет Всего: 4 |
Полностью выбросить не получится т.к. начальство не позволяет. Хотелось бы получить на выходе: 1. кроссплатформенные интерфейсы базового плагина и менеджера плагинов 2. экземпляр менеджера плагинов в dll А весь остальной функционал в реализациях плагинов на Builder, VS, и т.д. GUI в базовом функционале не заявляется. Движек GUI это плагин интерфейса IPluginShell. Все GUIшные плагины цепляются к нему. Это типы из WINDOWS.H, WINDEF.H ![]() На что ума хватило, то и написал (лучше что-то, чем ничего). Пытался разобраться в исходниках FireFox, но абсолютно ничего не понял. Здоровенные они там. -------------------- НЕ ФЛУДИМ. Пользуемся кнопками "+" или "-" для выражения своего отношения к теме или сообщению. Гуглим "Как правильно задавать вопросы" |
|||
|
||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |