2010-05-27 8 views
19

¿Conoces una forma de atrapar, registrar y volver a subir la excepción en el código Delphi? Un ejemplo sencillo:¿Cómo debo volver a plantear una excepción de Delphi después de iniciar sesión?

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; 

así que tengo que volver a subir en el bloque de excepción pero me preguntaba si hay una mejor manera de escribir mi propio método para manejar y (en determinadas condiciones) para volver elevar excepciones

+3

Simplemente vuelva a subirlo de la forma habitual. Mantiene su código auto-documentado y no estropea el trazado de la pila. –

+0

sí definitivamente quiero preservar la pila! –

+3

Si está registrando las excepciones que está buscando, debería echar un vistazo a madExcept, EurekaLog y/o Jedi. Cada uno tiene un mejor manejo del registro de excepción que nunca soñaría con hacer usted mismo. –

Respuesta

29

Si desea volver a subir la excepción sólo bajo ciertas condiciones, escriba

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

esto es una buena idea y lo tenía en mente, pero aun así me alejé al saber que hay otra forma sin código para hacerlo en una función separada. Gracias. –

2

Usted debe ser capaz de usar sólo el comando Raise por sí mismo para volver a subir la excepción:

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

Creo que eso es exactamente lo que intenta no hacer : tiene que poner ** subir ** después de MyHandleException cada vez que lo llama poniendo ** ** ** dentro de MyHandleException. –

+0

@Mason: Es posible que tengas razón. Probablemente lo estaba leyendo incorrectamente. Eliminaré mi respuesta ... después de un rato para que tengas la oportunidad de ver este comentario :) –

+0

Creo que es correcto. Puso el 'raise' en un procedimiento llamado dentro del bloque' except', que no funciona. El 'raise' necesita estar precisamente en el bloque' except'. Si escribe 'raise AException' en el procedimiento externo, obtiene una infracción de acceso. –

4

Usted podría tratar de usar (system.pas):

function AcquireExceptionObject: Pointer; 

AcquireExceptionObject devuelve un puntero al objeto de excepción actual y evita que el objeto de excepción sea desasignado cuando se cierra el controlador de excepción actual.

Nota: AcquireExceptionObject incrementa el recuento de referencias del objeto de excepción. Asegúrese de que el recuento de referencias disminuya cuando ya no se necesite el objeto de excepción. Esto sucede automáticamente si usa el objeto de excepción para volver a generar la excepción. En todos los demás casos, cada llamada a AcquireExceptionObject debe tener una llamada correspondiente a ReleaseExceptionObject. Las secuencias de AcquireExceptionObject/ReleaseExceptionObject se pueden anidar.

+0

Interesante intentaré esto. Gracias por los detalles. –

+0

Cuidado: al menos en Delphi 2007, ReleaseExceptionObject es un método vacío. Al usarlo junto con AcquireExceptionObject, se produce una pérdida de memoria. He encontrado que FreeAndNil funciona como alternativa. –

+0

En D2009 'ReleaseExceptionObject' también está vacío. Me pregunto si la documentación es correcta sobre este recuento de referencias. 'Exception' se deriva de TObject no de' TInterfacedObject'. – Wodzu

5

El siguiente trabajo, pero por supuesto no es ideal para 2 razones:

  • La excepción se lanza desde un lugar diferente en la pila de llamadas.
  • No obtiene una copia exacta de la excepción, especialmente aquellas clases que agregan atributos. Es decir. tendrá que copiar explícitamente los atributos que necesita.
  • La copia de atributos personalizados puede complicarse debido a la verificación de tipos requerida.

.

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

Los beneficios son que usted preserva la clase excepción original, y el mensaje (y cualquier otro atributo que desea copiar).

Lo ideal es que quieras llamar al System._RaiseAgain, pero, por desgracia, esa es una rutina de "compilación mágica" y solo se puede llamar por raise;.

+0

+1: He estado buscando una manera de volver a publicar una excepción fuera del intento ... excepto bloquear desde hace bastante tiempo. Me doy cuenta de que pierdes la pila de llamadas, pero para volver a plantear una excepción en el hilo principal de VCL que ocurrió en otro hilo, esto funciona muy bien. ¡Gracias! –

6

Después de la publicación de Craig Young, utilicé algo similar al siguiente código. Puede conservar la ubicación de excepción original utilizando el identificador "at" con la función ExceptAddr. El tipo de clase de excepción original y la información también se conservan.

procedure MyHandleException(AMethod: string); 
var 
    e: Exception; 
begin 
    e := Exception(AcquireExceptionObject); 
    e.Message := e.Message + ' raised in ' + AMethod; 
    raise e at ExceptAddr; 
end; 

try 
    ... 
except 
    MyHandleException('MyMethod'); 
end; 
0

Puede adquirir el objeto de excepción antes de llamar a su controlador y mantener el manejador en sí mismo un liner. Sin embargo, todavía tiene una gran cantidad de carga "Try/Except/Do/End".

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; 

Sin embargo, si lo que sólo desea hacer es deshacerse de código de control de errores repetitivos en los controladores de eventos, podría intentar esto:

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

La reducción de su código de controlador de eventos a esto:

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; 

también puede hacer que funcione para funciones, utilizando funciones parametrizadas:

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; 

Y haciendo ShowException devolver un valor (que actúa como un tránsito):

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; 

O incluso haciendo el procedimiento anon toque directamente la variable (s) ámbito exterior:

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; 

hay algunas limitaciones en la interacción de variables desde el interior del cuerpo de la función anónima y las definidas en el ámbito externo, pero para casos simples como estos, estará más que bien.

Cuestiones relacionadas