Что означает `at ReturnAddress` в Delphi?
При просмотре System.Zip(Delphi XE2), чтобы увидеть, как это работает, я нашел эту функцию:
procedure VerifyWrite(Stream: TStream; var Buffer; Count: Integer);
begin
if Stream.Write(Buffer, Count) <> Count then
raise EZipException.CreateRes(@SZipErrorWrite) at ReturnAddress;
end;
Это часть at ReturnAddress
, которая меня озадачивает.
Я не знал, что at
является допустимым ключевым словом (синтаксический маркер тоже не распознает его).
В соответствии с IDE, объявленным как System.ReturnAddress
, но я могу найти его только как метку где-то в коде (asm) procedure _HandleAnyException;
. Системный блок полон ссылок на него, хотя.
Итак, я хотел бы знать следующее:
- Что такое
ReturnAddress
?
- Что именно делает
Raise Exception.Create ... at ReturnAddress
?
Бонусные баллы, если вы можете дать реальный пример того, где это будет полезной конструкцией, или если вы можете советоваться с ее использованием.
Ответы
Ответ 1
ReturnAddress
- это адрес, на который VerifyWrite
вернется после завершения.
Raise Exception.Create... at ReturnAddress
означает, что когда отображается диалоговое окно исключения, оно указывает адрес исключения как на ReturnAddress
. Другими словами, сообщение об исключении будет читать Exception <whatever> raised at <ReturnAddress>: <Exception Message>
.
Вот выдержка из файла справки для Delphi 7. Это почти то же самое, что онлайн-версия.
Чтобы создать объект исключения, используйте экземпляр исключения класс с выражением raise. Например,
raise EMathError.Create;
В общем случае форма оператора raise
raise object at address
где объект и по адресу являются необязательными; видеть Переопределение исключений. Когда адрес указан, это может быть любое выражение, которое оценивается указателем тип, но обычно является указателем на процедуру или функцию. Например:
raise Exception.Create('Missing parameter') at @MyFunction;
Используйте этот параметр, чтобы поднять исключение из более ранней точки в стеке, чем тот, где произошла ошибка.
Обратите внимание на последнее предложение в частности. Это довольно специфично в отношении использования at <address>
.
Ответ 2
ReturnAddr
не был загадкой с предыдущими версиями Delphi. Рассмотрим следующий тест (Delphi XE):
procedure RaiseTest1;
procedure RaiseException(ReturnAddr: Pointer);
begin
raise Exception.Create('OOPS!') at ReturnAddr;
end;
asm
POP EAX
JMP RaiseException
end;
procedure RaiseTest2;
begin
raise Exception.Create('OOPS!');
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
RaiseTest1;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
RaiseTest2;
end;
если вы нажмете Button3 в отладчике и нажмите "Разрыв" в окне сообщений о событиях, отладчик остановится на
procedure TForm1.Button3Click(Sender: TObject);
begin
RaiseTest1; // <-- here
end;
если вы нажмете Button4, отладчик остановится на
procedure RaiseTest2;
begin
raise Exception.Create('OOPS!'); // <-- here
end;
Как вы можете видеть, RaiseTest1 изменяет фрейм стека исключений по умолчанию и делает отладку более простой, поскольку единственной целью процедур RaiseTest1 (2) является создание исключения.
Я думаю, что что-то изменилось в XE2, так что синтаксис ReturnAddr
упрощен.