Почему изменения в объекте Exception теряются при его повторном поднятии? - PullRequest
23 голосов
/ 08 сентября 2011

Я был уверен, что раньше это работало на меня, и я видел это в сети (Джолион Смит и Дэвид Мурхаус). Просто попробовав его в простой программе как в D2007, так и в пробной версии XE2, он не сохраняет измененное Сообщение. Как только происходит "повышение", сообщение возвращается к исходному исключению.

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

var a: Integer;
begin
  try
    a := 0;
    Label1.Caption := IntToStr(100 div a);
  except
    on e: Exception do
    begin
      e.Message := 'Extra Info Plus the original : ' + e.Message;
      raise;
    end;
  end;
end;

Ответы [ 2 ]

21 голосов
/ 08 сентября 2011

Хорошо, взорви меня!Это выглядело так неправильно, что я должен был попробовать это сам, и вы абсолютно правы!Я сузил это до того факта, что это исключение ОС (деление на ноль), которое генерируется самой ОС, а не Delphi.Если вы попытаетесь вызвать EIntError самостоятельно, вы получите ожидаемое поведение, а не то, что видите выше.Обратите внимание, что ожидаемое поведение возникает всякий раз, когда вы сами вызываете исключение.

Обновление: В модуле System.pas при повторном возникновении исключения вызывается следующий код:

{ Destroy any objects created for non-delphi exceptions }

MOV     EAX,[EDX].TRaiseFrame.ExceptionRecord
AND     [EAX].TExceptionRecord.ExceptionFlags,NOT cUnwinding
CMP     [EAX].TExceptionRecord.ExceptionCode,cDelphiException
JE      @@delphiException
MOV     EAX,[EDX].TRaiseFrame.ExceptObject
CALL    TObject.Free
CALL    NotifyReRaise

Таким образом, если исключение не является исключением Delphi (в данном случае исключением ОС), исключение (измененное) Delphi освобождается, а исходное исключение повторно вызывается, тем самым отбрасывая любые изменения, внесенные висключение.Дело закрыто!

Обновление 2: (ничего не могу с собой поделать).Вы можете воспроизвести это с помощью следующего кода:

type
  TThreadNameInfo = record
    InfoType: LongWord;  // must be $00001000
    NamePtr: PAnsiChar;  // pointer to message (in user address space)
    ThreadId: LongWord;  // thread id ($ffffffff indicates caller thread)
    Flags: LongWord;     // reserved for future use, must be zero
  end;

var
  lThreadNameInfo: TThreadNameInfo;

  with lThreadNameInfo do begin
    InfoType := $00001000;
    NamePtr := PAnsiChar(AnsiString('Division by zero'));
    ThreadId := $ffffffff;
    Flags := $00000000;
  end;
  RaiseException($C0000094, 0, sizeof(lThreadNameInfo) div sizeof(LongWord), @lThreadNameInfo);

Веселитесь!

15 голосов
/ 08 сентября 2011

См. Объяснение Миши. В качестве обходного пути вы можете сделать это:

except
  on E: Exception do
  begin
    E := Exception(ExceptObject);
    E.Message := '(Extra info) ' + E.Message;
    AcquireExceptionObject;
    raise E;
  end;
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...