![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Работа с плагинами [D7]
dreamse © (2012-01-20 02:51:00)Приветствую.
В виде плагинов использую обычные DLL c набором функций:
1) имя, версия и пр.
2) функция старта (тут обычно в плагине запускается отдельный поток со своим функционалом)
3) Функция передачи команды в плагин
4) Функция получения данных из плагина
Схема очень простая, примеров в сети кучи. Если нужно приведу листинг.
Все работает, но возникли проблемы при расширении, т.е плагинов уже много и функционал растет.
Проблема в том что если возникает критическая ошибка в одном из плагинов - падает все приложение (почему то вспомнил miranda которая падает через раз :) )
Как можно защитить приложение от падения ? в идеале - переодически опрашивать плагины и выгружать те которые не отвечают (либо перезапускать)
Дмитрий Белькевич (2012-01-22 11:11:00) [1]
> Другое дело что если в этот момент попытатся завершить работу
> 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.ru/cgi-bin/forum.pl?id=1302154341&n=0
Inovet (2012-01-21 23:39:00) [2]Ты компьютер тоже отключаешь выдиранием вилки из розетки? Вот и потоки завершай штатно, а не прибиванием. Вроде это очевидно.
Inovet (2012-01-21 23:37:00) [3]> [17] dreamse © (21.01.12 23:22)
> Другое дело что если в этот момент попытатся завершить работу
> windows - винда сообщает что тот или иной процес не отвечает
> ( так как ждет когда же поток очнется) так что в этом случае
> удобней убивать поток.
Этот случай отдельно обрабатывать.
dreamse (2012-01-21 23:22:00) [4]> При падении процесса, ответственного за плагины, он возобновляется.
Если бы падение не сопровождалось еще выводом виндового сообщения о том что процесс некорректно завершился ....
> Если объект уничтожен и обнилен - можно попробовать сделать проверку на Assigned.
Да так и делаю - как видно из приведенного кода.
Проблема в том была что на DataModule стоял компонент, например для работы с почтой у которого таймаут был по sleep. И обращеие происходило внутри самого компонента к себе же. Из за чего он вылетал. Решилось убиранием sleep.
> Поток должен сам корректно завершиться.
Логически это верно.
Другое дело что если в этот момент попытатся завершить работу windows - винда сообщает что тот или иной процес не отвечает ( так как ждет когда же поток очнется) так что в этом случае удобней убивать поток.
Dimka Maslov (2012-01-21 21:21:00) [5]
> В принципе текущая проблема решилась, осталось придумать
> как сделать глобальную обработку ошибок именно в DLL чтобы
> не падало все приложение из за кривых рук разработчиков.
>
Тут можно посоветовать разбить своё приложение на два отдельных процесса, один из которых управляет плагинами и тесно взаимодействует с основным. При падении процесса, ответственного за плагины, он возобновляется.
Дмитрий Белькевич (2012-01-21 20:25:00) [6]
> после sleep будет выполнен дальше код - который обратится
> к уже уничтоженному объекту.
Если объект уничтожен и обнилен - можно попробовать сделать проверку на Assigned.
Inovet (2012-01-21 17:39:00) [7]> [13] dreamse © (21.01.12 17:26)
> Лучше как показал в предыдущем примере. Так будет правельней
> отслеживать выгрузку.
Я не говорил про "убивать потоки".
Поток должен сам корректно завершиться. Для этого надо выставить флаг завершения, который поток периодически проверяет и завершается при его поднятии. т.е.
TerminateThread
принудительно завершит поток, а флаг только сообщит ему о необходимости завершения. Далее
WaitForSingleObject
но вот сказали, что нельзя ждать в
DLLEntryPoint
dreamse (2012-01-21 17:26:00) [8]> Inovet © (21.01.12 13:01) [10]
Лучше как показал в предыдущем примере. Так будет правельней отслеживать выгрузку.
dreamse (2012-01-21 17:23:00) [9]В общем проблема с глючным плагином решилась. Не без помощи данного форума должен заметить.
В общем:
Для разраотчиков плагинов нужно составить правила работы которым они должны следовать.
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 чтобы не падало все приложение из за кривых рук разработчиков.
Dimka Maslov (2012-01-21 14:28:00) [10]Ну надо было начинать с того, что там есть потоки. С потоками всё гораздо сложнее. Предлагаемое решение - каждый поток должен время от времени проверять общий флаг завершения и немедленно прекращать работу при его выставлении. Другое дело, что плагины может разрабатывать кто угодно... Как-то я разговаривал с разработчиками одной проги. У них была возможность подключения плагинов и расширения функционала третьми лицами. Потом они отказались. Почему? А потому-что вопросы пользователей по глакам плагинов адресовались им, не разработчикам плагинов. Когда это надоело, лавочка прикрылась.
Inovet (2012-01-21 13:01:00) [11]> [9] Leonid Troyanovsky © (21.01.12 11:26)
> Нельзя там ожидать.
Хм. Ну, если так, то тогда остаётся написать специальную функцию для остановки потоков, и вызывать её перед выгрузкой ДЛЛ.
Leonid Troyanovsky (2012-01-21 11:26:00) [12]
> Inovet © (21.01.12 00:22) [8]
> Подать знак потокам и дождаться их завершения в функции
> ДЛЛ DllEntryPoint
Нельзя там ожидать.
http://msdn.microsoft.com/en-us/windows/hardware/gg487379.aspx
--
Regards, LVT.
Inovet (2012-01-21 00:22:00) [13]> [7] dreamse © (20.01.12 22:05)
> Есть ли решение по правельной выгрузки загруженной DLL ?
Подать знак потокам и дождаться их завершения в функции ДЛЛ DllEntryPoint
dreamse (2012-01-20 22:05:00) [14]В чем еще проблема.
В DLL запускаются потоки.
И если их основного приложения выгрузить DLL методом FreeLibrary все данные уничтожаются а потоки продолжают работать неокторое время, обращатся к уже уничтоженным данным и генерировать исключения.
В итоге выгрузить плагины наример для замены на новую уже нельзя, нужно выключать все приложение.
пробывал FreeLibraryAndExitThread
http://www.vsokovikov.narod.ru/New_MSDN_API/DLL/fn_freelibraryandexitthread.htm
Тоже самое, потоки продолжают работать.
Есть ли решение по правельной выгрузки загруженной DLL ?
dreamse (2012-01-20 21:47:00) [15]Нашел баг который рушит всю систему.
Это обращение к уже удаленному объекту (Free)
если эта ошибка происходит в обычном приложении то появляется лишь диалог с ошибкой (access violation) и можно закрыв дальше работать.
Если эта же ошибка возниает в плагине (dll) то она сразу выбивает и рушит все приложени.
Вот что странно.
Dimka Maslov (2012-01-20 19:26:00) [16]
> icWasya © (20.01.12 16:08) [3]
При правильном подходе можно даже ловить сишные исключения в дельфийском коде и наоборот. Главное знать, что делаешь.
>
> Как можно защитить приложение от падения
Код вызова функций из плагина обернуть в try..except. При словленном исключений - банить плагин навсегда. Впрочем от сильно глобального сбоя это может и не прокатить.
Дмитрий Белькевич (2012-01-20 16:23:00) [17]1. Бить по руками и заставлять ловить блоками try.
2. Смотреть в сторону IPC.
icWasya (2012-01-20 16:08:00) [18]поскольку у каждой DLL свой менеджер памяти и своя RTTI и вследствии чего передавать объекты, в том числе и наследники TException между модулями настоятельно не рекомендуется, то прокатят только административные методы, типа а) внутри плагина не должно возбужнаться исключение; б) если таковые возникают, то перехватывать внутри функции плагина и превращать их во вменяемые код ошибки в) если это не выполняется, сильно бить по рукам разработчиков плагинов .
Можно ещё посмотреть в сторону safecall
dreamse (2012-01-20 14:49:00) [19]1. Плагины пишутся так же сторонними людьми. Отловить все баги нереально в принципе.
2. Идея хорошая, но как ее раализовать ? Интересует такой момент как обмен сообщениями между плагинами.
Есть ли какой то способ глобально поймать ошибку в dll и либо заблокировать ее try except либо выгрузить dll в которой произошла ошибка ?
Дмитрий Белькевич (2012-01-20 14:14:00) [20]DLL - это dynamic linked library, после линковки дллка - это фактически кусок вашего экзешника. Если критическая ошибка возникает в дллке - считайте, что критическая ошибка возникает в куске экзешника.
Что делать:
1. Искать ошибки и их устранять.
2. Выполнить плагины в виде отдельных процессов.