Маскировка исключений в Delphi

В течение нескольких дней я упорно борюсь (но напрасно) с масками-исключениями.

Я разработал приложение, которое выполняет тяжелые вычисления с плавающей запятой для сотен тысяч записей. Очевидно, что код должен уметь обрабатывать исключения, особенно связанные с вычислениями с плавающей запятой: Overflow, ZeroDivide и т. д.

Приложение корректно работает под Windows 7 (32-разрядная или 64-разрядная версия) со многими различными типами процессоров, при возникновении ошибки условие обрабатывается должным образом, возникает исключение и запись отбрасывается.

К сожалению, проблемы начинаются, когда я запускаю приложение именно там, где оно предназначено: на выделенном сервере с процессором Intel Xeon E5-2640 v2 и Windows Server 2003 R2. Здесь исключений не возникает: записи с ошибками не отбрасываются, поэтому результаты загрязняются теми числовыми значениями, которыми машина изображает +INF или -INF.

Проблема в том, что на сервере настройки маскирования ошибок по умолчанию отличаются от тех, что мы находим в Windows 7. В частности, при вызове процедуры GetExceptionMask на сервере по умолчанию я нахожу exZeroDivide, а при вызове GetExceptionMask в Windows 7 это исключение не маскируется. В результате я сказал: при запуске приложения на сервере эти исключения не вызываются, а обрабатываются процессором, возвращающим экстремумы и «загрязняющие» числовые значения.

Ок, не паникуйте, я говорю, вы просто вызываете (т.е. в секции инициализации) SetExceptionMask исключая exZeroDivide, но не работает. Или лучше, хотя сразу после вызова SetExceptionMask исключение exZeroDivide больше не маскируется, когда выполняется код с вычислениями с плавающей запятой, набор TArithmeticExceptionMask, возвращаемый GetExceptionMask, все еще содержит exZeroDivide, и поэтому, если возникает ошибка, исключение не возникает.

Может ли кто-нибудь сказать мне, как правильно называть SetExceptionMask?

В чем причина того, что маскирование по умолчанию может отличаться от компьютера и другого? операционная система или тип процессора?

Спасибо.


person Juan Bonaventura    schedule 04.12.2014    source источник
comment
Это хорошее описание, но можете ли вы опубликовать пример кода, который генерирует исключение, и пример, показывающий, как вы пытаетесь справиться с маскировкой? Это значительно облегчит нам поиск решения. :)   -  person Mason Wheeler    schedule 04.12.2014
comment
Согласен, сложно сказать, не видя, как ты это делаешь. Я предлагаю запустить новое тестовое приложение, которое воспроизводит это поведение, и поделиться этим кодом с нами.   -  person Jerry Dodge    schedule 04.12.2014
comment
Похоже, что в ваш процесс на сервере Server 2003 что-то внедряется — программа проверки на вирусы, глобальный хук, другая утилита мониторинга системы? - который устанавливает флаговое слово FP с неверными значениями.   -  person 500 - Internal Server Error    schedule 04.12.2014


Ответы (1)


Обычной причиной этого является то, что вы вызываете сторонний код, очищающий маски. Это может быть библиотека, которую вы используете сознательно, но, скорее всего, это то, что вы не особо знаете о вызове. Типичным примером этого являются драйверы принтера. Они печально известны изменением флагов управления с плавающей запятой.

Следующим шагом является определение той части кода, которая изменяет флаги управления. Я предлагаю вам добавить ведение журнала трассировки отладки. Вызовов OutputDebugString было бы достаточно, но лучше использовать более продвинутую библиотеку протоколирования. Регистрируйте состояние управляющих флагов во время выполнения вашей программы. Вам понадобится несколько циклов добавления вызовов в журнал, запуска, чтения журнала, прежде чем вы сможете найти виновника. Как только вы нашли внешний код, который изменяет флаги, убедитесь, что вы восстановили их после выполнения этого внешнего кода.

Боюсь, это сложная область. Получить права непросто. Внешний код иногда играет быстро и свободно с управляющими флагами, как если бы этот код был единственным существующим кодом. Delphi RTL также не лучший способ обработки управляющих флагов. Например, мало кто знает, что Set8087CW не является потокобезопасным.

Я лично прошел через ваши трудности с моим собственным приложением с плавающей запятой. Но вы должны уметь решать такие задачи. Удачи!

person David Heffernan    schedule 04.12.2014