Тема: AHK: Взаимодействие с диалогами, принадлежащими собственному процессу
Рассмотрим пример по включению настройки «Отображать содержимое окна при перетаскивании» подделкой действий пользователя с апплетом Панели управления.
(Это нужно лишь для примера, саму задачу можно решить с помощью
DllCall("SystemParametersInfo"
без всяких мелькающих окон):
Run % "rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2",,, PID
WinWait Свойства: Экран ahk_pid %PID%
WinGet HWND, ID
ControlClick &Эффекты..., ahk_id %HWND%
WinWait Эффекты
Control Check,, &Отображать содержимое окна при перетаскивании
ControlSend ,, {Enter}
WinWait ahk_id %HWND%
ControlClick ThemePreview1
ControlSend ,, {Enter}
Но ведь вместо rundll32.exe можно вызвать функцию из DLL собственными средствами AHK: раз уж наш сценарий начал выполняться, значит они доступны, а присутствует или нет rundll32.exe в точности не известно. Тем более, что мы выбрали функцию, вызываемую с помощью rundll32.exe лишь для примера. Если при этом вызвать функцию из DLL в отдельном процессе (например AHK-сценарий перезапускает себя с каким-либо ключом), то всё работает так же. Но опять же, если рассматривать общий случай, файл сценария может быть удалён во время его работы, поэтому хотелось бы обойтись одним процессом.
Если заменить первые две строки:
;Run % "rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2",,, PID
;WinWait Свойства: Экран ahk_pid %PID%
DllCall("shell32.dll\Control_RunDLL" . (A_IsUnicode? "W": "A")
, "UInt", A_ScriptHwnd ; HWND hwnd // 0 не работает...
, "Ptr", 0 ; HINSTANCE hInstance
, "Str", "desk.cpl,,2" ; LPTSTR lpszCmd
, "UInt", 1) ; nCmdShow
Process Exist
WinWait Свойства: Экран ahk_pid %ErrorLevel%
WinGet HWND, ID
ControlClick &Эффекты..., ahk_id %HWND%
WinWait Эффекты
Control Check,, &Отображать содержимое окна при перетаскивании
ControlSend ,, {Enter}
WinWait ahk_id %HWND%
ControlClick ThemePreview1
ControlSend ,, {Enter}
то переход к следующему действию после DllCall произойдёт только, когда созданное им окно будет закрыто сторонними средствами (например, вручную).
В cправке сказано:
Timers are useful because they run asynchronously, meaning that they will run at the specified frequency (interval) even when the script is waiting for a window, displaying a dialog, or busy with another task
Попробуем действия после DllCall, запустить в отдельном потоке с помощью таймера:
;Run % "rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2",,, PID
;WinWait Свойства: Экран ahk_pid %PID%
SetTimer Step2, -1
DllCall("shell32.dll\Control_RunDLL" . (A_IsUnicode? "W": "A")
, "UInt", A_ScriptHwnd ; HWND hwnd // 0 не работает...
, "Ptr", 0 ; HINSTANCE hInstance
, "Str", "desk.cpl,,2" ; LPTSTR lpszCmd
, "UInt", 1) ; nCmdShow
Exit
Step2:
Process Exist
WinWait Свойства: Экран ahk_pid %ErrorLevel%
WinGet HWND, ID
ControlClick &Эффекты..., ahk_id %HWND%
WinWait Эффекты
Control Check,, &Отображать содержимое окна при перетаскивании
ControlSend ,, {Enter}
WinWait ahk_id %HWND%
ControlClick ThemePreview1
ControlSend ,, {Enter}
— в этом случае кнопка «Эффекты» нажимается, поддиалог «Эффекты» открывается и на этом всё. Если его закрыть вручную, то основной диалог уже вручную не закрывается — надо завершать сам сценарий. Ещё можно заметить, что если после ControlClick вставить MsgBox, то до него дело дойдёт только после закрытия «Эффекты».
Что делать?
Первончальное название вопроса «Взаимодействие с диалогами, созданными DllCall» не совсем удачно, т.к. подобные случаи возможны и без использования DllCall. Например открываем диалог свойств ярлыка и пытаемся взаимодействовать с ним дальше, открываем поддиалог выбора значка и т.д. — Изменил.