Подтвердить что ты не робот

Что означает `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?

Бонусные баллы, если вы можете дать реальный пример того, где это будет полезной конструкцией, или если вы можете советоваться с ее использованием.

4b9b3361

Ответ 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 упрощен.