Implementación de Try-Catch-Finally en VBA
Generalmente, el manejo de errores se realiza mínimamente en cada función y luego se lanza al llamador. Sin embargo, en VBA no es fácil implementar esto.
Si se crea un controlador de errores, se suprime el error y, al intentar lanzar Err.Raise nuevamente, se captura el error dentro de la misma función, creando un bucle infinito.
Por ejemplo,
- Después de abrir un archivo con la función Open, incluso si ocurre un error, se debe ejecutar Close.
- Además, se debe transmitir el error al llamador.
On Error GoTo 0
Call Err.Raise(Err.Number)
Para resolver esto, se utiliza el código anterior. A continuación se presenta una explicación y ejemplos.
On Error GoTo 0
Al usar On Error GoTo 0, se anula cualquier On Error definido anteriormente.
Normalmente, al escribir Err.Raise en un controlador de errores,
- Se captura el error
- Se salta al controlador de errores
- Se lanza el error
- Se vuelve al paso 1
Esto crea un bucle, pero al incluir On Error GoTo 0,
- Se captura el error
- Se salta al controlador de errores
- Se anula el controlador de errores
- Se lanza el error al llamador
Esto permite recrear Try-Catch-Finally, similar a otros lenguajes de programación.
Ejemplo de código
Solo manejo de errores y lanzamiento
Public sub Caller()
On Error GoTo ERROR_HANDLER
Call Called
ERROR_HANDLER:
Debug.Print(Err.Number)
End Sub
Public sub Called()
On Error GoTo ERROR_HANDLER
Dim errNumber as Long
' Escribir el código que puede causar errores
Exit Sub
ERROR_HANDLER:
errNumber = Err.Number
On Error GoTo 0
' Escribir el código que se ejecuta después de un error
' Transmitir el error al llamador
Call Err.Raise(errNumber)
End Sub
Recrear Try-Catch-Finally
Requiere una escritura bastante larga.
Public sub Caller()
On Error GoTo ERROR_HANDLER
Call Called
ERROR_HANDLER:
Debug.Print(Err.Number)
End Sub
Public sub Called()
On Error GoTo ERROR_HANDLER
Dim errNumber as Long
' Escribir el código que puede causar errores
FINALLY:
If errNumber <> 0 then
Err.Raise(errNumber)
End if
Exit Sub
ERROR_HANDLER:
errNumber = Err.Number
On Error GoTo 0
' Escribir el código que se ejecuta después de un error
Resume FINALLY
End Sub
Ejemplo práctico
Solo Try-Catch
'------------------------------------------------
'
' Función que llama a otra función
'
'------------------------------------------------
Public Sub Caller()
On Error GoTo ERROR_HANDLER
Call Called
ERROR_HANDLER:
Debug.Print "Llamador : Catch"
End Sub
'------------------------------------------------
'
' Función llamada (que lanza el error)
'
'------------------------------------------------
Public Sub Called()
On Error GoTo ERROR_HANDLER
Dim errNumber as Long
Dim n As Long: n = 1 / 0
Exit Sub
ERROR_HANDLER:
errNumber = Err.Number
On Error GoTo 0
Debug.Print "Llamado : Catch"
' Transmitir el error al llamador
Call Err.Raise(errNumber)
End Sub
Resultado de ejecución
Llamado : Catch
Llamador : Catch
Recrear Try-Catch-Finally
'------------------------------------------------
'
' Función que llama a otra función
'
'------------------------------------------------
Public sub Caller()
On Error GoTo ERROR_HANDLER
Call Called
ERROR_HANDLER:
Debug.Print "Llamador : Catch"
End Sub
'------------------------------------------------
'
' Función llamada (que lanza el error)
'
'------------------------------------------------
Public sub Called()
On Error GoTo ERROR_HANDLER
Dim errNumber as long
Dim n as long: n = 1 / 0
FINALLY:
Debug.Print "Llamado : Finally"
If errNumber <> 0 then
Err.Raise(errNumber)
End if
Exit Sub
ERROR_HANDLER:
errNumber = Err.Number
On Error GoTo 0
Debug.Print "Llamado : Catch"
Resume FINALLY
End Sub
Resultado de ejecución
Llamado : Catch
Llamado : Finally
Llamador : Catch