tag:blogger.com,1999:blog-1702873441071265539.post6811533643235605427..comments2023-05-29T19:02:20.570+03:00Comments on Блог GunSmoker-а: Разработка системы плагинов, часть 6: UI в плагинахGunSmokerhttp://www.blogger.com/profile/15611696588191431330noreply@blogger.comBlogger34125tag:blogger.com,1999:blog-1702873441071265539.post-87238633508431204232020-07-12T06:08:07.998+03:002020-07-12T06:08:07.998+03:00Совсем кратко, это так.
В PluginAPI.pas добавляютс...Совсем кратко, это так.<br />В PluginAPI.pas добавляются 2 интерфейса:<br /> + IMdiForm: Интерфейс главной MDI-формы<br /> + IMDIChild: Интерфейс дочерней MDI-формы<br /><br />В юните главной формы добавляется реализация IMdiForm в MainForm или MainFormProvider.<br />Плагин создается как любой плагин по Разработке системы плагинов с IMDIChild. В плагине делается вызов и создание дочерней MDI-формы.<br />Я такой вариант сделал, работает.Anonymoushttps://www.blogger.com/profile/08705381642245637890noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-18845913736442003962020-07-10T14:46:10.612+03:002020-07-10T14:46:10.612+03:00У меня нет сейчас возможности/времени посмотреть п...У меня нет сейчас возможности/времени посмотреть подробно. Вы можете описать словами сам принцип того что вы делаете.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-60027988681683328432020-07-09T12:00:10.204+03:002020-07-09T12:00:10.204+03:00Добрый день! Уже 2020 год на дворе.
Дополнение к &...Добрый день! Уже 2020 год на дворе.<br />Дополнение к "части 6: UI в плагинах". По пункту MDI.<br /><br />Краткие решения, 4 пункта не рассматривал, как сложные и не вписываются в систему плагинов.<br />Мое решение 5:<br />- использовать Главную форму проекта (MainForm.FormStyle = fsMDIForm) с плагинами,<br />- создавать дочерние MDI-формы (Form.FormStyle = fsMDIChildForm) в плагинах.<br />Всё вписывается в систему плагинов.<br />Такой вариант я реализовал, проверено на нескольких реальных плагинах. Всё работает.<br />Дополнений получилось для комментария многовато. Составил изменения по юнитам ~300 строк, смогу отправить по почте для включения в Систему разработки плагинов, если такой вариант подойдет. GunSmoker, куда я могу отправить е-письмо?<br /><br />НиколайAnonymoushttps://www.blogger.com/profile/08705381642245637890noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-26187491498855348052017-05-23T15:21:57.440+03:002017-05-23T15:21:57.440+03:00Ссылки поправил, спасибо.Ссылки поправил, спасибо.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-46972337318814022062017-05-23T14:54:02.567+03:002017-05-23T14:54:02.567+03:00Добрый день!
Ссылка для скачивания примера - битая...Добрый день!<br />Ссылка для скачивания примера - битая, файл там не найден.<br />Как как можно получить его другим способом7Anonymoushttps://www.blogger.com/profile/10618568966713935761noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-87829925967881749832013-12-02T07:06:51.660+04:002013-12-02T07:06:51.660+04:00>>Вы хотите сделать выгрузку плагинов по зап...>>Вы хотите сделать выгрузку плагинов по запросу?<br /><br />Да, чтобы плагин можно было выгрузить по ходу работы программы, при этом он корректно освободил ресурсы и удалил свою форму.<br /><br />Насколько я понимаю, реализовывать в плагине возможность создания формы (например, для того чтобы редактировать настройки плагина) не самое правильное решение, гораздо правильней будет создать интерфейсы для создания нужных элементов настроек в основной программе.Mikhail Grigorevhttps://www.blogger.com/profile/02545636226243801838noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-13132161018272941442013-11-27T11:15:31.364+04:002013-11-27T11:15:31.364+04:00Вы хотите сделать выгрузку плагинов по запросу? Эт...Вы хотите сделать выгрузку плагинов по запросу? Это довольно непросто сделать. В нашей модели выгрузку по запросу сделать можно, но при этом предполагается, что к моменту выгрузки все ссылки на плагин уже отпущены (читай: все ресурсы плагина освобождены). В целом, это самый простой для реализации способ, хотя не всегда может быть очевидно как освободить ресурсы плагина перед его выгрузкой.<br /><br />В некоторых случаях вы можете захотеть сделать форсированную выгрузку плагина. Это существенно сложнее, т.к. ссылку на ваш ресурс (в вашем примере - форму) может держать кто угодно. В таком случае плагин должен вести явный или неявный список всех ресурсов (читай: интерфейсов), отданных наружу. В момент, когда его попросят выгрузиться, плагин должен пройтись по всем этим ресурсам и попросить их выгрузить. Если все ресурсы были отпущены - выгружаемся. Соответственно, форма будет удалена вместе с отпусканием последней ссылки на неё.<br /><br />Само собой, чтобы это работало, необходимо, чтобы любой кто запрашивает ресурс, имел бы способ сказать ему "отдай вот этот". Это нетривиальная задача. И если вы пойдёте этим путём, то здорово осложните себе жизнь. Потому что в этом случае вам для <b>каждого</b> объекта придётся реализовывать метод "отпусти ресурс", в котором <b>для каждого</b> поля объекта реализовать "если это - тот ресурс, то поле - очистить". Да ещё при этом придумать что делать, если вашему объекту этот ресурс необходим. Короче, муть полная.<br /><br />В частных и более простых ситуациях можно попробовать придумать другое решение. К примеру, если ссылку на форму держит (и может держать) только ядро, то ядро может перед выгрузкой плагина проверить, нет ли активных форм, принадлежащих ему, и сначала освободить формы, а потом выгружать плагин.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-32587768128058344472013-11-27T10:46:16.490+04:002013-11-27T10:46:16.490+04:00Добрый день,
цикл статей интересный, с созданием ф...Добрый день,<br />цикл статей интересный, с созданием формы проблем нет, а вот как сделать чтобы при выгрузке плагина форма удалялась? Намекните плиз. Спасибо.Mikhail Grigorevhttps://www.blogger.com/profile/02545636226243801838noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-80961261690859873022012-08-31T19:04:39.648+04:002012-08-31T19:04:39.648+04:00прежде всего , спасибо за очень интересную серию с...прежде всего , спасибо за очень интересную серию статей.<br />у меня тут возник вопрос по поводу встроенных форм / фреймов. как офрмляется встраивание дочерней формы из плагина в основную форму программы?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-2235192386541873892012-07-28T16:08:27.845+04:002012-07-28T16:08:27.845+04:00> Получается еще одно ограничение VCL в плагине...<i> > Получается еще одно ограничение VCL в плагине - не будут работать Action-ы из StdActns и подобные.</i><br /><br />Chaa, а можно демку на проблемы при использовании Action?GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-81202867163339659332012-05-20T08:12:43.287+04:002012-05-20T08:12:43.287+04:00Пускай будет вроде TXPManifest, но при этом хотя б...Пускай будет вроде TXPManifest, но при этом хотя бы будет понятно по версии компонента, когда происходили изменения. А то сейчас в процессе написания статей исходники системы меняются...<br />А насчёт событий, то неплохо было бы добавить например такие события как onLoadAll и onUnLoadAll, для того чтобы показать процесс загрузки/выгрузки плагинов в прогрессбаре. Это достаточно необходимые события, при наличии большого количества плагинов.<br /><br />>Пожалуй, надо будет мне примеры расширить.<br /><br />Поддерживаю, размер плагина имеет весомое значение, особенно когда этих плагинов больше сотни...Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-43656615198654330072012-05-13T07:56:03.428+04:002012-05-13T07:56:03.428+04:00Не очень понятно, что здесь будет делать компонент...Не очень понятно, что здесь будет делать компонент. Использование такой системы плагинов в программе подразумевает, что вы объявите новые интерфейсы под свою функциональность и зарегистрируете их в менеджере. Тут нет свойств и событий, которые мог бы выставлять наружу компонент. Поэтому, если сделать это в виде компонента, то получится вроде TXPManifest из старых Delphi: вся задача такого компонента - подключить модуль системы в uses.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-35214960820334647572012-05-12T10:22:54.658+04:002012-05-12T10:22:54.658+04:00Александр, возможно ли эту систему плагинов сделат...Александр, возможно ли эту систему плагинов сделать в виде компонента? На данный момент вижу значительные преимущества по сравнению с другими системами и хотелось бы дальнейшего развития и желательно с регулярными обновлениями, чтобы, так сказать, морально не устаревала.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-52114647032405054792012-05-10T14:18:42.764+04:002012-05-10T14:18:42.764+04:00> Ооо, ЛОЦМАН... пример того, как не надо делат...<i>> Ооо, ЛОЦМАН... пример того, как не надо делать плагины</i><br /><br />Приходится работать с тем, что есть. Импортные решения на порядок дороже.<br /><br />Сейчас посмотрел внимательнее, на первый взгляд можно сделать обработчик Application.OnActionExecute примерно таким:<br /><br />procedure TForm1.ApplicationActionExecute(Action: TBasicAction; var Handled: Boolean);<br />begin<br /> Handled := not Assigned(Action.OnExecute);<br />end;<br /><br />Тогда все Action-ы, для которых не задан обработчик OnExecute, будут игнорироваться и не попадут из плагина в главное приложение.<br /><br />Получается еще одно ограничение VCL в плагине - не будут работать Action-ы из StdActns и подобные.Chaahttps://www.blogger.com/profile/14387721107858333063noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-26191211326722353872012-05-10T11:48:57.619+04:002012-05-10T11:48:57.619+04:00> Нет, потому что тогда OnUpdate/OnExecute у са...<i> > Нет, потому что тогда OnUpdate/OnExecute у самого TAction не будет вызываться.</i><br /><br />Вот этого не понял. CM_ACTIONEXECUTE отправляется из единственного места - TContainedAction.Execute. И делается это в последнюю очередь. А до этого вызывается ActionList.ExecuteAction, Application.ExecuteAction и свой Execute.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-72382965232399962752012-05-10T11:35:49.423+04:002012-05-10T11:35:49.423+04:00Ооо, ЛОЦМАН... пример того, как не надо делать пла...Ооо, ЛОЦМАН... пример того, как не надо делать плагины :)<br /><br />ANSI нуль-терминированные строки совместно с интерфейсами, пересечение границ модулей исключениями, отдельные функции работы для разных языков,... не говоря уже об обычных ошибках реализации. Типа, неверных сигнатур функций :)<br /><br />Но это всё равно уже лучше многих других вариантов.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-27878373770307016682012-05-10T07:52:40.013+04:002012-05-10T07:52:40.013+04:00> Проблематично воспользоваться имейджлистом, ч...> Проблематично воспользоваться имейджлистом, чтобы наш плагин не дублировал ресурсы.<br /><br />Можно сделать как-то так:<br />ImageList1.ShareImages := True;<br />ImageList1.Handle := ACore.ImageListHandle;<br /><br />> Достаточно присвоить обработчик OnActionUpdate/OnActionExecute объекту Application в плагинах и устанавливать там Handled в True?<br /><br />Нет, потому что тогда OnUpdate/OnExecute у самого TAction не будет вызываться. Можно для всех Action-ов назначить обработчики OnUpdate и OnExecute, но это только частичное решение проблемы.<br /><br />Дело в том, что есть три способа работы с Action-ми.<br /><br />1. Обработчики событий OnUpdate/OnExecute у объектов TActionList, TApplication или TAction.<br /><br />2. Метод UpdateAction/ExecuteAction у контролов (см. TCustomStatusBar.ExecuteAction).<br /><br />3. Вызовы HandlesTarget/UpdateTarget/ExecuteTarget у объектов-наследников TCustomAction (см. StdActns).<br /><br />Пример кода и вообще про реализацию плагинов в одной системе отечественного производства можно посмотреть <a href="http://achechulin.blogspot.com/2012/04/plugin-sample.html" rel="nofollow">здесь</a>.<br /><br />Плагины там работают только в модальном режиме, и при вызове плагина я подменял оконную процедуру для TApplication и фильтровал в ней сообщений CM_ACTIONUPDATE/CM_ACTIONEXECUTE.Chaahttps://www.blogger.com/profile/14387721107858333063noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-83735214558476296082012-05-06T02:34:00.231+04:002012-05-06T02:34:00.231+04:00> куда-то исчез мой предыдущий пост :(
BlogSpo...<i> > куда-то исчез мой предыдущий пост :(</i><br /><br />BlogSpot решил, что это спам. Вернул.<br /><br /><i> > Кроме того ведь в VCL можно удобно на форму например назначит попапменю... если форма в длл а меню в приложении - выкручиваться через винапи.</i><br /><br />Почему-же. Никто же не запрещает плагину попросить ядро создать popupmenu и/или пункты в нём.<br /><br /><i> > От плагинов хотелось бы еще некоторой языковой переносимости, чтобы можно было и из плюсцов, и из васиков там всяких писать. </i><br /><br />Сборка DLL с пакетами не привносит проблем совместимости с другими языками - потому что протокол плагинов не использует пакеты. Пакеты используются только сугубо внутри двух десятков плагинов Delphi для разделения кода между ними. Больше никаких бонусов с них мы не получаем. Даже менеджер памяти разделяемый не используем.<br /><br />Пожалуй, надо будет мне примеры расширить.<br /><br /><i> > Кстати давно хотел спросить, как вы находите мотивацию писать такие подробные статьи.</i><br /><br />Честно - не знаю :) Я рассматриваю это как хобби/отдых. Кроме того, изложение материала на бумаге иногда помогает и самому упорядочить всё в голове.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-41870313404317087312012-05-06T02:24:21.619+04:002012-05-06T02:24:21.619+04:00куда-то исчез мой предыдущий пост :(
ну да ладно
...куда-то исчез мой предыдущий пост :(<br />ну да ладно<br /><br />>Вообще-то это было в прошлой части, где плагин создавал меню. В целом, если плагину нужно встраивать что-то в главную программу, то главная программа должна для этого предоставлять интерфейс.<br /><br />Ну это вариант "выкрутится" из подобной ситуации. А ведь есть же еще всякие экшнлисты, имейджлисты, деревья, списки и т.п. Кроме того ведь в VCL можно удобно на форму например назначит попапменю... если форма в длл а меню в приложении - выкручиваться через винапи. Кроме того передача хендлов - это ниразу не кроссплатформенно. На практике выходят какие-то ограничения с подводными камнями, а хочется какой-то автиматизации, чтобы ух...<br /><br /><br />>Можно было плагины собрать как DLL с пакетами. Пакет можно было делать стандартный rtl/vcl или же собрать свой собственный. При таком подходе общий код будет содержаться в пакетах и не дублироваться.<br /><br />От плагинов хотелось бы еще некоторой языковой переносимости, чтобы можно было и из плюсцов, и из васиков там всяких писать. Кроме того существуют некоторые проблемы при использовании стандартных модулей в пакетах, например модуль ComServ нельзя включать в пакет.<br /><br />Кроме того, с возможностями RTTI Delphi2010 и выше не сильная проблема написать программу генерирующую код с врапперами для этой цели. Возможно я как-нибудь соберусь с духом, и напишу статью на основе своих разработок.<br /><br />Кстати давно хотел спросить, как вы находите мотивацию писать такие подробные статьи. Для меня когда я что-то изучил/сделал, писать становится сложно, ибо оно тривиально и понятно.MrShoorhttps://www.blogger.com/profile/13896494656726335749noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-47035388105074862492012-05-05T23:47:08.642+04:002012-05-05T23:47:08.642+04:00Лично я очень негативно отношусь к идее двух VCL в...Лично я очень негативно отношусь к идее двух VCL в одном приложении. Всякие костыли типа предложенных для MDI если и будут работать, то только в простейших случаях. Как только потребуется что-нибудь посложнее, типа скинов, все сразу посыпется.<br />Так что только создание VCL-объектов на стороне хоста. Это может быть простая передача dfm + кастомный механизм привязки событий. Либо более продвинутый вариант: передача dfm и скрипта, который будет дергать интерфейс плагина (эдакий MVC). Кстати у некоторых скриптовых движков имеется встроенная поддержка работы с dfm.Torbinsnoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-14556255421465925842012-05-05T19:49:16.954+04:002012-05-05T19:49:16.954+04:00Сам себе ещё пометку сделаю, чтобы не забыть - про...Сам себе ещё пометку сделаю, чтобы не забыть - про потоки отдельно сказать хотел.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-74152465304866740982012-05-05T19:44:01.599+04:002012-05-05T19:44:01.599+04:00> Так же проблема размещения некоторых контроло...<i> > Так же проблема размещения некоторых контролов на чужой форме (не всегда плагину нужно создавать форму, иногда просто пару контролов кинуть). В особенности это касается стандартных классов windows, типа BUTTON, TRACKBAR. Например эта особенность является подводным камнем, т.к. все стандартные контролы посылают WM_COMMAND родительскому окну. И дельфийский код ищет дочерний контрол и посылает ему соответствующее CM_ уведомление. А если контрол из длл то понятное дело он его не найдет.</i><br /><i> > Как например - проблематично добавить свой пункт скажем в попап меню. Проблематично воспользоваться имейджлистом, чтобы наш плагин не дублировал ресурсы.</i><br /><br />Вообще-то это было в прошлой части, где плагин создавал меню. В целом, если плагину нужно встраивать что-то в главную программу, то главная программа должна для этого предоставлять интерфейс.<br /><br /><i> > когда посчитал, у меня вышло что весь проект в скомпилированном виде будет весить от 30 мбайт чисто код. При этом если убрать "дубликаты" VCL то там и 10 мбайт не набегало</i><br /><br />Можно было плагины собрать как DLL с пакетами. Пакет можно было делать стандартный rtl/vcl или же собрать свой собственный. При таком подходе общий код будет содержаться в пакетах и не дублироваться.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-49724976504349068732012-05-05T19:36:35.178+04:002012-05-05T19:36:35.178+04:00Есть еще много недостатков использования VCL в длл...Есть еще много недостатков использования VCL в длл, на вскидку:<br /><br />Например проблемы с использованием TThread.Synchronize, который не будет работать внутри длл. В длл придется либо писать свой аналог через SendMessage нашему окну, либо выводить подобный метод в интерфейсе приложения.<br /><br />Так же проблема размещения некоторых контролов на чужой форме (не всегда плагину нужно создавать форму, иногда просто пару контролов кинуть). В особенности это касается стандартных классов windows, типа BUTTON, TRACKBAR. Например эта особенность является подводным камнем, т.к. все стандартные контролы посылают WM_COMMAND родительскому окну. И дельфийский код ищет дочерний контрол и посылает ему соответствующее CM_ уведомление. А если контрол из длл то понятное дело он его не найдет.<br /><br />Возникнут проблемы с VCL-ным Drag&Drop-ом. Я думаю это очевидно.<br /><br />Но это явные проблемы, а еще получаем кучу ограничений. Как например - проблематично добавить свой пункт скажем в попап меню. Проблематично воспользоваться имейджлистом, чтобы наш плагин не дублировал ресурсы.<br /><br />Другие недостатки - размер получаемых длл. От мегабайта и выше. Когда я разрабатывал систему для себя, я сначала поступил примерно так же как описано в статье. В последствии мне пришлось отказаться от этого подхода, т.к. я хотел чтобы каждый мой плагин имел строго определенный набор функционала. Один - это всплывающие окошки, другой - звуковые уведомления, третий - менеджер загрузок, четрветый - сам загрузчик с докачами и прочими плюшками, пятый - сниффер, шестой седьмой и восьмой - парсеры соснифанного траффика, девятый - хранилище (в котором хранят опции сами плагины) и так далее... когда посчитал, у меня вышло что весь проект в скомпилированном виде будет весить от 30 мбайт чисто код. При этом если убрать "дубликаты" VCL то там и 10 мбайт не набегало. В качестве выхода из этой ситуации пришлось делать интерфейсный врапперы для вцл начиная от IPersistent. Задача значительно облегчается начиная с д2010, которая со своими возможностями RTTI позволяет написать код, полностью генерирующий такие врапперы.MrShoorhttps://www.blogger.com/profile/13896494656726335749noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-15283565124564237522012-05-05T14:57:53.366+04:002012-05-05T14:57:53.366+04:00Тогда (как я понял) достаточно присвоить обработчи...Тогда (как я понял) достаточно присвоить обработчик OnActionUpdate/OnActionExecute объекту Application в плагинах и устанавливать там Handled в True?GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-37841353161406636802012-05-05T14:53:03.209+04:002012-05-05T14:53:03.209+04:00Последовательность такая:
ActionList.UpdateAction(...Последовательность такая:<br />ActionList.UpdateAction(Self)<br />Application.UpdateAction(Self)<br />OnUpdate<br />SendAppMessage(CM_ACTIONUPDATE, 0, Longint(Self))<br /><br />Проблема больше не в Execute, а в Update. Если для Action-а не установлен обработчик OnUpdate, то будет послано сообщение. А это не такой уж редкий случай, если действие всегда доступно.<br />Кроме того, для выполнения Action-ов есть еще второй механизм: HandlesTarget - UpdateTarget - ExecuteTarget. Именно он и запускается в обработчике CM_ACTIONEXECUTE в TApplication.Chaahttps://www.blogger.com/profile/14387721107858333063noreply@blogger.com