【问题标题】:Why are changes to an Exception object lost when re-raising it?为什么在重新引发异常对象时会丢失对异常对象的更改?
【发布时间】:2011-09-08 11:03:43
【问题描述】:

我确信这曾经对我有用,而且我已经在网上看到了它(Jolyon Smith 和 David Moorhouse)。刚刚在 D2007 和 XE2 试用版中的一个简单程序中尝试过,它不会保留修改后的 Message。一旦“引发”发生,消息就会恢复为原始异常。

我错过了什么明显的东西?另一种方法是“引发 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;

【问题讨论】:

    标签: delphi exception


    【解决方案1】:

    好吧,吹我!这看起来很错误,我不得不自己尝试,你是绝对正确的!我将其范围缩小到这是一个由操作系统本身而不是由 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 异常(在本例中为 OS 异常),则(修改后的)“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);
    

    玩得开心!

    【讨论】:

    • 谢谢米莎。我以为我输了!
    • 除以零不是操作系统异常。这是由 CPU 本身引发的硬件异常(因此,它是异步异常)。 OS 和 Delphi 异常都是软件异常,由特定代码调用引发(因此,它是同步异常)。因此,所讨论的问题不是关于 OS 和 Delphi 异常,而是关于 Delphi 和非 Delphi 异常。 “增加;”重新引发原始异常(可能是硬件或软件异常),因此只有在原始异常是 Delphi 异常时才会保留 Delphi 对象中的任何更改。这很合乎逻辑。
    • 有什么解决办法吗?也发生在 Delphi 10 中。
    【解决方案2】:

    见 Misha 的解释。作为一种解决方法,您可以这样做:

    except
      on E: Exception do
      begin
        E := Exception(ExceptObject);
        E.Message := '(Extra info) ' + E.Message;
        AcquireExceptionObject;
        raise E;
      end;
    end;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-09
      • 2017-09-20
      相关资源
      最近更新 更多