Se você usa o Application.OnTime do Word VBA para agendar uma macro para um horário específico, pode notar que a macro não é executada após o computador retornar do modo inativo, como suspensão ou hibernação. Isso acontece porque o OnTime depende do relógio do sistema e da fila de temporizadores interna do Word, ambos pausados durante estados inativos. Quando o sistema é retomado, o Word não reavalia automaticamente as chamadas OnTime pendentes em relação ao novo horário, então o evento agendado é ignorado. Este artigo explica a causa raiz em detalhes e fornece uma solução alternativa confiável para garantir que suas macros agendadas sejam executadas corretamente após a retomada do modo inativo.
Principais Conclusões: Por que o OnTime Falha Após o Modo Inativo e Como Corrigir
- Application.OnTime usa valores de tempo absolutos, não temporizadores relativos: Quando o sistema dorme, o horário agendado passa e o Word não dispara a macro ao retomar.
- O Word não enfileira um temporizador de ativação do sistema para eventos OnTime do VBA: Não existe um mecanismo interno para ativar o computador ou reexecutar agendamentos perdidos.
- Use um loop de polling com DoEvents e uma verificação de inatividade do sistema: Substitua
OnTimepor um loopDoEventsque verificaGetTickCountouTimerpara detectar a retomada da inatividade e executar a macro.
Por que o Application.OnTime Perde Eventos Agendados Após a Retomada do Modo Inativo
O método Application.OnTime no Word VBA agenda uma macro para ser executada em uma data e hora específicas. Internamente, o Word armazena esse horário absoluto e o verifica em relação ao relógio do sistema a cada poucos milissegundos enquanto o aplicativo está ativo. Quando o computador entra em suspensão, hibernação ou mesmo inatividade prolongada que aciona o desligamento do display, o relógio do sistema para de avançar durante o estado inativo. O Word também não processa nenhum evento VBA durante esse período, pois a thread do aplicativo é suspensa.
Quando o sistema é retomado, o relógio do sistema avança para o horário real atual. O Word não dispara retroativamente eventos OnTime cujo horário agendado já passou. O evento é simplesmente descartado da fila de temporizadores. Esse comportamento é proposital: OnTime não é um sistema de alarme recorrente; é um agendador único que espera que o computador permaneça ativo. Se o horário agendado já passou no momento em que o Word verifica a fila após a retomada, o evento é ignorado sem qualquer erro ou notificação.
Outro fator é que a fila de temporizadores interna do Word não é persistida em disco. Se o sistema perder energia ou o aplicativo for fechado durante a suspensão, todas as chamadas OnTime pendentes são perdidas. O mecanismo VBA não fornece um método interno para consultar quais eventos OnTime ainda estão pendentes ou para registrá-los novamente após um evento de retomada.
Solução Alternativa Confiável: Loop de Polling com Detecção de Inatividade
Como o OnTime não é confiável após a retomada do modo inativo, você deve substituí-lo por um mecanismo de polling que seja executado continuamente e verifique o tempo de atividade do sistema ou um horário alvo armazenado. A abordagem a seguir usa um loop DoEvents em uma macro que é executada em um temporizador definido por Application.OnTime, mas com um intervalo curto, como a cada 10 segundos. Dentro do loop, você compara o valor atual de Timer com um horário alvo armazenado. Se o horário alvo já passou, a macro é executada. Esse método sobrevive à retomada do modo inativo porque o loop continua após o sistema acordar.
Passo 1: Armazenar o Horário Alvo em uma Variável de Nível de Módulo
- Abra o Editor VBA
Pressione Alt + F11 para abrir o editor VBA. No Project Explorer, clique duas vezes em ThisDocument ou em um módulo padrão onde deseja o código. - Declare uma variável de nível de módulo para o horário alvo
No topo do módulo, adicione:Private m_targetTime As Double. Essa variável armazena o valor absoluto de tempo da funçãoTimerquando o agendamento foi definido.
Passo 2: Criar uma Rotina de Início que Define o Agendamento
- Escreva uma sub-rotina pública chamada StartSchedule
Dentro dessa sub, definam_targetTime = Timer + 60para agendar a macro para ser executada 60 segundos a partir de agora. Substitua 60 pelo atraso desejado em segundos. - Chame o loop de polling
Após definir o horário alvo, chamePollingLoop(que você criará a seguir). Exemplo:Call PollingLoop.
Passo 3: Escrever o Loop de Polling que Verifica o Horário
- Crie uma sub-rotina chamada PollingLoop
Adicione a seguinte estrutura de código:Public Sub PollingLoop() Do While Timer < m_targetTime DoEvents Loop Call YourActualMacro End SubEste loop é executado continuamente, cedendo a outros processos via
DoEvents, até que o horário atual atinja ou ultrapasse o horário alvo. - Substitua YourActualMacro pelo nome da sua macro desejada
A macro é executada imediatamente após a saída do loop, independentemente da retomada do modo inativo.
Passo 4: Iniciar o Agendamento a Partir de um Gatilho Amigável
- Atribua StartSchedule a um botão ou evento AutoOpen
Para iniciar o agendamento automaticamente quando o documento for aberto, coloqueCall StartScheduleno eventoDocument_OpendeThisDocument. - Teste a macro
ExecuteStartSchedulemanualmente no editor VBA pressionando F5. Coloque seu computador para dormir por 30 segundos e depois acorde-o. A macro deve ser executada dentro de alguns segundos após a retomada.
Armadilhas Comuns ao Substituir OnTime por um Loop de Polling
O Word Fica Sem Resposta Durante o Loop
Sem DoEvents, o loop bloqueia a thread da interface do Word, fazendo com que o aplicativo congele. Sempre inclua DoEvents dentro do loop. Para atrasos muito longos, considere adicionar uma chamada de API Sleep para reduzir o uso da CPU. Exemplo: Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) e chame Sleep 500 dentro do loop para pausar meio segundo entre as verificações.
A Macro é Executada Imediatamente Após a Retomada Mesmo que o Horário Alvo Não Tenha Chegado
Isso indica que Timer avançou durante o sono e seu horário alvo já estava no passado. Para evitar isso, armazene o horário alvo como um valor UTC absoluto usando DateAdd e Now em vez de Timer. Compare usando Now >= m_targetDateTime. Esse método funciona porque Now reflete a data e hora atuais do sistema, que são corrigidas após a retomada.
O Loop de Polling Impede que o Word Feche Corretamente
Se o usuário tentar fechar o Word enquanto o loop está em execução, o aplicativo pode travar. Adicione uma condição de saída usando uma variável booleana pública. Por exemplo, defina Public m_cancelPolling As Boolean como False no início. Dentro do loop, verifique If m_cancelPolling Then Exit Do. No evento Document_BeforeClose, defina m_cancelPolling = True.
OnTime vs Loop de Polling: Comparação de Comportamento Após Retomada do Modo Inativo
| Item | Application.OnTime | Loop de Polling com DoEvents |
|---|---|---|
| Comportamento após suspensão ou hibernação | Ignora a macro agendada completamente | Continua verificando e executa a macro após a retomada |
| Uso de CPU durante inatividade | Quase zero (sem loop) | Baixo a moderado, dependendo do uso da API Sleep |
| Requer cancelamento manual ao fechar o documento | Não | Sim, para evitar travamento |
| Suporta agendamentos recorrentes | Sim, chamando OnTime novamente dentro da macro | Sim, reentrando no loop após a execução da macro |
| Funciona com mudanças de estado de energia do sistema | Não | Sim |
Após aplicar o método de loop de polling, suas macros agendadas sobreviverão à retomada do modo inativo sem serem ignoradas. A principal mudança é substituir um temporizador passivo por uma verificação ativa que cede o controle via DoEvents. Para código de produção, adicione tratamento de erros dentro do loop usando On Error Resume Next e um número máximo de iterações para evitar loops infinitos. Teste a macro com vários ciclos de suspensão para confirmar a confiabilidade.