Проблема: запуск AutoIT программы в блокированном сеансе Windows
Удивительно, проработав несколько лет мой скрипт AutoIT для автоматизации сборки проекта из MG-SOFT Visual MIB Builder 9 в один день перестал работать и файлы перестали собираться. При активном рабочем столе однако все работало. Также и при подключении по удаленному доступу к рабочему столу — скрипты работают пока окно не свернуто и активно.
Предыстория
MG-SOFT Visual MIB Builder 9 у нас лицензионный потому мы его до сих пор используем для работы, но вот вручную экспортировать готовые MIB и вручную же их проверять это рутинно и скучно, особенно при наличии сервера автоматизации Jenkins.
Я пользуясь привилегией лени над палкой, написал на AutoIT 3 автоматизатор, который кликал куда надо за меня и на вывод кидал бы мне список ошибок, если они есть. Если сборка падает я сразу в письме получаю список ошибок в проекте по каждому файлу.
Чтобы Jenkins мог выполнять задачи в которых задействованы графические приложения я создал удаленный сборщик Jenkins (далее jenkins-gui-win-slave) на этой же машине. На ПК создан отдельный пользователь ci-server в сеансе которого при входе в систему запускается из автостарта скрипт запуска агента Jenkins. Подключается агент через Java WebStart, JNLP. Чтобы jenkins-gui-win-slave стартовал для пользователя ci-server настроен автоматический вход в систему при старте ПК, в автозапуске лежит скрипт запуска jenkins-gui-win-slave, а в планировщике заданий Windows есть задача "при входе" выполнить "скрипт блокировки сеанса пользователя" — это позволяет сразу после входа заблокировать от нежелательных посетителей сервера сеанс ci-server.
В итоге при старте системы происходит автоматический вход для пользователя `ci-server`, запуск агента `jenkins-gui-win-slave`, блокирование сеанса пользователя `ci-server`.
Проблема
Программы при блокировке сеанса Windows помещает приложения в изолированный сеанс Session 0 для обеспечения безопасности, т.к. считается, что активность в графическом приложении во время блокировки должна отсутствовать. Это накладывает ограничения, и часть оконных функций тогда не работает.
В отношении к AutoIT это приводит к тому, что невозможно дождаться события активации окна WinWaitActive, WinActive всегда возвращает ошибку, невозможно управлять мышью, невозможно переключить активное окно, потому и функции работающие в контексте активного окна перестают работать, например Send.
Решение
Есть два решения: без изменения кода и с изменением кода.
1. Без изменения кода
Установить виртуальную машину, в нее поставить Windows, агент Jenkins и нужное для сборки ПО. Причем для Windows 7 Pro можно использовать легально Windows XP Mode — получаете виртуалку с XP и на ней все гоняете. Или используйте другое ПО для виртуализации, например VirtualBox, туда придется ставить ОС отдельно. Для виртуалки нужно настроить IP адрес, чтобы агент Jenkins мог подключаться к мастер серверу Jenkins. Для XP Mode IP адреса также можно настроить.
В виртуалке можно использовать пользователя по умолчанию.
В виртуалке в папку Автозапуск поместить скрипт командной строки (.bat\.cmd) для запуска агента Jenkins.
Пример скрипт запуска агента Jenkins:
rem Runs jenkins-gui-win-slave to work with GUI builds
SET http_proxy=
set https_proxy=
"C:\Program Files\Java\jre1.8.0_151\bin\java.exe" -jar "C:\Program Files\Jenkins\war\WEB-INF\slave.jar" -jnlpUrl http://<jenkins-master-addr>:8080/computer/jenkins-gui-win-slave/slave-agent.jnlp -secret <secret_string>
:: Где
:: jenkins-master-addr — адрес MASTER сервера jenkins
:: secret_string — ключ доступа к серверу для агента, см. в настройках агента на сервере Jenkins
Отключить скринсервер, отключить функцию "начитать с блокировки экрана". Настроить автоматический вход для пользователя без пароля при старте системы.
Далее настроить автозапуск виртуалки.
В этом случае даже при блокировке сеанса пользователя в виртуальной машине сеанс будет не заблокирован. Естественно вы несете расходы на виртуалку — это обосновано, когда у вас много скриптов и их переделка и отладка займет много времени или у вас приложения которые работают не так как обычные и им нужно обязательно активное окно для работы с его контролами.
2. С изменением кода
Я пошел этим путем, т.к. у меня только одна такая задача была.
Чтобы обойти ограничения режима блокировки, нужно:
- отказаться от тех функций, что работают с активными окнами или заменить их на альтернативные.
- Отказаться от движения мышью MouseMove, MouseClick и т.п.
- Заменять функцию Send на ControlSend или ControlClick.
Примечание: скрипты записанные Au3Record используют как раз функции не работающие в режиме блокировки сеанса.
С приложением работаем так будто бы окна уже активны в момент обращения.
Заменяем:
; Делаем окно активным и жмем кнопку
if (WinActivate($windowName)) Then
; Нажать кнопку в окне
ControlClick($windowName, $buttonName, "")
EndIf
На:
; Попытаемся сделать активным окно. При блокировке сеанса может не срабатывать, но нам и не важно
WinActivate($windowName)
; Нажимаем кнопку
ControlClick($windowName, $buttonName, "")
; или ControlSend($windowName, "", $buttonClass, "{SPACE}")
Лучше дожидаться появления окна через WinWait и после использовать полученный хендл окна для отправки ему сообщений.
В данном случае можно работать с существующей инфраструктурой на сервере, но не все скрипты могут быть так однозначно трансформировать. Некоторые приложения могут быть не приспособлены для такого взаимодействия, тогда придется прибегнуть к варианту 1.