2011-09-08 5 views
21

私はこれが私のために働いていたと確信していました。ネットでそれを見てきました(Jolyon SmithとDavid Moorhouse)。 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; 

答えて

20

よく吹いてください!これは間違って見えたので、自分で試してみる必要がありました。あなたは絶対に正しいです!私は、OS自体(Delphiではなく)で生成されるOS例外(ゼロで割る)であるという事実に絞り込んだ。あなた自身で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); 

楽しんでください!

+0

ありがとうございます。私はプロットを失っていたと思った! – Paul

+0

ゼロ除算はOS例外ではありません。これはハードウェアの例外で、CPU自体によって生成されます(したがって、非同期例外です)。 OSとDelphiの例外はどちらもソフトウェアの例外で、特定のコード呼び出しによって発生します(したがって、同期例外です)。したがって、問題はOSとDelphiの例外ではなく、DelphiとDelphi以外の例外についてです。 "レイズする";元の例外(ハードウェア例外またはソフトウェア例外のいずれか)を再発生させるため、元の例外がDelphiの例外だった場合にのみ、Delphiオブジェクトの変更は保持されます。それはかなり論理的です。 – Alex

14

ミシャの説明を参照してください。これを回避するには、次のようにします。

except 
    on E: Exception do 
    begin 
    E := Exception(ExceptObject); 
    E.Message := '(Extra info) ' + E.Message; 
    AcquireExceptionObject; 
    raise E; 
    end; 
end; 
関連する問題