Automatização de Aplicativos Windows
usando o AutoHotKey
Muitos processos de negócio dependem de aplicativos de terceiros que assumem a
presença de um operador humano para executar determinadas rotinas. Isto é
particularmente verdadeiro em plataforma MS-Windows, onde, como o próprio nome sugere,
estes pacotes consistem em um conjunto de aplicativos com interface gráfica.
Neste artigo, procuro mostrar como criar um script robusto para automatizar tarefas
rotineiras, com foco em como deixá-lo pronto para uso sob controle do Light/BM.
Estrutura de Um Script
Ao criar o script de robotização de uma aplicação com o AutoHotkey, é comum (e tentador!)
usar o utilitário AutoScriptWriter para capturar a sequência de comandos a ser executada.
Embora esta abordagem funcione em ambiente desktop, para o qual o AutoHotkey foi
desenvolvido e no qual normalmente queremos apenas eliminar a digitação sequências de
teclas, a robotização de uma aplicação em ambiente de produção requer scripts mais
robustos.
Robustez, neste contexto, significa acima de tudo a capacidade de executar a tarefa em
questão e apenas esta tarefa, tratando qualquer desvio não previsto como um erro a ser
notificado ao operador. Implementar esta robustez requer uma visão clara do que a
aplicação faz, quais os requisitos para que ela opere e quais os resultados esperados.
Para se ter uma noção do que isto significa na prática, considere um job qualquer cuja rotina
possa ser representada pela seguinte sequência de passos:

Iniciar a aplicação;

Efetuar o logon na mesma (quando aplicável);

Selecionar a função desejada da aplicação, normalmente via menu;

Preencher os parâmetros para execução da rotina;

Aguardar a finalização da rotina;

Fechar a aplicação.
Vamos analisar o que deve ser feito em cada passo para tornar a rotina robusta.
1 de 6
Iniciar a aplicação
A implementação é trivial: usa-se o comando Run do AutoHotkey. Mas, espere ! Quem disse
que a aplicação está onde deveria ? E se estiver e alguém tiver mudado as permissões de
acesso ?
Robustez, aqui, significa testar se o executável realmente foi criado após o retorno do
comando. A forma mais comum de se fazer isto é:
Run, aplicativo, , UseErrorLevel, AppPID
If ErrorLevel = ERROR
{
BMLogMessage("Erro iniciando aplicação, cód " . A_LastError )
Exit, -1
}
Outro ponto a considerar: Acesso simultâneo à tela. Scripts são particularmente sensíveis às
interferências provocadas por usuários ou outras aplicações que estejam rodando
simultaneamente. Para reduzir o risco associado a este tipo de situação, a orientação é
utilizar o comando BlockInput, bem como fazer uso das rotinas de semáforo do Light/BM:
; Bloqueia usuário durante a execução do script
BlockInput, On
; Evita dois scripts do BM ao mesmo tempo neste computador
mutex := BMOpenMutex(“BMLOCK_” . %COMPUTERNAME% )
if ( BMWaitMutex(mutex,60000) < 0 )
{
BMLogMessage(“Erro esperando mutex”)
exit,2
}
Algumas observações sobre o trecho acima:
 O BlockInput bloqueia teclado e mouse;
 Isto significa que o operador só poderá executar alguma intervenção caso o script
finalize ou explicitamente reabilite-os via BlockInput, Off;
 Note a forma como o nome do semáforo foi criado, levando em conta o nome do
computador;
 Isto significa que apenas um script poderá rodar na mesma máquina;
 mesmo de outros usuários/sessões;
 No caso de um servidor Terminal Services, uma forma alternativa é limitar a apenas
um;
2 de 6
 script por sessão;
 Isto pode ser feito utilizando-se a variável de ambiente %SESSIONNAME%.
Efetuar o Logon
A tela de logon costuma ser um diálogo modal que surge logo após a aplicação ser iniciada.
Outro cenário possível é termos uma opção de menu que, por sua vez, abre o diálogo
modal. Fora usuário e senha, é possível que se tenha campos adicionais a preencher,
indicando o servidor de banco de dados, p.ex.A sequência normal usando WinWaitActive é
a recomendada, porem use sempre a variante com timeout e verifique após a saída de a
janela apareceu:
WinWaitActive, Logon,,5
IfWinNotActive, Logon
{
BMLogMessage("Timeout aguardando tela de logon")
Exit,-1
}
Uma vez que a tela tenha aparecido, preencha os campos usando o Send.
Para navegação entre campos, dê preferência ao uso dos aceleradores no lugar de teclas
de navegação ({tab}) ou simulação de mouse. Outro ponto importante é quanto ao
tratamento da senha em si.
Em um ambiente de produção, a senha deve estar "hardcoded" no script (diretamente ou via
#include), o qual será compilado em executável pela área de segurança. Esta técnica
permite atender o requisito de que o operador não deve ter acesso a uma senha de
aplicação.
Após o envio de usuário/senha, deve-se verificar possíveis erros. Os mais comuns são:
 Usuário/senha inválido;
 Senha expirada;
 Erro de acesso ao servidor.
A tática mais comum para tratar estes erros consiste em iniciar um loop onde se verifica as
possíveis situações de erro. Este loop controla também a duração total de execução do
script, o qual, via de regra, deve ter sempre um mecanismo de timeout. Uma forma simples
e efetiva de implementar este controle é por meio da variável A_TickCount, que retorna um
valor inteiro que corresponde ao número de milissegundos decorridos desde o último boot.
3 de 6
; Salva o instante em que começamos
loopStart := A_TickCount
elapsed := 0
; Loop de 1 minuto (60s * 1000)
While elapsed < 60000
{
; Testa erro de logon
IfWinActive, Erro ahk_class #32770
{
WinGetText, msg
BMScreenShot()
BMLogMessage("Erro efetuando logon :" . msg )
Send, !{F4}
Exit, -10
}
; .... Outros testes
; Apareceu a janela principal ?
IfWinActive, Aplicativo
{
BMLogMessage("Logon efetuado com sucesso")
Break
}
; Se não aconteceu nada, dorme um pouco para não
; monopolizar a CPU
Sleep, 100
; Atualiza tempo decorrido
elapsed := A_TickCount - loopStart
}
Algumas observações:
 ahk_class #32770 indica um "message box" padrão do windows. O valor é obtido
pelo utilitário AutoIt3 Windows Spy, que acompanha a distribuição do AutoHotkey;
 Note o uso generoso de mensagens de log em todos os pontos de falha. Em caso de
erro, a produção é responsável por passar o máximo de detalhes do ocorrido, e
nada melhor que estas mensagens para descobrir o que aconteceu.
Preencher Parâmetros
Este passo, embora possa ser trabalhoso no caso de telas complexas, normalmente requer
menos tratamentos de erro, já que a sequência de preenchimento, em princípio, deve estar
4 de 6
correta. Procure, no entanto, verificar o que acontece quando dados incorretos forem
inseridos nos campos. É comum surgirem "popups" que devem ser removidos para que o
processo continue. Como no caso do login, dê preferência à navegação entre campos por
meio dos aceleradores. Isto torna o script menos sensível a eventuais mudanças no layout
do formulário que alterem a ordem de navegação. Infelizmente, são poucas a as aplicações
"in-house" que utilizam de forma correta este tipo de recurso...Um caso particular de
preenchimento é a seleção de um ou mais itens de uma lista de valores apresentada em um
"listbox". É comum que esta lista seja montada dinamicamente a partir de uma consulta à
base. Isto significa que seu conteúdo pode variar entre execuções. Digamos que a
automação requer a seleção de um item específico (p.ex, o nome de um cliente, passado via
parâmetro para o script). Como resolver a situação ?Simples(ou quase). Utilize a função
ControlGet. Esta função possui vários sub-comandos, dentre os quais o que resolve este
caso é o List. Com ele, você recupera em uma variável toda a lista contida no controle, e
pode processá-la em um Loop, Parse:
; Assumo que o foco já está no ListBox
ControlGet, Opcoes,List,,ListBox1
Loop, Parse, Opcoes, `n
{
opcao=%A_LoopField%
if ( opcao = "Joao" )
{
; Seleciona o item
Send, {SPACE}Break
}
; Desce para o próximo
Send, {DOWN}
}
Aguardar a finalização da rotina
Mais uma vez, a melhor técnica é implementar um loop de espera, onde a cada ciclo
verifica-se a presença de erros e/ou de um indicador de final da operação. Tanto um quanto
outro costumam ser identificados por uma combinação das funções IfWinExist, IfWinActive
ou semelhantes, em uma estrutura idêntica à fase de logon. Um ponto que pode passar
despercebido é que, em alguns cenários, a aplicação sob controle do script pode ser
finalizada por outros meios, mesmo com a entrada inibida. Por exemplo, o administrador da
máquina pode utilizar um utilitário para "matar" o processo, o que provoca o
"desaparecimento" das janelas. Para evitar que o script fique aguardando até o fim do prazo
5 de 6
limite para identificar esta situação, utilize a função Process, Exist, passando como
argumento o identificador do processo que foi obtido ao iniciar a aplicação:
; Espera até 3min (aprox.) para finalização.
loopStart := A_TickCount
elapsed := 0
While elapsed < 180000
{
; Testes para identificar erros
IfWinExist, Erro ahk_class #32770
{
BMScreenShot()
Break
}
; Teste para fim da operação
IfWinExist, Informação ahk_class #32770
{
BMScreenShot()
Break
}
; Aplicação está viva ?
Process, Exist, %AppPID%
If ErrorLevel = 0
{
BMLogMessage("Aplicação terminou de forma inesperada !")
Break
}
; Espera de 100ms
Sleep, 100
; Atualiza tempo decorrido
elapsed := A_TickCount - loopStart
}
Conclusão
Scripts de automação bem construídos são fundamentais para operar de forma suave sua
produção, especialmente em um ambiente onde estas rotinas operam sob a supervisão do
Lighthouse/BM. As técnicas apresentadas neste artigo, desenvolvidas a partir de casos
reais, servem como referência para atingir este objetivo.
Referências Adicionais:
AutoHotKey - Site do pacote descrito neste artigo
6 de 6
Download

Automatização de Aplicativos Windows usando o