Под управлением сторонним приложением я подразумеваю некие действия своей программы, эмулирующие действия живого человека. Например, нажатия на кнопки в диалоговых окнах. Попробуем сделать это на примере лоадера для популярного файлового менеджера Total Commander. Кто пользуется им, тот знает, что единственное различие между полной и незарегистрированной версией в том, что триалка при запуске показывает наг-скрин с предложением купить программу или нажать для продолжения одну из трех кнопок с цифрами. Конечно, кнопку можно нажать самостоятельно, но можно доверить это лоадеру.

Наг-скрин Total Commander
Чтобы работать с содержимым окна, сперва надо узнать его хэндл. Проще всего воспользоваться функцией FindWindow, указав в качестве параметра наименование класса наг-скрина. Его можно посмотреть при помощи моей программы WinDowzer или любой другой аналогичной программы. Противное окно имеет название класса TNASTYNAGSCREEN, первый шаг сделан. Теперь нам надо узнать хэндл нужной кнопки, для этого нужно изучить все связи родительских и дочерних объектов окна.
Иерархический список хэндлов всех элементов нужного окна можно получить при помощи последовательного вызова функции FindWindowEx или же рекурсивного вызова функции EnumChildWindows. Вот что у меня получилось:
TNASTYNAGSCREEN - Total Commander
TNotebook
TPage - NagPage
TPanel
TPanel - 3
TButton - &3
TButton - &2
TButton - &1
TPanel - 3
TButton - &3
TButton - &2
TButton - &1
TButton - &Registration info
TButton - Program &information
TPanel
TPanel - 3
TButton - &3
TButton - &2
TButton - &1
TPanel - 3
TButton - &3
TButton - &2
TButton - &1
TButton - &Registration info
TButton - Program &information
TPage - StartupPage
TPanel
TPanel
TPage - UserPage
TButton - &Update Information
TButton - &Update Information
TPage - NagPage
TPanel
TPanel - 3
TButton - &3
TButton - &2
TButton - &1
TPanel - 3
TButton - &3
TButton - &2
TButton - &1
TButton - &Registration info
TButton - Program &information
TPanel
TPanel - 3
TButton - &3
TButton - &2
TButton - &1
TPanel - 3
TButton - &3
TButton - &2
TButton - &1
TButton - &Registration info
TButton - Program &information
TPage - StartupPage
TPanel
TPanel
TPage - UserPage
TButton - &Update Information
TButton - &Update Information
В списке для удобства я записал название класса и текст дочерних окон. Казалось бы, в момент перебора надо просто "нажать" нужную кнопку. Но здесь не все так просто, так как каждый раз выбирается случайный номер кнопки, и если нажать неправильную, то Total Commander просто завершит работу. Значит нам еще надо узнать правильный номер кнопки, прежде чем "нажать" на нее. Обратите внимание, что номер кнопки повторяется в форме несколько раз, и длина строки (то есть цифры) - всегда один символ. Облегчим себе задачу и рекурсивно переберем все дочерние элементы наг-скрина, а строку длиной один символ будем считать правильным номером.
; Секция данных
button db '&',0,0
buff rb 3
; Секция кода
...
invoke EnumChildWindows,[nag],FindNastyNumber,NULL
...
proc FindNastyNumber hwnd:dword,lParam:dword
mov [buff],0
invoke GetWindowText,[hwnd],buff,100
invoke lstrlen,buff
cmp eax,1
jne @f
invoke lstrcpy,button+1,buff
@@:
invoke EnumChildWindows,[hwnd],FindNastyNumber,NULL
mov eax,1
ret
endp
Нужный нам номер в виде строки теперь записан в переменную button, причем именно так, как он написан на кнопке, то есть "&3". Нам осталось найти хэндл самой кнопки и "нажать" на нее. Воспользуемся точно таким же трюком, рекурсивно пройдемся по всем дочерним элементам и кликнем по всем найденным элементам, текст на которых совпадает с искомым номером кнопки.
Лирическое отступление. Наиболее правильная эмуляция клика на каком-либо элементе окна выглядит следующим образом:
invoke SendMessage,[hwnd],WM_SETFOCUS,NULL,NULL
invoke SendMessage,[hwnd],WM_LBUTTONDOWN,MK_LBUTTON,NULL
invoke SendMessage,[hwnd],WM_LBUTTONUP,MK_LBUTTON,NULL
Возвращаемся к перебору дочерних элементов наг-скрина.
...
invoke EnumChildWindows,[nag],ClickNastyButton,NULL
...
; Рекурсивная функция перебора всех дочерних элементов
proc ClickNastyButton hwnd:dword,lParam:dword
mov [buff],0
; Прочитать текст дочернего элемента
invoke GetWindowText,[hwnd],buff,3
; Это текст искомой кнопки?
invoke lstrcmp,buff,button
or eax,eax
jnz @f
; Эмулировать нажатие на кнопку
invoke SendMessage,[hwnd],WM_SETFOCUS,NULL,NULL
invoke SendMessage,[hwnd],WM_LBUTTONDOWN,MK_LBUTTON,NULL
invoke SendMessage,[hwnd],WM_LBUTTONUP,MK_LBUTTON,NULL
@@:
invoke EnumChildWindows,[hwnd],ClickNastyButton,NULL
mov eax,1
ret
endp
Вот и все. Осталось приделать к лоадеру функцию запуска программы, чтобы наг-скрин закрывался сразу же после старта Total Commander, но это уже к делу не относится. В приложении пример программы, находящей и закрывающей наг-скрин уже запущенного Total Commander.