Нет, это невозможно:
Существует два вида исключений: логические исключения, которые вызываются программистом с помощью команды raise
, и внешние исключения, которые инициируются ЦП для различных условий: деление на ноль, переполнение стека, нарушение доступа. Для первого типа, логические исключения, вы ничего не можете сделать, потому что они являются частью «потока» приложения. Вы не можете связываться с потоком стороннего кода, вы даже не можете связываться с потоком своего собственного кода.
Внешние исключения
Обычно они возникают в результате выполнения одной инструкции ЦП, когда эта инструкция не выполняется. В Delphi они доступны как EExternal
потомков. Список включает нарушения доступа, деление на ноль, переполнение стека, привилегированные инструкции и не так много других. Теоретически, для некоторых из этих исключений условие исключения может быть удалено, и одиночная инструкция ЦП повторяется, позволяя исходному коду продолжить работу, как будто ошибки не произошло. Например, НЕКОТОРЫЕ нарушения доступа могут быть «устранены» путем сопоставления страницы ОЗУ с адресом, где произошла ошибка.
В механизме SEH (Структурная обработка исключений), предоставляемом Windows, предусмотрены механизмы для устранения таких повторяющихся ошибок, и Delphi использует SEH под капотом. К сожалению, Delphi не предоставляет необходимые элементы, чтобы сделать это легко доступным, поэтому их использование будет очень трудным, если не невозможным. Тем не менее, для определенных типов ошибок EExternal
умные дельфинианцы могут попытаться написать собственный код SEH и заставить его работать. Если это так, задайте новый вопрос, указав конкретный тип ошибки, которую вы получаете, плюс шаги, которые вы хотели бы предпринять, чтобы устранить условие ошибки: вы либо получите некоторый рабочий код, либо индивидуальное объяснение того, почему идея не сработает.
Логические исключения, инициированные с помощью raise
Большинство исключений попадают в эту категорию, потому что большая часть кода проверяет свои входные данные, прежде чем делать потенциально опасные вещи низкого уровня. Например, при попытке доступа к недопустимому индексу в TList
этот индекс будет проверен, и перед попыткой доступа к запрошенному индексу возникнет исключительная ситуация недопустимого индекса. Без проверки доступ к недопустимому индексу либо вернет недействительные данные, либо вызовет нарушение прав доступа. Оба этих условия будет очень трудно отследить ошибки, поэтому исключение недопустимого индекса является очень хорошей вещью. Ради этого вопроса, даже если бы коду был разрешен доступ к недопустимому индексу, что вызвало нарушение прав доступа, было бы невозможно «исправить» код и продолжить, потому что невозможно угадать, каким должен быть правильный индекс.
Другими словами, исправление «логических» исключений не работает, не должно работать и безумно опасно. Если код, вызывающий ошибку, принадлежит вам, вы можете просто реструктурировать его, чтобы НЕ вызывать исключения для предупреждений. Если это не ваш код, то продолжение исключения попадает в категорию «безумно опасных» (не говоря уже о том, что это технически невозможно). Когда вы смотрите на уже написанный код, спросите себя: будет ли код работать правильно, если raise Exeption
заменить на ShowMessage
? Ответ в основном должен быть "NO, the code would fail anyway"
. В очень редком, очень неправильном случае стороннего кода, который вызывает исключение без уважительной причины, вы можете обратиться за конкретной помощью по исправлению кода во время выполнения, чтобы НИКОГДА не вызывать исключение.
Вот что может быть в каком-то стороннем коде:
function ThirdPartyCode(A, B: Integer): Integer;
begin
if B = 0 then raise Exception.Create('Division by zero is not possible, you called ThirdPartyCode with B=0!');
Result := A div B;
end;
Должно быть очевидно, что продолжение этого кода после исключения не позволит вещам "самовосстановиться".
Сторонний код также может выглядеть следующим образом:
procedure DoSomeStuff;
begin
if SomeCondition then
begin
// do useful stuff
end
else
raise Exception.Create('Ooops.');
end;
Где этот код "продолжить"? Совершенно очевидно, что это не часть "do usefull stuff"
, если код специально не разработан таким образом.
Это были, конечно, простые примеры, только царапающие поверхность.С технической точки зрения «продолжить» после исключения, как вы предлагаете, намного сложнее, чем перейти к адресу ошибки.Вызовы методов используют пространство стека для установки локальных переменных.Это пространство было освобождено в процессе «отката» после ошибки по пути к вашему обработчику исключений.Было выполнено finally
блоков, возможно, при необходимости выделив ресурсы.Переход назад к исходному адресу был бы очень неправильным, потому что вызывающий код больше не имеет того, что он ожидает в стеке, его локальные переменные больше не являются тем, чем они должны быть.
Если поднимается ваш кодисключение
Ваш код может легко быть исправлен.Используйте что-то вроде этого:
procedure Warning(const ErrorText:string);
begin
if not UserWantsToContinue(ErrorText) then
raise Exception.Create(ErrorText);
end;
// in your raising code, replace:
raise Exception.Create('Some Text');
// with:
Warning('Some Text');