Как определить, где возникает исключение во время финализации в приложении Delphi? - PullRequest
3 голосов
/ 06 марта 2012

Я работаю на месте с клиентом и пытаюсь помочь им в решении сложной проблемы.Я надеюсь, что в Delphi есть инструмент или функция, которую мы можем использовать, чтобы заглянуть во внутреннюю работу, чтобы помочь нам найти проблему.

Вот общий обзор проблемы, с которой мы имеем дело.Это коммерческое приложение, которое в настоящее время развернуто в Delphi 5. За последний год приложение было перенесено в Delphi XE.Миграция почти завершена, но есть некоторые серьезные ошибки.

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

Отладчик прерывается в функции RaiseException в kernel32, которая вызывается NotifyNonDelphiException.Мы попытались установить непрерывную точку останова, которая регистрирует стек вызовов изнутри NotifyNonDelphiException, но это не дает нам ничего полезного.Стек вызовов содержит только методы, обработавшие исключение, а именно RtlRaiseStatus и KUserExceptionDispatcher.

Как определить код, который генерирует исходное исключение, которое обрабатывается NotifyNonDelphiException?


edit: вот два изображения, снятые после одного случая исключения.Первое - повышенное исключение, а второе - окно ЦП после закрытия диалогового окна исключения.

Access violation upon exit

CPU window upon closing the exception dialog box

Новое редактирование:

Прошло больше недели с тех пор, как я разместил этот вопрос, и я впечатлен различными ответами.Некоторые комментарии к первоначальному вопросу были наиболее ценными, но некоторые ответы сами по себе очень информативны.

Мой визит к этому клиенту завершен, и я попрошу их рассмотреть ответы, которые были размещены здесь.,Хотя мы не смогли отследить фактический источник ошибки, причина ошибки была более чем очевидной.Много лет настройки пользовательского интерфейса без серьезного рефакторинга привели к нестабильному процессу входа в систему.Когда пользователь отменил вход, основная форма находилась в состоянии частичной инициализации.Когда этому процессу не было разрешено запускаться, что и происходило, когда пользователь прерывал вход в систему, возникали очень серьезные проблемы с завершением.

Компания приобрела AQTime Pro, чтобы помочь выявить будущие проблемы, но требуется рефакторинг процесса входа в систему, и это решит проблему в долгосрочной перспективе.

В какой-то момент я рассмотрелудаляя этот вопрос, но я решил оставить его в курсе, так как я считаю, что другие найдут много отличных предложений, которые были опубликованы информативно.

В настоящее время я принимаю ответ @Deltics, так как я ненавижуоставив вопрос без ответа.Однако я прошу зрителей по этому вопросу также рассмотреть все остальные ответы и комментарии, и они одинаково ценны.

Ответы [ 3 ]

5 голосов
/ 07 марта 2012

Исключительно нельзя допускать, чтобы исключения "уходили" из секций финализации (или инициализация ) именно по этой причине.

С очень немногими исключениями [sic],любой код в секции finalization должен быть заключен в try..except .Что вы делаете, когда получаете исключение, зависит от вас, но, по крайней мере, вызов OutputDebugString () даст вам информацию при отладке и даст вам точку, на которой нужно установить точку останова, которая будет тольковызывать перерыв при возникновении действительного исключения.

finalization
  try
    // Perform finalization processing here

  except
    on e: Exception do
      OutputDebugString('%s: $s in unit %s', [e.ClassName, e.Message, 'MyUnitName']);
  end;
end.

ПРИМЕЧАНИЕ: OutputDebugString () в этом коде является моей собственной "строкойдружественный », обернутый вокруг функции в Windows блоке, расширенный для принятия аргументов.

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

Кроме того, процесс применения этой обработки исключений даст вам возможность просмотреть каждый раздел finalization и определить, нельзя ли обработать его по-другому,с целью устранения как можно большего количества разделов .

Это не должно рассматриваться как "ненужные накладные расходы" или "пустая трата времени", а является важной частью ведения домашнего хозяйства, чтобы принестиКачество кода до приемлемого стандарта.

Альтернативный подход

Альтернативный подход заключается в управлении собственным списком процедур завершения, так как мыпришлось сделать до того, как были представлены разделы финализации.т. е. в разделе инициализации модуля, который в данный момент имеет финализацию, удалите текущий код финализации в процедуру без параметров и зарегистрируйте эту процедуру в «менеджере финализации».

Затем в своем приложении вызовите «менеджер финализации»«в соответствующее время во время завершения работы приложения, чтобы выполнить финализацию до любой фактической финализации блока.Это гарантирует, что ваши процедуры завершения выполняются с обработчиком исключений во время выполнения.

Это также предоставляет возможность для предоставления более сложного «менеджера завершения», с механизмами, гарантирующими, что процедуры завершения выполняются в определенном, определенный порядок (если требуется).Эта возможность, естественно, зависит от того, как вы реализуете свой собственный менеджер финализации.

2 голосов
/ 08 марта 2012
  1. Перейдите в раздел «Просмотр / отладка Windows / Модули», найдите cxLibraryD15.bpl и извлеките его базовый адрес.Теперь вычтите $ 00E51B9E - base = offset.

  2. Запустите приложение и немедленно приостановите его.Зайдите в «Просмотр / Отладка Windows / Модули», найдите cxLibraryD15.bpl и извлеките его базовый адрес (он может быть таким же).Теперь добавьте к нему смещение от шага 1: база + смещение = абсолютный адрес.

  3. Откройте окно точек останова или представление CPU и установите точку останова на адресе с шага 2. Теперь вы остановитесьнепосредственно перед возникновением исключения, таким образом, вы можете увидеть стек вызовов и проанализировать ситуацию в отладчике.

0 голосов
/ 07 марта 2012

Попробуйте установить для точки останова KiUserExceptionDispatcher, RaiseException и Exception.GetExceptionStackInfoProc.

...