2010-05-27 18 views
19

Czy znasz sposób pułapkowania, rejestrowania i ponownego zgłaszania wyjątku w kodzie Delphi? Prosty przykład:W jaki sposób powinienem ponownie przejąć wyjątek Delphi po zalogowaniu?

procedure TForm3.Button1Click(Sender: TObject); 
begin 
    try 
    raise Exception.Create('Bum'); 
    except 
    on E: Exception do 
    begin 
     MyHandleException(E); 
    end; 
    end; 
end; 

procedure TForm3.MyHandleException(AException: Exception); 
begin 
    ShowMessage(AException.Message); 
    LogThis(AException.Message); 
    // raise AException; - this will access violate 
end; 

Więc muszę ponownie podnieść go w wyjątkiem bloku ale zastanawiałem się, czy istnieje lepszy sposób napisać własną metodę do obsługi i (w określonych warunkach), aby ponownie podnieść wyjątki.

+3

Wystarczy ponownie podnieść go w zwykły sposób. Utrzymuje twój własny kod dokumentujący i nie zepsuł śladu stosu. –

+0

tak, zdecydowanie chcę zachować stos! –

+3

Jeśli chodzi o rejestrowanie wyjątków, o które prosisz, powinieneś rzucić okiem na madExcept, EurekaLog i/lub Jedi. Każda z nich lepiej radzi sobie z rejestrowaniem wyjątków, o jakim można marzyć. –

Odpowiedz

29

Jeśli chcesz ponownie podnieść wyjątek jedynie pod pewnymi warunkami, pisać

procedure TForm3.Button1Click(Sender: TObject); 
begin 
    try 
    raise Exception.Create('Bum'); 
    except 
    on E: Exception do 
    begin 
     if MyHandleException(E) then 
     raise; 
    end; 
    end; 
end; 

function TForm3.MyHandleException(AException: Exception): boolean; 
begin 
    ShowMessage(AException.Message); 
    result := true/false; 
end; 
+1

to jest dobry pomysł i miałem to na myśli, ale nadal uświadomiłem sobie, że istnieje inny sposób bez kodu, aby zrobić to w oddzielnej funkcji. Dzięki. –

2

powinien być w stanie po prostu użyć komendy Raise sama do ponownego podniesienia wyjątek:

begin 
    MyHandleException(E); 
    Raise; 
end; 
+1

Myślę, że dokładnie to, czego próbuje nie robić : po każdorazowym wywołaniu funkcji MyHandleException trzeba ** podwyższyć ** podbijając ** podbijając ** wewnątrz samego wyjątku MyHandleException. –

+0

@Mason: Możliwe, że masz rację. Prawdopodobnie czytałem to niepoprawnie. Skasuję odpowiedź ... trochę później, więc masz szansę zobaczyć ten komentarz :) –

+0

Myślę, że to prawda. Wstawił 'raise' w procedurze wywoływanej w bloku' except', co nie działa. 'Raise' musi znajdować się dokładnie w bloku' except'. Jeśli napiszesz "podniesienie wyjątku AE" w zewnętrznej procedurze, otrzymasz naruszenie zasad dostępu. –

4

można spróbować użyć (system.pas):

function AcquireExceptionObject: Pointer; 

Funkcja AcquireExceptionObject zwraca wskaźnik do bieżącego obiektu wyjątku i zapobiega zwolnieniu obiektu wyjątku po wyjściu bieżącego wyjątku.

Uwaga: AcquireExceptionObject zwiększa liczbę odwołań do obiektu wyjątku. Upewnij się, że liczba odwołań jest zmniejszana, gdy obiekt wyjątku nie jest już potrzebny. Dzieje się tak automatycznie, jeśli użyjesz obiektu wyjątku do ponownego podniesienia wyjątku. We wszystkich innych przypadkach każde wywołanie obiektu AcquireExceptionObject musi mieć pasujące wywołanie do ReleaseExceptionObject. Można zagnieżdżać sekwencje AcquireExceptionObject/ReleaseExceptionObject.

+0

Interesujące, spróbuję tego. Dzięki za szczegóły. –

+0

Uwaga - przynajmniej w Delphi 2007, ReleaseExceptionObject jest pustą metodą. Używanie go w połączeniu z AcquireExceptionObject powoduje przeciek pamięci. Znalazłem FreeAndNil do pracy jako alternatywa. –

+0

W D2009 'ReleaseExceptionObject' jest również pusty. Zastanawiam się, czy dokumentacja whetera jest poprawna w odniesieniu do tego liczenia referencyjnego. 'Wyjątek' pochodzi z TObject nie z' TInterfacedObject'. – Wodzu

5

dodaje się działa, ale oczywiście nie nadaje się do 2 powodów:

  • Wyjątkiem jest podniesiony z innego miejsca w stosie połączeń.
  • Nie otrzymujesz dokładnej kopii wyjątku - szczególnie te klasy, które dodają atrybuty. To znaczy. będziesz musiał jawnie skopiować potrzebne atrybuty.
  • Kopiowanie niestandardowych atrybutów może powodować bałagan ze względu na wymagane sprawdzanie typu.

.

procedure TForm3.MyHandleException(AException: Exception); 
begin 
    ShowMessage(AException.Message); 
    LogThis(AException.Message); 
    raise ExceptClass(AException.ClassType).Create(AException.Message); 
end; 

Korzyści są takie, że zachowuje się oryginalną klasę wyjątków i wiadomość (oraz wszelkie inne atrybuty, które należy skopiować).

Najlepiej byłoby zadzwonić pod numer System._RaiseAgain, ale niestety jest to rutyna "kompilatora-magii" i można ją wywołać tylko przez raise;.

+0

+1: Szukałem sposobu na powtórzenie wyjątku poza blokiem try ... except, ale już od jakiegoś czasu. Rozumiem, że utraciłeś stos wywołań, ale aby zmienić wyjątek w głównym wątku VCL, który wystąpił w innym wątku - działa to bardzo ładnie. Dzięki! –

6

Po napisaniu od posta Craiga Younga, z powodzeniem używam czegoś podobnego do następującego kodu. Możesz zachować oryginalną lokalizację wyjątku, używając identyfikatora "at" z funkcją ExceptAddr. Oryginalny typ klasy wyjątków i informacje są również zachowywane.

0

Przed wywołaniem programu obsługi można uzyskać obiekt wyjątku i zachować tylko jedną warstwę obsługi. Jednak nadal masz wiele obciążeń "Spróbuj/Z wyjątkiem/Zrób/Zakończ".

Procedure MyExceptionHandler(AException: Exception); 
Begin 
    Log(AException); // assuming it accepts an exception 
    ShowMessage(AException.Message); 
    raise AException; // the ref count will be leveled if you always raise it 
End; 

Procedure TForm3.Button1Click(Sender: TObject); 
Begin 
    Try 
    Foo; 
    Except On E:Exception Do 
    MyExceptionHandler(Exception(AcquireExceptionObject)); 
    End; 
End; 

Jednakże, jeśli to, co tylko chcesz zrobić, to pozbyć się powtarzalnego kodu obsługi błędów w obsługi zdarzeń, można spróbować to:

Procedure TForm3.ShowException(AProc : TProc); 
Begin 
    Try 
    AProc; 
    Except On E:Exception Do Begin 
    Log(E); 
    ShowMessage(E.Message); 
    End; End; 
End; 

Zmniejszenie swój kod obsługi zdarzenia to:

Procedure TForm3.Button1Click(Sender: TObject); 
Begin 
    ShowException(Procedure Begin // anon method 
    Foo; // if this call raises an exception, it will be handled by ShowException's handler 
    End); 
End; 

można również zrobić to praca dla funkcji, za pomocą sparametryzowanych funkcji:

Function TForm3.ShowException<T>(AFunc : TFunc<T>) : T; 
Begin 
    Try 
    Result := AFunc; 
    Except On E:Exception Do Begin 
    Log(E); 
    ShowMessage(E.Message); 
    End; End; 
End; 

i podejmowania ShowException zwrócić wartość (działając jako pośredni):

Procedure TForm3.Button1Click(Sender: TObject); 
Var 
    V : Integer; 
Begin 
    V := ShowException<Integer>(Function : Integer Begin // anon method 
    Result := Foo; // if this call raises an exception, it will be handled by ShowException's handler 
    End); 
End; 

Albo nawet podejmowania dotyku procedura anon bezpośrednio zmienną zakres zewnętrzny (s):

Procedure TForm3.Button1Click(Sender: TObject); 
Var 
    V : Integer; 
Begin 
    ShowException(Procedure Begin // anon method 
    V := Foo; // if this call raises an exception, it will be handled by ShowException's handler 
    End); 
End; 

istnieją pewne ograniczenia interakcji zmiennych z wnętrza ciała funkcji anonimowej i tych zdefiniowanych w zewnętrznym zakresie, ale w prostych przypadkach takich jak te, będziesz więcej niż w porządku.

Powiązane problemy