lunes, 13 de enero de 2014

Control de errores en VBA para Excel


Cualquier usuario que se haya adentrado en el mundo de las macros o del VBA (Visual Basic for Applications) habrá comprobado como es necesario convivir con los errores que se producen al ejecutar determinado código.
A veces, que ocurra un error es comprensible, natural y en ocasiones inevitable. Lo que sí debemos evitar es que nuestra macro o procedimiento se bloquee, apareciendo el típico mensaje de error:



Pero comencemos desde el principio. En el ámbito del VBA, hay tres tipos de error:

  • Los errores de compilación (sintaxis inadecuada). Estos errores de compilación son detectados por el editor de VBA al compilar el código, antes de comenzar a ejecutarse el procedimiento:
    • Por escribir inadecuadamente una expresión. Es decir, escribir una palabra inexistente en VBA
    • Por omitir una expresión donde debería estar escrita
    • Por escribir una expresión en un lugar que no corresponde
  • Los errores lógicos. Estos errores son más bien de planteamiento del código, no de sintaxis. El código está bien escrito y no se producen errores que bloqueen la ejecución. Aunque el programa funciona, no realiza las funciones para las que se diseñó o las realiza de forma errónea.
  • Los errores en tiempo de ejecución. Estos son detectados por Excel cuando ya se está ejecutando el código y pueden ser motivados por múltiples causas. En cualquier caso, siempre hay algo en común: Excel no puede ejecutar la acción que nosotros pretendemos con el código porque se produce una incoherencia (el resultado es un error, una condición que nunca se cumple, un valor que es imposible de asignar, un límite sobrepasado, etc.)



En la imagen anterior, vemos un error en tiempo de ejecución. Concretamente, Excel no ha podido encontrar el archivo que el código solicitaba abrir. En adelante, en este post, nos referiremos a este último tipo de errores (los errores en tiempo de ejecución).

¿Qué podemos hacer ante la posibilidad de que se produzca un error en la ejecución de nuestro código?

En Visual Basic tenemos la instrucción "On Error" para decidir lo que queremos que ocurra si se produjera un error en nuestro código. Tenemos 3 opciones para complementar la instrucción "On Error":
  • On error Resume Next (traducido sería: si hay un error, reanudar el código en la siguiente línea). Con esta opción, cuando se detecta el error, no aparece ningún cuadro de diálogo para "Finalizar" o "Depurar". La ejecución continua en la siguiente línea de código. Podemos ver un ejemplo aquí:
  • On Error GoTo MiEtiqueta (traducido sería: si hay un error, ir a MiEtiqueta). Con esta opción, al detectarse un error, obligamos a que la ejecución de código continúe en una línea marcada por nosotros con una etiqueta*. De esta forma y a continuación de esa etiqueta, podemos escribir un código que se ejecute en el caso de existencia de error:

Vemos en el ejemplo anterior que, en caso de error (al no encontrar el archivo Libro2.xlsm), enviamos la ejecución a la etiqueta ControlErrores para que se ejecute el código a partir de ahí (un MsgBox).

(*) Las etiquetas pueden ser textos (sin espacios), pero también pueden ser números (excepto el 0 (cero)). En el siguiente punto de este artículo quedará explicado por qué no se debe poner un cero como etiqueta..
Cuando nombramos una etiqueta después de las instrucciones "On Error GoTo ...." , la etiqueta no debe llevar dos puntos al final, pero al etiquetar la linea (a la que enviamos la ejecución del código), la etiqueta SÍ debe llevar dos puntos al final (ver imagen anterior).

Es conveniente utilizar una instrucción Exit Sub antes del control de errores (que se suele colocar al final del procedimiento) para evitar que sea leído el código si no es necesario (si no existe error). Por ejemplo:


  • On Error Goto 0 (traducido sería: si hay un error, deshabilitamos el control de errores). Con esta opción, si habíamos utilizado en el procedimiento la instrucción "On Error..." quedará deshabilitada y los errores de nuevo bloquearán Excel si se producen. 

         En la imagen de arriba vemos como se produce un error, puesto que "On Error GoTo 0" deshabilita el anterior "On Error GoTo ControlErrores"

          Es importante resaltar que "On Error.." habilita el control de errores sólo para un ámbito de procedimiento. En otras palabras:
    • Cuando se termina de ejecutar un procedimiento, termina el efecto de "On Error"
    • "On Error..." utilizado en un procedimiento x , no afecta a ningún otro procedimiento.
  • On Error Goto -1 (traducido sería: si hay un error, se reinicia la posibilidad de detectar y controlar un error)**. Es necesario saber que si en un momento dado hay una instrucción "On Error.." controlando un error (está en pleno trabajo)... si ocurriera otro error en ese instante, no podría ser controlado. Para restablecer "On Error..."  y que pueda volver a detectar errores (quede libre de trabajo), se utiliza On Error GoTo -1.
** Es importante tener en cuenta la diferencia entre "On Error GoTo 0" y "On Error GoTo -1":
  • "On Error GoTo 0", se utiliza para dejar de detectar errores
  • "On Error GoTo -1", se utiliza para volver a detectar errores, durante el control de un error
¿Cómo puedo comprobar si hay un error en algún rango o expresión y decidir lo que ocurrirá después?

También podemos controlar el comportamiento de Visual Basic en el caso de que se produzca un error, de otra forma distinta a las ya conocidas. Es el caso de la función de comprobación IsError. La forma de utilizarla es:
IsError(expresión)

Veamos un ejemplo en este fragmento de código:


Podemos apreciar como, después de declarar la variable "Edad" y de asignarle como valor lo introducido en un cuadro de entrada de datos (InputBox), el código comprueba...:
  1. Con una estructura condicional If...Then...Else, si la edad es < 18
  2. Con la función de comprobación IsError, si la variable contiene un error (este se podría producir si el usuario no introduce un número, pues la variable se declaró como numérica)


Si desea llevar a la práctica todo lo que le hemos ofrecido en este artículo, puede asistir a nuestros cursos prácticos de Excel o puede solicitarlos in company:







18 comentarios:

  1. Gracias miles yo pense que andaba algo mal con mi office y a cambiar de versiones y cd's jejeje habia que cambiar al programador (yo) gracias gracias

    ResponderEliminar
  2. Buenas! Necesito ayuda. Quise abrir un formulario de VBA desde una imagen en un Excel a través de la cual se abría normalmente. Ayer quise abrirlo y me saltó el siguiente error: "No se puede ejecutar la macro 'LibroX'. Puede que la macro no esté disponible en este libro o que hayan deshabilitado todas las macros" y sólo el botón de Aceptar... Qué está pasando? Cómo se soluciona?

    ResponderEliminar
    Respuestas
    1. Pues está pasando justo lo que dice el cuadro de diálogo. Que la macro que estaba asignada al botón ya no está: o bien porque se ha eliminado, o bien porque la macro no se grabó para este libro y por lo tanto no está alojada en este libro.
      También podría haber alguna que otra razón más extraña...pero posible.

      Eliminar
  3. Hola, necesito ayuda. Quisiera crear una macro que cerrase Word cuando detecta que el archivo esta corrupto o dañado. He conseguido hacer una que cierra la aplicación cuando se produce un error en el codigo de la macro, pero eso no me sirve, ya que la macro no arranca si no acepto las ventanas de aviso de archivo corrupto. Saludos y gracias.

    ResponderEliminar
  4. Hola, necesito ayuda. Busco como hacer una macro para que cierre Word o la aplicación de Office que corresponda cuando al abrir el archivo lo detecte como corrupto o dañado. He conseguido hacer una macro que cierra la aplicación si aparace algún error en el código de la macro, pero no me sirve del todo ya que si no acepto las ventanas de aviso de archivo dañado la macro no se ejecuta. Saludos y gracias.

    ResponderEliminar
  5. Prueba con Application.DisplayAlerts = False

    Saludos

    ResponderEliminar
  6. MIRA MI CÓDIGO ES EL SIGUIENTE:
    Private Sub CommandButton1_Click()
    On Error GoTo ERROR
    Dim Myconm As New Connection
    Dim rm As String
    Dim X As Integer
    X = 0
    Myconm.Open "ConexionMantenimiento"
    Set rsrc = New Recordset
    rm = "Select CODIGO, APELLIDO, NOMBRE FROM FAM_OFI"
    rsrc.Open rm, Myconm
    listfo.Clear
    Do While rsrc.EOF = False
    listfo.AddItem
    listfo.List(X, 0) = rsrc!CODIGO
    listfo.List(X, 1) = rsrc!APELLIDO
    listfo.List(X, 2) = rsrc!NOMBRE
    X = X + 1
    rsrc.MoveNext
    Loop

    ERROR:
    MsgBox "ALGO INESPERADO SUCEDIO"
    End Sub

    cuando lo ejecuto así no exista algún error igual me muestra el mensaje, cosa que no debería ser ya que no existe ningún error.

    ResponderEliminar
  7. Un poco mas arriba pone lo siguiente, lo cual no has aplicado...
    Es conveniente utilizar una instrucción Exit Sub antes del control de errores (que se suele colocar al final del procedimiento) para evitar que sea leído el código si no es necesario (si no existe error).

    ResponderEliminar
  8. me pueden ayudar con el siguiente error:

    Select method of Range class failed

    El codigo es el siguiente:

    Set wbDestino = Workbooks.Open(ActiveWorkbook.Path & "\CargaBibanking.xlsx")

    ThisWorkbook.Activate

    Set wsOrigen = Worksheets("CARGA BIBANKING")
    Set wsDestino = wbDestino.Worksheets("CARGA BIBANKING")

    Const celdaOrigen = "A1"
    Const celdaDestino = "A1"

    Set rngOrigen = wsOrigen.Range(celdaOrigen)
    Set rngDestino = wsDestino.Range(celdaDestino)

    rngOrigen.Select
    Range(Selection, Selection.End(xlDown)).Select
    Range(Selection, Selection.End(xlToRight)).Select
    Selection.Copy

    rngDestino.PasteSpecial xlPasteValues
    Application.CutCopyMode = False

    wbDestino.Save
    wbDestino.Close

    y el error lo muestra en la linea que dice "rngOrigen.Select"

    Gracias.

    ResponderEliminar
  9. Probablemente es porque en el momento de requerir la selección de "rngOrigen" no esté activo an pantalla ese objeto (wsOrigen ... o lo que es lo mismo, la hoja CARGA BIBANKING)
    Previamente habría que introducir un código que activara esa hoja
    Espero haber ayudado

    ResponderEliminar
  10. Muy buena información!! Gracias. Sólo no se entiende lo de GoTo -1.

    ResponderEliminar
  11. ESTOY INGRESANDO EN EXCEL VBA NUEVOS CAMPOS EN UN FORMULARIO PERO ME SALE ERROR DE COMPILACION NO SE HA ENCONTRADO LA VARIABLE COMO LO PUEDO SOLUCIONAR ME PODRÍAN AYUDAR ENVIANDO LA RPTA A MI EMAIL miguelafan@hotmail.com

    ResponderEliminar
  12. Hola.
    Te cuento el caso, hice una macro con la grabadora de macros para actualizar varias tablas de datos desde la web. El problema es que hasta el hacerlo manualmente requiere, a veces, tener que detener la ejecucion de la actualizacion presionando "ESC" y volver a actualizar para evitar el largo tiempo de espera en el que cae, haciendo eso la actualizacion se ejecuta en menos de 15 segundos, esto puede pasar con cualquier tabla al hacer la seguidilla de actualizaciones.
    El pedido es, qué codigo me permitiria que en caso de demorar la actualizacion en mas de 10 segundos sobre una tabla, que suspenda y vuelva a ejecutar el mismo codigo dos veces más y recien luego seguir con el siguiente, ya sea que se pudo o no actualizar (por error o tiempo de espera).
    gracias!

    ResponderEliminar
  13. Buenos dias yo tengo el siguiente error, Private Sub CommandButton1_Click() me podrias ayudar a corregirlo la misma quedo asi
    Private Sub CommandButton2_Click()
    Unload Me
    End Sub
    Private Sub CommandButton1_Click()
    GuardarInformacion()

    End Sub
    Sub GuardarInformacion()

    Rem Declación de variables
    Dim contFilas As Long
    Dim hoja As Worksheets

    Set hoja = Worksheets(1)

    'Validamos que los campos de texto correspondientes a los datos del container esten dilingenciados totalmente

    If Trim$(TextBox1.Text) = Empty Or Trim$(TextBox2.Text) = Empty Or Trim$(TextBox3.Text) = Empty Or Trim$(TextBox4.Text) = Empty Or Trim$(TextBox5.Text) = Empty Or Trim$(TextBox6.Text) = Empty Or Trim$(TextBox7.Text) = Empty Or Trim$(TextBox8.Text) = Empty Or Trim$(TextBox9.Text) = Empty Or Trim$(TextBox10.Text) = Empty Or Trim$(TextBox11.Text) = Empty Then
    MsgBox "Por Favor Ingrese Todos Los Datos!", "Datos Incompletos"
    Exit Sub
    End If
    ' Validamos la fila siguiente en la hoja donde se deben ingresar los datos'
    ConFila = hoja.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row

    hoja .Cells(ContFila, 1).Value = TextBox1
    hoja .Cells(ContFila, 2).Value = TextBox2
    hoja .Cells(ContFila, 3).Value = TextBox3
    hoja .Cells(ContFila, 4).Value = TextBox4
    hoja .Cells(ContFila, 5).Value = TextBox5
    hoja .Cells(ContFila, 6).Value = TextBox6
    hoja .Cells(ContFila, 7).Value = TextBox7
    hoja .Cells(ContFila, 8).Value = TextBox8
    hoja .Cells(ContFila, 9).Value = TextBox9
    hoja .Cells(ContFila, 10).Value = TextBox10
    hoja .Cells(ContFila, 11).Value = TextBox11

    'Se limpia o borran los datos de los campos de texto del formulario
    TextBox1.Value = ""
    TextBox2.Value = ""
    TextBox3.Value = ""
    TextBox4.Value = ""
    TextBox5.Value = ""
    TextBox6.Value = ""
    TextBox7.Value = ""
    TextBox8.Value = ""
    TextBox9.Value = ""
    TextBox10.Value = ""
    TextBox11.Value = ""
    TextBox1.SetFocus

    End Sub

    ResponderEliminar
    Respuestas
    1. No entiendo la pregunta ni el problema.
      Puedes explicarlo de otra forma?
      Saludos

      Eliminar
  14. Hola.. quisiera que me ayudaran..
    Tengo una macro que me crea hyperlinks en el nombre del producto de mi base de datos.. que al darle clic me habre la imagen de este mismo con el mismo nombre guardada en una carpeta ... ok esa parte esta bien.. el problema es que quisiera saber si hay algun codigo que cuando la foto no exista (o este mal el nombre) entonces que automaticamente que valla a la o las celdas del error y que quite el hyperlink...
    Digo que lo quite por que cuando corro mi macro hasta al producto que no tengo guardado con foto le pone hyperlink... solo que cuando le doy clic dice que no se puede encontrar el archivo... por tal razon quisiera el codigo de que si se produce el error en alguna celda le quite el hyperlink.. de antemano muchas gracias

    ResponderEliminar