Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2014.07.20;
Скачать: CL | DM;

Вниз

Работа с плагинами   Найти похожие ветки 

 
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;
Скачать: CL | DM;

Наверх




Память: 0.54 MB
Время: 0.008 c
15-1388304091
Мишаня
2013-12-29 12:01
2014.07.20
Ищу книгу


15-1388153053
Дмитрий СС
2013-12-27 18:04
2014.07.20
Список ком-объектов процесса.


15-1387384044
Kerk
2013-12-18 20:27
2014.07.20
DelphiSpec


2-1379322318
Никита2013
2013-09-16 13:05
2014.07.20
Блокировка спящего режима Андроид


15-1388057457
Scott Storch
2013-12-26 15:30
2014.07.20
Битрикс