none
application.EnableEvents = True (Como garantir execução do evento open?) RRS feed

  • Pergunta

  • Olá pessoal.

    Estou criando um sisteminha com uma série de macros e em muitas delas é preciso desabilitar os eventos e habilitá-los no fim das rotinas. Sem problema.

    Porém estou receoso de que por algum motivo a ativação no final (application.EnableEvents = True) não ocorra. Seja porque o código foi interrompido durante a execução ou porque o computador travou, desligou, sei lá...

    Caso os eventos fiquem desativados o sistema não funcionará, porque o evento open na abertura chama formulários de senha de acesso e etc. Ou seja, se o evento open não executar a pessoa terá acesso a todo o sistema, o que seria um grande problema porque cada funcionário tem um grau de permissão.

    Eu havia inserido a habilitação no evento open, mas por motivos óbvios isto não faz sentido. O que fazer? Seguramente os usuários mais experientes já enfrentaram este problema.

    Muito obrigado,
    Michel Macário



    • Editado Michel MO quarta-feira, 16 de julho de 2014 17:30
    quarta-feira, 16 de julho de 2014 15:44

Respostas

  • "Um problema é que só é possível um goto dentro de cada procedimento e muitas vezes existem mais de um tratamento de erro"

    Este exemplo ilustra esta situação:

    Sub pMain()
      Dim lng As Long
      Dim sngDivisão As Single
    
      On Error GoTo linError
      Do
        lng = InputBox("Digite 0 para gerar um erro, um texto para outro tipo de erro, ou 1 para sair.", , 1)
        sngDivisão = 1 / lng
        MsgBox "Resultado da divisão: " & sngDivisão & ".", vbInformation
      Loop While lng <> 1
    
      MsgBox "Saindo da rotina.", vbInformation
    
    linEnd:
    On Error Resume Next 'É importante ter isto aqui porque se a rotina abaixo der
    'erro, a instrução Application.EnableEvents será executada de qualquer forma,
    'e você deverá garantir isso ao término da execução de seu programa.
    Call pQualquerRotina
    Application.EnableEvents = True Exit Sub linError: Select Case Err.Number Case 11 'Divisão por zero MsgBox "Se digitar 0, você obterá um erro de divisão por zero. Neste caso, vou forçar a barra para sngDivisão = 5.", vbExclamation sngDivisão = 5 Resume Next 'Resume Next salta para a próxima instrução de onde houve o erro. Case 13 'Tipos incompatíveis MsgBox "Erro: tentou-se passar um valor de texto à um tipo de dados numérico. Tente novamente.", vbExclamation Resume 'Resume volta à instrução que gerou erro e executa-a novamente. Case Else MsgBox "Erro não previsto. Número do erro: " & Err.Number & vbNewLine _ & "Descrição do erro:" & Err.Description Resume linEnd End Select End Sub

    Private Sub pQualquerRotina()
    Debug.Print 1 / 0
    End Sub

    ---

    "E se eu entendi a solução sobre o EnableEvents, não resolve, porque eu estou me referindo a pasta de trabalho aberta pelo próprio usuário e não uma aberta programaticamente."

    Seu código tem que prever que independentemente do erro que eventualmente ocorrer durante a execução de seu programa, que ele restaure o valor do EnableEvents = True ao término da execução. Meu exemplo mostra como isso acontece.


    Felipe Costa Gualberto - http://www.ambienteoffice.com.br


    sexta-feira, 25 de julho de 2014 01:16
    Moderador
  • Onde você escreveu "ScreenUpdating" estou entendendo que você confundiu por "EnableEvents", mas tudo bem, porque vale para os dois:

    Em toda nova seção do Excel, por padrão, a propriedade EnableEvents inicia com valor True. Logo, se o sistema travar/computador desligar, estou assumingo que você deverá reiniciar o Excel e, ao fazer isso, o EnableEvents estará como True.

    O ScreenUpdating funciona um pouco diferente: Toda vez que um código cessa a execução, seu valor retorna para True. Em outra palavras, você não precisaria colocar um Application.ScreenUpdating no final do código. Infelizmente isso está documentado incorretamente, porque já passei por situações que mesmo após o código ter terminado de executar, as atualizações gráficas da tela continuaram desativadas.


    Felipe Costa Gualberto - http://www.ambienteoffice.com.br

    • Marcado como Resposta Michel MO segunda-feira, 28 de julho de 2014 02:57
    domingo, 27 de julho de 2014 19:18
    Moderador

Todas as Respostas

  • Talvez algo como mostrado abaixo?

    Private Sub Workbook_Open()
      On Error GoTo linEnd
      frmLogin.Show
    
    linEnd:
      Application.EnableEvents = True
    End Sub
    


    Felipe Costa Gualberto - http://www.ambienteoffice.com.br

    quinta-feira, 17 de julho de 2014 02:42
    Moderador
  • Olá Felipe. Muito obrigado pela resposta.

    Não entendi a sua solução. Porque o problema é exatamente o fato do evento open não ser executado. Então não faz sentido habilitar os eventos dentro de um evento. O código não seria executado.

    Grato.
    Michel Macário
    sexta-feira, 18 de julho de 2014 01:26
  • Boa noite Michel!

    O que o Felipe te sugeriu faz com que sempre que ocorrer um erro no seu código o Excel execute a linha indicada em:

    On Error GoTo linEnd

    Coloque seu código normal abaixo dessa linha. Assim, caso aconteça um erro, o Excel executará todo o código abaixo da linha "linEnd".

    Abraço.



    Filipe Magno

    sábado, 19 de julho de 2014 02:03
  • Entendi Felipe, interessante.

    Mas não resolve o problema. Na realidade eu uso 'On Error Resume Next'. Então em tese a habilitação sempre vai acontecer no final. Só que não sei se é normal, mas já vi alguns códigos pararem durante a execução mesmo com esta linha. E o outro problema é a questão da energia. Alguns procedimentos demoram alguns segundos. E se o computador desligar neste processo? Sei que pode parecer meio exagero pensar nisto, mas todo o sistema é baseado no evento open. Se não executar ferrou.

    A solução que tenho pensado é sempre ocultar e proteger as planilhas e selecionar uma planilha em branco com um botão de ativação no fim das rotinas. Assim se o evento não executar o usuário só teria acesso a este botão que resolveria os problemas. E o mesmo problema ocorreria se as macros estivessem desabilitadas. Acho que assim já resolveria os dois.

    Grato!
    Michel Macário



     
    sábado, 19 de julho de 2014 04:02
  • "Na realidade eu uso 'On Error Resume Next'."

    Não é recomendável fazer isso. Use o On Error Goto no lugar! (explicação a seguir)

    ---

    "já vi alguns códigos pararem durante a execução mesmo com esta linha"

    Realmente, existem casos em que o On Error Resume Next exibe a janela de erro do VBE. Veja um exemplo:

    Sub pMain()
      On Error GoTo linError
      
      Debug.Print 1 / 0
      
      Exit Sub 'Esta linha nunca será executada
        
    linError:
      
      On Error Resume Next
      Debug.Print 1 / 0 'O VBE exibirá uma janela de erro.
    End Sub

    A forma mais fácil de explicar isso é imaginar que o código VBE possui um 'flag' que determina se a execução do cursor passou por algum GoTo de erro. Se sim, um erro ocasionará  na janela de erro do VBE.

    No entanto, é possível limpar esse flag. Veja o exemplo a seguir:

    Sub pMain()
      On Error GoTo linError1
      
      Debug.Print 1 / 0
      
      Exit Sub 'Esta linha nunca será executada
        
    linError1:
      Resume linError2
      Exit Sub 'Esta linha nunca será executada
    
    linError2:
      On Error Resume Next
      Debug.Print 1 / 0 'Esta linha não causará erro.
    End Sub

    ---

    Voltando à origem de seu problema, talvez haja uma forma mais simples de resolver seu problema. Provavelmente você abre pastas de trabalho pelo método Workbooks.Open. Então, habilite os eventos sempre antes de abrir uma pasta de trabalho:

    Application.EnableEvents = True
    Workbooks.Open strPath
    


    Felipe Costa Gualberto - http://www.ambienteoffice.com.br

    sábado, 19 de julho de 2014 17:56
    Moderador
  • Muito obrigado pelas respostas Felipe.

    Talvez por que eu ainda esteja aprendendo, mas não consigo usar On Error Goto. Um problema é que só é possível um goto dentro de cada procedimento e muitas vezes existem mais de um tratamento de erro. Talvez funcione em algumas planilhas onde o tratamento de erro seja o mesmo, tipo "Valor inválido". 

    Outro problema é que programo muitas vezes com base no número de erro. Por exemplo, se o PDF estiver aberto não poderá ser subscrito e dará um erro x (não lembro o número). Aí crio uma condição de If Err.Number = x Then... 

    Para muitas situações me parece muito mais fácil do que verificar se o PDF está aberto. 

    E se eu entendi a solução sobre o EnableEvents, não resolve, porque eu estou me referindo a pasta de trabalho aberta pelo próprio usuário e não uma aberta programaticamente.

    Grato!
    Michel Macário

    quarta-feira, 23 de julho de 2014 14:43
  • Michel,

    apenas como complementação, é possível desativar um dado tratamento para inserir um outro ou deixar sem, basta usar:

    On Error GoTo 0

    Adicionalmente, não  vejo problemas em tratar o erro pelo seu número, pelo contrário. Nesse caso vc precisaria apenas de uma chamada para indicar o local do tratamento e não precisaria repetir no código.

    Abraço.


    Filipe Magno

    quarta-feira, 23 de julho de 2014 14:51
  • "Um problema é que só é possível um goto dentro de cada procedimento e muitas vezes existem mais de um tratamento de erro"

    Este exemplo ilustra esta situação:

    Sub pMain()
      Dim lng As Long
      Dim sngDivisão As Single
    
      On Error GoTo linError
      Do
        lng = InputBox("Digite 0 para gerar um erro, um texto para outro tipo de erro, ou 1 para sair.", , 1)
        sngDivisão = 1 / lng
        MsgBox "Resultado da divisão: " & sngDivisão & ".", vbInformation
      Loop While lng <> 1
    
      MsgBox "Saindo da rotina.", vbInformation
    
    linEnd:
    On Error Resume Next 'É importante ter isto aqui porque se a rotina abaixo der
    'erro, a instrução Application.EnableEvents será executada de qualquer forma,
    'e você deverá garantir isso ao término da execução de seu programa.
    Call pQualquerRotina
    Application.EnableEvents = True Exit Sub linError: Select Case Err.Number Case 11 'Divisão por zero MsgBox "Se digitar 0, você obterá um erro de divisão por zero. Neste caso, vou forçar a barra para sngDivisão = 5.", vbExclamation sngDivisão = 5 Resume Next 'Resume Next salta para a próxima instrução de onde houve o erro. Case 13 'Tipos incompatíveis MsgBox "Erro: tentou-se passar um valor de texto à um tipo de dados numérico. Tente novamente.", vbExclamation Resume 'Resume volta à instrução que gerou erro e executa-a novamente. Case Else MsgBox "Erro não previsto. Número do erro: " & Err.Number & vbNewLine _ & "Descrição do erro:" & Err.Description Resume linEnd End Select End Sub

    Private Sub pQualquerRotina()
    Debug.Print 1 / 0
    End Sub

    ---

    "E se eu entendi a solução sobre o EnableEvents, não resolve, porque eu estou me referindo a pasta de trabalho aberta pelo próprio usuário e não uma aberta programaticamente."

    Seu código tem que prever que independentemente do erro que eventualmente ocorrer durante a execução de seu programa, que ele restaure o valor do EnableEvents = True ao término da execução. Meu exemplo mostra como isso acontece.


    Felipe Costa Gualberto - http://www.ambienteoffice.com.br


    sexta-feira, 25 de julho de 2014 01:16
    Moderador
  • Felipe, muito obrigado pela resposta!

    'Sem querer' você respondeu uma outra grande outra dúvida minha que havia postado em outro tópico sobre a construção de um log de erros. Não conhecia o funcionamento deste 'Resume Next'. Acredito que desta forma eu consigo registrar todos os erros que acontecerem durante a execução, sem que a execução pare. Claro que espera-se que não hajam erros, mas...

    Mas sobre o EnableEvents ainda não resolve o problema. Eu entendi que consigo habilitar a habilitação independe do que aconteça, mas e se durante a execução:

    - o sistema travar?
    - o computador desligar? 

    ScreenUpdating fica como False e ao abrir a pasta de trabalho todo o sistema que se inicia no evento open não será executado.

    Grato! Michel Macário
    • Editado Michel MO segunda-feira, 28 de julho de 2014 02:53
    domingo, 27 de julho de 2014 18:01
  • Onde você escreveu "ScreenUpdating" estou entendendo que você confundiu por "EnableEvents", mas tudo bem, porque vale para os dois:

    Em toda nova seção do Excel, por padrão, a propriedade EnableEvents inicia com valor True. Logo, se o sistema travar/computador desligar, estou assumingo que você deverá reiniciar o Excel e, ao fazer isso, o EnableEvents estará como True.

    O ScreenUpdating funciona um pouco diferente: Toda vez que um código cessa a execução, seu valor retorna para True. Em outra palavras, você não precisaria colocar um Application.ScreenUpdating no final do código. Infelizmente isso está documentado incorretamente, porque já passei por situações que mesmo após o código ter terminado de executar, as atualizações gráficas da tela continuaram desativadas.


    Felipe Costa Gualberto - http://www.ambienteoffice.com.br

    • Marcado como Resposta Michel MO segunda-feira, 28 de julho de 2014 02:57
    domingo, 27 de julho de 2014 19:18
    Moderador
  • Putz confundi o nome do comando... já corrigi.

    Não acredito que a resposta era tão simples. Eu poderia jurar que só comecei a ter esta dúvida depois do open falhar.

    Putz, que chato isto. Eu li certa vez que não era certeza que o ScreenUpdating voltasse para True e que era aconselhável voltar programaticamente. É uma pena. Seria uma preocupação a menos.

    Grato!
    Michel Macário


    segunda-feira, 28 de julho de 2014 03:04