Что нового

Автоматизированный ввод данных во внешнюю программу

The_Immortal

Новичок
Сообщения
84
Репутация
4
Здравствуйте!

Задача следующая. Есть программа, которая при определенном запросе вызывает свое cmd-окно, где требует ввести пароль для подтверждения. Это приходится делать довольно часто, поэтому хотелось бы как-то автоматизировать процесс ввода пароля.
У этого окна уникальное имя. По-другому не знаю как к нему обратиться.

Не могли бы вы подсказать в каком направлении надо копать?


Спасибо!
 

CrazyDoc

Новичок
Сообщения
75
Репутация
2
The_Immortal сказал(а):
Не могли бы вы подсказать в каком направлении надо копать?
https://yandex.ru/search/?text=autoit%20%D0%B2%D0%B2%D0%BE%D0%B4%20%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D0%B0%20&lr=213
начиная с первой ссылки
 

InnI

AutoIT Гуру
Сообщения
4,982
Репутация
1,460
CrazyDoc
начиная с первой ссылки
Я бы сказал - только первая ссылка :smile:
Особенно радует фраза "Показаны результаты для Москвы" :D

The_Immortal
Вот та первая ссылка при активном окне. Также, возможно, будут работать
Код:
ControlSend()
ControlSetText()
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
InnI,
Код:
Send()
не подходит, потому что нельзя гарантировать, что нужное окно будет в момент ввода информации активным. А те две функции требуют ControlID. А как определить какой элемент управления в cmd-окне?
 

CrazyDoc

Новичок
Сообщения
75
Репутация
2
The_Immortal сказал(а):
А как определить какой элемент управления в cmd-окне?
При помощи AutoIT Window Info, ищи в директории инсталляции Autoit.


Добавлено:
Сообщение автоматически объединено:

The_Immortal сказал(а):
нельзя гарантировать, что нужное окно будет в момент ввода информации активным
Код:
WinActivate


Пожалуйста, пользуйтесь поиском прежде чем задать следующий вопрос:
https://yandex.ru/search/?lr=213&text=autoit%20controlid%20cmd
на беглый взгляд - вторая ссылка, но там скорее всего есть еще несколько пригодных.
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
CrazyDoc, этот инструмент выдает только Class: ConsoleWindowClass и название окна. Никаких элементов не показывает, т.к. их там и нет.

Может быть есть ещё какие-нибудь варианты без принудительной активации?


Добавлено:
Сообщение автоматически объединено:

[?]
Пожалуйста, пользуйтесь поиском прежде чем задать следующий вопрос:
Обязательно!

Только там нет решения для меня, ибо[?]
Пишет не в окно, а в поток, а какое окно будет читать этот поток в том и будут данные.
Дело в том, что может быть открыто несколько cmd-окон, которые принимают данные (и имя их процесса одинаковое). Надо писать данные в определенное окно.
 

Sln

Знающий
Сообщения
49
Репутация
7
очевидно же - надо эти cmd-окошки как-то различить... мож даж по PixelGetColor.
 

InnI

AutoIT Гуру
Сообщения
4,982
Репутация
1,460
The_Immortal
только Class: ConsoleWindowClass и название окна
Код:
Run("cmd.exe")
$wnd = WinWait("[class:ConsoleWindowClass]")
ControlSend($wnd, "", "", "dir{enter}")


может быть открыто несколько cmd-окон
Чтобы работать с потоком, вам нужно скриптом запустить приложение. Следовательно, вы будете знать, с потоком какого окна вы работаете.
http://autoit-script.ru/autoit3_docs/functions/StdinWrite.htm
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
InnI, всё сложнее :( Поясню. Есть исходный program.exe. Я запускаю его с параметром: program.exe /do. После этого он открывает новое cmd-окно, где требуется ввести данные.
Если сразу после выполнения program1.exe /do запустить случайным образом какое-нибудь совершенно другое cmd-окно, то данные могут попасть туда:
Код:
Run("program.exe /do") ; инициализирует новое cmd-окно для ввода данных
Run("cmd.exe") ;симулируем, что в этот самый момент времени запустилось совсем другое cmd-окно.
$wnd = WinWait("[class:ConsoleWindowClass]") ; обнаруживает "cmd.exe" - не то, что надо
ControlSend($wnd, "", "", "dir{enter}") ; вводит данные в "cmd.exe" - не туда, куда надо
 

Sln

Знающий
Сообщения
49
Репутация
7
Код:
$p1 = Run('cmd.exe')
$p2 = Run('cmd.exe')

$h1 = _GetHwndFromPID($p1)
$h2 = _GetHwndFromPID($p2)

WinActivate($h1)
Send("help{enter}")


WinActivate($h2)
Send("dir{enter}")

MsgBox(0, @ScriptName, "так?")


Func _GetHwndFromPID($PID)
    $hWnd = 0
    $stPID = DllStructCreate("int")
    Do
        $winlist2 = WinList()
        For $i = 1 To $winlist2[0][0]
            If $winlist2[$i][0] <> "" Then
                DllCall("user32.dll", "int", "GetWindowThreadProcessId", "hwnd", $winlist2[$i][1], "ptr", DllStructGetPtr($stPID))
                If DllStructGetData($stPID, 1) = $PID Then
                    $hWnd = $winlist2[$i][1]
                    ExitLoop
                EndIf
            EndIf
        Next
        Sleep(100)
    Until $hWnd <> 0
    Return $hWnd
EndFunc ;==>_GetHwndFromPID

маленько разнообразил...
 

InnI

AutoIT Гуру
Сообщения
4,982
Репутация
1,460
The_Immortal
При помощи class я вам показал, как можно работать с этими окнами. Вам же нужно использовать заголовок. Либо перечислять окна конкретного процесса, PID которого вам вернёт функция Run, при помощи функции
Код:
_WinAPI_EnumProcessWindows()
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
InnI, блин, так всё же просто оказывается:
Код:
Run("program.exe /do") ; инициализирует новое cmd-окно для ввода данных
Run("cmd.exe") ;симулируем, что в этот самый момент времени запустилось совсем другое cmd-окно.
$wnd = WinWait("НужныйТайтл") ; обнаруживает нужное окно по его имени
ControlSend($wnd, "", "", "dir{enter}") ; вводит данные в куда надо
Спасибо большое!

Sln, благодарю за пример!
 

InnI

AutoIT Гуру
Сообщения
4,982
Репутация
1,460
The_Immortal
просто оказывается
Я об этом и говорю - используйте уникальные заголовки ваших окон, а ControlID оставляйте пустым
Код:
ControlSend("Уникальный заголовок", "", "", "password{enter}")
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
InnI, я изначально думал, что ControlID надо обязательно указывать, ибо он в справке не помечен как необязательный - это меня и смутило.

Но почему-то не срабатывает в первый раз:
Код:
Run("program.exe /do")
ControlSend("НужныйТайтл", "", "", "password{enter}") ; вводит данные в куда надо
Запускаю скрипт, открывается окно, где требуется ввести данные, и так и остается (без введенных данных). Закрываю это окно вручную, запускаю скрипт второй раз - всё отрабатывает... :scratch:

Такое ощущение, что ControlSend вводит в первый раз настолько быстро, что окно не успевает это воспринять. Пробовал всунуть между ними WinWait(), но поведение такое же.
 

InnI

AutoIT Гуру
Сообщения
4,982
Репутация
1,460
The_Immortal
ControlID надо обязательно указывать
И это так. Просто при указании пустого ID, AutoIt отправит сообщение элементу, имеющему фокус ввода, а при отсутствии элемента (как в вашем случае) - самому окну.

не срабатывает в первый раз
Потому что ControlSend() не ждёт появления окна или элемента, а просто завершается с ошибкой.
Добавьте WinWait().


Добавлено:
Сообщение автоматически объединено:

The_Immortal
Пробовал всунуть между ними WinWait()
Вы редактируете сообщения быстрее, чем я пишу ответы :smile:
Попробуйте
Код:
WinWaitActive()
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
InnI, WinWaitActive() не подойдет, т.к. я запускаю окно в спрятанном виде.
[?]
Пробовал всунуть между ними WinWait(), но поведение такое же.
Потестировал побольше - очень редко, но всё-таки бывает, что не успевает. Бог с ним, пойдет :smile:

Меня больше беспокоит, а что если вдруг появится окно с таким же названием? В этом случае надо [?]
перечислять окна конкретного процесса, PID которого вам вернёт функция
?
 

InnI

AutoIT Гуру
Сообщения
4,982
Репутация
1,460
The_Immortal
очень редко, но всё-таки бывает
Добавьте небольшую паузу 30-50 мс
Код:
WinWait()
Sleep(30)
ControlSend()


если вдруг появится окно с таким же названием
Но ведь можно составить более сложное условие с учётом Title-Class-RegexpTitle-RegexpClass и даже с размерами и положением (см. справку). Дополнительно можно проверить окно на видимость. Ну а для полной уверенности, можно и перечислением окон процесса заняться.
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
InnI, [?]
Ну а для полной уверенности, можно и перечислением окон процесса заняться.
Пробую:
Код:
#include <WinAPIProc.au3>
Local $aData = _WinAPI_EnumProcessWindows(Run("program.exe /do", "", @SW_HIDE),0)


Выдает @error = 11 :(





Добавлено:
Сообщение автоматически объединено:

Пробовал так:
Код:
#include <WinAPIProc.au3>
Local $pid = Run("program.exe /do)
Local $aData = _WinAPI_EnumProcessWindows($pid)
В $pid попадает правильное значение, у этого pid есть окно - в Диспетчере задач видно. Однако всё равно почему-то возникает ошибка.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,487
The_Immortal
Код:
$iPID = Run('program.exe /do') ; инициализирует новое cmd-окно для ввода данных
Run('cmd.exe') ;симулируем, что в этот самый момент времени запустилось совсем другое cmd-окно.
$hWnd = _WinWaitByPID($iPID, 'WindowTitle', True, 0) ; обнаруживает нужное окно по его PID
ControlSend($hWnd, '', '', 'dir{enter}') ; вводит данные в куда надо

Func _WinWaitByPID($iPID, $sTitle = '', $bVisible = True, $iTimeout = 0)
	Local $aWins, $iTimer = TimerInit()
	
	While 1
		If $sTitle Then
			$aWins = WinList($sTitle)
		Else
			$aWins = WinList()
		EndIf
		
		For $i = 1 To $aWins[0][0]
			If WinGetProcess($aWins[$i][1]) = $iPID And (Not $bVisible Or ($bVisible And BitAND(WinGetState($aWins[$i][1]), 2))) Then
				Return $aWins[$i][1]
			EndIf
		Next
		
		If $iTimeout And TimerDiff($iTimer) >= ($iTimeout * 1000) Then
			ExitLoop
		EndIf
		
		Sleep(10)
	WEnd
	
	Return 0
EndFunc
 
Верх