try..except в dll

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Сообщение Voldemar » 01.05.2006 12:40:13

Привет всем !

Возникла проблема: не выполняется конструкция try..except в dll'ке
- т.е. прога просто говорит runerror и слетает.
В руководстве по fpc ничего о таком исключении не говориться,
я даже поначалу подумал, что это ошибка в rtl'е и написал bugrep
(http://www.freepascal.org/bugs/showrec.php3?ID=5067).
Но в ответ получил нечто не вполне понятное: 'это не ошибка' и 'см
br # 2573'. Но в br # 2573 стоит статус "Unfixed" ("не исправлено" или "не исправимо" ?). Этому BR уже три года.

Я решил подумать еще раз и покопаться в исходниках rtl. Дальнейшее
- мои соображения. Поправьте меня, пожалуйста, если я в чем-то ошибся.

Dll - это набор процедур, которые могут быть вызваны из основной программы
или другой dll'ки. Они исполняются в потоке основной программы,
следовательно любые нештатные ситуации (исключения, прерывания)
в них обрабатываются одинакого для всех dll данной программы, а также
и для процедур данной программы. Т.е., например, если в dll'ке или
основной программе возникает деление на 0, операционка должна
послать что-то вроде SIGFPE и либо снять программу либо вызвать
зарегистрированный программой обрабочик сигналов.

Обработчик для конкретного
сигнала в данный момент времени может быть только один. Если программа
написана полностью на pascal'e и не использует dll'ек, обработчик
регистрируется модулем system, в нем происходит либо возврат longjmp'ом
на except (если ошибка происходит внутри try), либо изучается exitproc
и либо происходит переход на exitproc либо выводится сообщение runerror.
Здесь вроде все понятно.

Если же код компилируется в dll'ку, компилятор, вообще говоря, не знает,
кем и как она будет вызвана. Однако руководство по компилятору и практика
показывает, что в dll'ке можно обращаться ко всем процедурам модуля
system (writeln, например), вероятно, можно использовать ansistring
и прочие прелести. Вместе с тем, dll'ку можно вызвать хоть из
полуторакилобайтной программы, сгенерированной flat assemblerом - в
которой гарантированно нет процедур, подобных writeln. Отсюда следует
вывод: в dll'ку встраивается весь необходимый код модуля system
и любых других модулей, которые будут упомянуты в uses.

Теперь предполагаем простой вариант: в программе используется две
dll'ки, сгенерированные fpc. Каждая из них содержит свой экземпляр
модуля system, со всеми преодпределенными константами, распределенной
памятью под переменные... И вот что любопытно: каждая копия system
функционирует, фактически, независимо от другой ! И к тому же
- независимо от той копии, что сидит в основной программе - exeшнике.
Т.е. переопределение exitproc, например, внутри dll'ки вообще
ни на чем не отразиться: при возникновении div by zero будет
вызван обработчик, зарегистрированный exe'шником. Процедура Halt
так же будет себя вести различно, в зависимости места своего
вызова - каждый модуль (dll и exe) будут вызывать свои цепочки exitproc.

Таким образом попытка вставить try .. except внутри dll'ки
может отлавливать лишь ошибки, которые диагностирует сам модуль
system (например, ошибка открытия файла), но любые ошибки,
связанные с вмешательством ОС (деление на ноль, GPF..)
будут вызывать обработчик основной программы, который просто не имеет
понятия о try..excepах, exitproc'ах и прочем, имеющемся в dll'ке
(у двух копий system просто нет никакого интерфейса, чтобы договиться
о взаимодействии).

Если же, например, dll'ка вызывается exeшником, постороенным
не fpc, а, например, C'шным компилятором - он может вообще не
не встраивать никакого обработчика на SIGSEGV, SIGFPE...

Т.е. правильно ли я понимаю, что try..except просто технически
не может обработать ошибки арифметики и это не ошибка rtl-fpc,
а просто не упомянутая в документации особенность ?

Это во первых. А во вторых: ошибка, не приводящая к вмешательсту
ОС, возникшая внутри dll, может быть просто "не замечена" основной программой ?
Т.е. процедура, находящаяся в dll, например,
попытавшаяся открыть для чтения
несуществующий файл будет просто снята с выполнения (но без выполнения
_exit() - почему ?), что приведет
к чему угодно. У меня, например, основная прога при этом вылетала
по runerror(216), а не runerror(2).

Причем это одинакого верно как для linux'ов/BSD так и для win ?

Что будет, если попробовать грязный хак - попытаться из раздела
инициализации dll'ки вызвать раздел инициализации модуля system
(своей копии),
чтобы перевести стрелки обработчиков на себя ? Это собъет с толку
копию system основной программы, конечно, но все таки - возможно
ли это теоретически и как ?

Собственно все началось с простой, на первый взгляд, проблемы:
есть прога на C, которая
рисует окно и выводит результаты математической обработки,
которая выполяется fpcшной dll'кой. Нужно сделать так, чтобы
при возникновении разнообразных арифметических проблем
в dll'ке
выполнялись freemem/dispose и управление возвращалось Cшной проге:
дать возможность пользователю задать новые параметры расчета.

Конечно, можно при выделении памяти составлять табличку: что
конкретно выделено, а потом, при аварии, которая будет обработана
Cшным кодом, вызывать из dll'ки специальную процедуру освобождения,
но try..except был бы красивее....

Спасибо заранее
Voldemar
 

Сообщение Replicator » 01.05.2006 14:23:02

По-моему, в dll вообще не нужно обрабатывать никаких исключительных ситуаций. try except должен быть в самой программе, вызывающей dll.

Если пытаться удалить программиста от ошибок путем вызова try except в dll, то мы фактически лишаем программиста возможности контроля ситуаций появления исключений. Конечно, можно возвращать результат, но как отличить простую ошибку вызова функции от исключения?

Даже просто с точки зрения хорошего стиля, в библиотеке должны быть алгоритмы - процедуры, но координировать их работу, включая контроль ошибок и исключительных ситуаций, должна именно основная программа.
Replicator
постоялец
 
Сообщения: 154
Зарегистрирован: 30.04.2006 17:14:45
Откуда: Outer Heaven

Сообщение pda » 02.05.2006 01:40:58

Replicator писал(а): По-моему, в dll вообще не нужно обрабатывать никаких исключительных ситуаций. try except должен быть в самой программе, вызывающей dll.

Насколько я помню, проблема в том, что в Linux (а может и в *bsd) нет модели исключений, и если в библиотеке происходит необработанное исключение, то падает вся программа. Другой вопрос - можно ли сделать так, что бы fpc автоматически добавлял обработчик, перетаскивающий необработанные исключения на верхний уровень...
Аватара пользователя
pda
постоялец
 
Сообщения: 303
Зарегистрирован: 27.05.2005 19:59:53

Сообщение Voldemar » 02.05.2006 09:19:28

Atrus писал(а): Насколько я помню, проблема в том, что в Linux (а может и в *bsd) нет модели исключений, и если в библиотеке происходит необработанное исключение, то падает вся программа. Другой вопрос - можно ли сделать так, что бы fpc автоматически добавлял обработчик, перетаскивающий необработанные исключения на верхний уровень...

Т.е. в windows существует механизм, который бы позволял иметь несколько (для exe и каждой dll) отдельных обработчиков исключительных ситуаций ? По какому ключевому слову (имени функции) можно что нибудь поискать на эту тему ?
Voldemar
 

Сообщение Nikolay » 02.05.2006 15:09:52

Чтобы исключение возвращалось, объявляй функцию как stdcall
Nikolay
 

Сообщение pda » 02.05.2006 15:53:39

Voldemar писал(а):Т.е. в windows существует механизм, который бы позволял иметь несколько (для exe и каждой dll) отдельных обработчиков исключительных ситуаций ?

И да и нет. Windows не делит процесс на исполняемый модуль и библиотеки. Вместо этого, он создаёт стек обработчиков исключений. Каждый try - устанавливает новый обработчик, except - снимает. Если грубо.
Соответсвенно, если самый верхний обработчик отказался обрабатывать ситуацию, то вызывается следующий, следующий и т.д. Послединим вызвается ситсемный, который пишет "программа выполнила недопустимую операцию" и закрывает программу. Не знаю, как fpc, но Delphi всегда его перекрывает, по этому вызвать такое в ней - сложно.
Для каждого потока поддерживается отдельный стек обработчиков исключений, но насколько я понимаю, последним указывается общий для приложения (если есть).

Voldemar писал(а):По какому ключевому слову (имени функции) можно что нибудь поискать на эту тему ?

Искать по SEH (Structured Exception Handling). Например, "Win32™ SEH изнутри":
<a href='http://wasm.ru/article.php?article=Win32SEHPietrek1' target='_blank'>часть 1</a>, <a href='http://wasm.ru/article.php?article=Win32SEHPietrek2' target='_blank'>часть 2</a>, <a href='http://wasm.ru/article.php?article=Win32SEHPietrek3' target='_blank'>часть 3</a>...
Аватара пользователя
pda
постоялец
 
Сообщения: 303
Зарегистрирован: 27.05.2005 19:59:53

Сообщение hawke » 06.05.2006 23:55:32

Voldemar писал(а):Возникла проблема: не выполняется конструкция try..except в dll'ке
- т.е. прога просто говорит runerror и слетает.
В руководстве по fpc ничего о таком исключении не говориться,
я даже поначалу подумал, что это ошибка в rtl'е и написал

Очень интересный вопрос. Если рассмотреть поподробнее исходники rtl, то в процедурке _FPC_EXE_ENTRY можно заметить комментарий как раз по данному вопросу:
{ install the handlers for exe only ?
or should we install them for DLL also ? (PM) }

Уважаемый (PM) ;) разрешите от имени одной очень крупной российской софтверной компании ответить вам "Yes, we should"

По-моему, в dll вообще не нужно обрабатывать никаких исключительных ситуаций. try except должен быть в самой программе, вызывающей dll.

Если пытаться удалить программиста от ошибок путем вызова try except в dll, то мы фактически лишаем программиста возможности контроля ситуаций появления исключений. Конечно, можно возвращать результат, но как отличить простую ошибку вызова функции от исключения?

Вообще, очень странно видеть сообщения типа "в dll обработка исключений не нужна". Хочется напомнить уважаемому Replicator'у что в разделе except ... end вообще-то еще и фильтры бывают.
И уж никак эта конструкция, будучи умело примененной, не "удалит программиста от ошибки" а позволит реализовать очень удобный и гибкий подход
hawke
незнакомец
 
Сообщения: 2
Зарегистрирован: 06.05.2006 23:36:32

Сообщение Replicator » 07.05.2006 12:41:12

Я выразил свое IMHO. Я лишь сказал, что если этого нет, то и ничего страшного.

Конечно, если кому-то хочется это использовать, если кто-то получит от такого способа какие-то преимущества, то нужно использовать обработку в dll. Кроме того, то, что я описал - это лишь вопрос хорошего стиля и пр., а в компиляторе эта возможность должна быть реализована.
Replicator
постоялец
 
Сообщения: 154
Зарегистрирован: 30.04.2006 17:14:45
Откуда: Outer Heaven


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 49

Рейтинг@Mail.ru