Форум: "Основная";
Текущий архив: 2014.07.20;
Скачать: [xml.tar.bz2];
ВнизРабота с плагинами Найти похожие ветки
← →
dreamse © (2012-01-20 02:51) [0]Приветствую.
В виде плагинов использую обычные DLL c набором функций:
1) имя, версия и пр.
2) функция старта (тут обычно в плагине запускается отдельный поток со своим функционалом)
3) Функция передачи команды в плагин
4) Функция получения данных из плагина
Схема очень простая, примеров в сети кучи. Если нужно приведу листинг.
Все работает, но возникли проблемы при расширении, т.е плагинов уже много и функционал растет.
Проблема в том что если возникает критическая ошибка в одном из плагинов - падает все приложение (почему то вспомнил miranda которая падает через раз :) )
Как можно защитить приложение от падения ? в идеале - переодически опрашивать плагины и выгружать те которые не отвечают (либо перезапускать)
← →
Дмитрий Белькевич (2012-01-20 14:14) [1]DLL - это dynamic linked library, после линковки дллка - это фактически кусок вашего экзешника. Если критическая ошибка возникает в дллке - считайте, что критическая ошибка возникает в куске экзешника.
Что делать:
1. Искать ошибки и их устранять.
2. Выполнить плагины в виде отдельных процессов.
← →
dreamse © (2012-01-20 14:49) [2]1. Плагины пишутся так же сторонними людьми. Отловить все баги нереально в принципе.
2. Идея хорошая, но как ее раализовать ? Интересует такой момент как обмен сообщениями между плагинами.
Есть ли какой то способ глобально поймать ошибку в dll и либо заблокировать ее try except либо выгрузить dll в которой произошла ошибка ?
← →
icWasya © (2012-01-20 16:08) [3]поскольку у каждой DLL свой менеджер памяти и своя RTTI и вследствии чего передавать объекты, в том числе и наследники TException между модулями настоятельно не рекомендуется, то прокатят только административные методы, типа а) внутри плагина не должно возбужнаться исключение; б) если таковые возникают, то перехватывать внутри функции плагина и превращать их во вменяемые код ошибки в) если это не выполняется, сильно бить по рукам разработчиков плагинов .
Можно ещё посмотреть в сторону safecall
← →
Дмитрий Белькевич (2012-01-20 16:23) [4]1. Бить по руками и заставлять ловить блоками try.
2. Смотреть в сторону IPC.
← →
Dimka Maslov © (2012-01-20 19:26) [5]
> icWasya © (20.01.12 16:08) [3]
При правильном подходе можно даже ловить сишные исключения в дельфийском коде и наоборот. Главное знать, что делаешь.
>
> Как можно защитить приложение от падения
Код вызова функций из плагина обернуть в try..except. При словленном исключений - банить плагин навсегда. Впрочем от сильно глобального сбоя это может и не прокатить.
← →
dreamse © (2012-01-20 21:47) [6]Нашел баг который рушит всю систему.
Это обращение к уже удаленному объекту (Free)
если эта ошибка происходит в обычном приложении то появляется лишь диалог с ошибкой (access violation) и можно закрыв дальше работать.
Если эта же ошибка возниает в плагине (dll) то она сразу выбивает и рушит все приложени.
Вот что странно.
← →
dreamse © (2012-01-20 22:05) [7]В чем еще проблема.
В DLL запускаются потоки.
И если их основного приложения выгрузить DLL методом FreeLibrary все данные уничтожаются а потоки продолжают работать неокторое время, обращатся к уже уничтоженным данным и генерировать исключения.
В итоге выгрузить плагины наример для замены на новую уже нельзя, нужно выключать все приложение.
пробывал FreeLibraryAndExitThread
http://www.vsokovikov.narod.ru/New_MSDN_API/DLL/fn_freelibraryandexitthread.htm
Тоже самое, потоки продолжают работать.
Есть ли решение по правельной выгрузки загруженной DLL ?
← →
Inovet © (2012-01-21 00:22) [8]> [7] dreamse © (20.01.12 22:05)
> Есть ли решение по правельной выгрузки загруженной DLL ?
Подать знак потокам и дождаться их завершения в функции ДЛЛ DllEntryPoint
← →
Leonid Troyanovsky © (2012-01-21 11:26) [9]
> Inovet © (21.01.12 00:22) [8]
> Подать знак потокам и дождаться их завершения в функции
> ДЛЛ DllEntryPoint
Нельзя там ожидать.
http://msdn.microsoft.com/en-us/windows/hardware/gg487379.aspx
--
Regards, LVT.
← →
Inovet © (2012-01-21 13:01) [10]> [9] Leonid Troyanovsky © (21.01.12 11:26)
> Нельзя там ожидать.
Хм. Ну, если так, то тогда остаётся написать специальную функцию для остановки потоков, и вызывать её перед выгрузкой ДЛЛ.
← →
Dimka Maslov © (2012-01-21 14:28) [11]Ну надо было начинать с того, что там есть потоки. С потоками всё гораздо сложнее. Предлагаемое решение - каждый поток должен время от времени проверять общий флаг завершения и немедленно прекращать работу при его выставлении. Другое дело, что плагины может разрабатывать кто угодно... Как-то я разговаривал с разработчиками одной проги. У них была возможность подключения плагинов и расширения функционала третьми лицами. Потом они отказались. Почему? А потому-что вопросы пользователей по глакам плагинов адресовались им, не разработчикам плагинов. Когда это надоело, лавочка прикрылась.
← →
dreamse © (2012-01-21 17:23) [12]В общем проблема с глючным плагином решилась. Не без помощи данного форума должен заметить.
В общем:
Для разраотчиков плагинов нужно составить правила работы которым они должны следовать.
1. Старатся по возможности не использовать в dll потоки
2 Если использование потоков все таки необходимо то
Отслеживать выгрузку DLL плагина:
procedure DLLEntryPoint(dwReason: DWord);
begin
case dwReason of
//DLL_PROCESS_ATTACH:
DLL_PROCESS_DETACH:
begin
if assigned(dmMod) then
begin
TerminateThread ...
end;
end;
end;
end;
DllProc := @DLLEntryPoint;
DllEntryPoint(Dll_Process_Attach);
где в DLL_PROCESS_DETACH: убивать все созданные потоки.
3. Не использовать в коде функцию sleep. вообще!
Если нужна задержка воспользоватся таймером (либо c TDataModule либо создать временный таймер - нужно создавать свое окно.)
Проблема в SLEEP очень простая - если пока идет задержка (в данном коде это было в компоненте расположенном на TDataModule) выгрузить библиотеку то после sleep будет выполнен дальше код - который обратится к уже уничтоженному объекту.
-----
В принципе текущая проблема решилась, осталось придумать как сделать глобальную обработку ошибок именно в DLL чтобы не падало все приложение из за кривых рук разработчиков.
← →
dreamse © (2012-01-21 17:26) [13]> Inovet © (21.01.12 13:01) [10]
Лучше как показал в предыдущем примере. Так будет правельней отслеживать выгрузку.
← →
Inovet © (2012-01-21 17:39) [14]> [13] dreamse © (21.01.12 17:26)
> Лучше как показал в предыдущем примере. Так будет правельней
> отслеживать выгрузку.
Я не говорил про "убивать потоки".
Поток должен сам корректно завершиться. Для этого надо выставить флаг завершения, который поток периодически проверяет и завершается при его поднятии. т.е.
TerminateThread
принудительно завершит поток, а флаг только сообщит ему о необходимости завершения. Далее
WaitForSingleObject
но вот сказали, что нельзя ждать в
DLLEntryPoint
← →
Дмитрий Белькевич (2012-01-21 20:25) [15]
> после sleep будет выполнен дальше код - который обратится
> к уже уничтоженному объекту.
Если объект уничтожен и обнилен - можно попробовать сделать проверку на Assigned.
← →
Dimka Maslov © (2012-01-21 21:21) [16]
> В принципе текущая проблема решилась, осталось придумать
> как сделать глобальную обработку ошибок именно в DLL чтобы
> не падало все приложение из за кривых рук разработчиков.
>
Тут можно посоветовать разбить своё приложение на два отдельных процесса, один из которых управляет плагинами и тесно взаимодействует с основным. При падении процесса, ответственного за плагины, он возобновляется.
← →
dreamse © (2012-01-21 23:22) [17]> При падении процесса, ответственного за плагины, он возобновляется.
Если бы падение не сопровождалось еще выводом виндового сообщения о том что процесс некорректно завершился ....
> Если объект уничтожен и обнилен - можно попробовать сделать проверку на Assigned.
Да так и делаю - как видно из приведенного кода.
Проблема в том была что на DataModule стоял компонент, например для работы с почтой у которого таймаут был по sleep. И обращеие происходило внутри самого компонента к себе же. Из за чего он вылетал. Решилось убиранием sleep.
> Поток должен сам корректно завершиться.
Логически это верно.
Другое дело что если в этот момент попытатся завершить работу windows - винда сообщает что тот или иной процес не отвечает ( так как ждет когда же поток очнется) так что в этом случае удобней убивать поток.
← →
Inovet © (2012-01-21 23:37) [18]> [17] dreamse © (21.01.12 23:22)
> Другое дело что если в этот момент попытатся завершить работу
> windows - винда сообщает что тот или иной процес не отвечает
> ( так как ждет когда же поток очнется) так что в этом случае
> удобней убивать поток.
Этот случай отдельно обрабатывать.
← →
Inovet © (2012-01-21 23:39) [19]Ты компьютер тоже отключаешь выдиранием вилки из розетки? Вот и потоки завершай штатно, а не прибиванием. Вроде это очевидно.
← →
Дмитрий Белькевич (2012-01-22 11:11) [20]
> Другое дело что если в этот момент попытатся завершить работу
> windows - винда сообщает что тот или иной процес не отвечает
> ( так как ждет когда же поток очнется) так что в этом случае
> удобней убивать поток.
Sleep"ы лучше в потоках вообще не использовать, но уже сам убрал и хорошо - туда и дорога.
Усыплять потоки удобнее TEvent"ами:
FTerminateEvent: TEvent;
procedure TDICOMStoreQueue.StartAndTerminate;
begin
....
FSession.Terminate;
FTerminateEvent.SetEvent;
constructor TSCUStoreSessionQueue.Create(AOwner: TDICOMStoreQueue);
begin
...
FTerminateEvent := TEvent.Create(nil, True, False, "");
destructor TSCUStoreSessionQueue.Destroy;
begin
...
FreeAndNil(FTerminateEvent);
Собственно, сама замена slee"а:
FTerminateEvent.WaitFor(Owner.FTimeDelay);
Так можно поток присыпить как sleep"ом, при этом можно его "снаружи" запустить и остановить в любой момент перед закрытием приложения.
Event"ов может быть несколько.
> ( так как ждет когда же поток очнется)
Не нужно ожидать просыпания потоков, нужно их заставлять останавливаться.
Глобально: разобраться с работой с потоками. Не всё там просто. Но - сделать можно. Сами на точно такие же грабли с потоками наступали:
http://delphimaster.net/view/1-1302154341/
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2014.07.20;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.004 c