Что нового

Скрыть MsgBox и нажать на нем кнопку

The_Immortal

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

Ситуация следующая. Имеется окно с кнопкой, по нажатию на которую появляется MsgBox (с таким же заголовком, как и главное окно) с кнопками "Да" (выбрана по умолчанию) и "Нет". Необходимо всё это дело спрятать и нажать сначала на инициализирующую MsgBox кнопку, а потом на "Да" в MsgBox.

Проблема в том, что этот MsgBox не определяется как окно, поэтому я идентифицирую кнопку на нем по ID:

Код:
$sTitle = "MyTitle"
$sFileExe = "program.exe"

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle)
ControlClick($hWin, '', "[CLASS:TButton; INSTANCE:1]")
Sleep(1000)
ControlClick("", "", "[CLASS:TButton; ID: 6]"); кнопка "Да"

Мне этот вариант не очень нравится, т.к. кликание по ID как-то не надежно, поэтому я попробовал задействовать WinList() после вызова MsgBox'а:

Код:
$sTitle = "MyTitle"
$sFileExe = "program.exe"

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle)
ControlClick($hWin, '', "[CLASS:TButton; INSTANCE:1]")
$aWins = WinList($sTitle)
Sleep(1000)
ControlClick($aWins[1][1], "", [CLASS:TButton; INSTANCE:1]); попытка нажать кнопку "Да"
ControlClick($aWins[2][1], "", [CLASS:TButton; INSTANCE:1]); попытка нажать кнопку "Да"
- в $aWins приходят два хендла. Как видите, я пробовал использовать оба, но не один не реагировал на кнопку "Да".
И второе, что хотелось бы реализовать, это скрытие этого MsgBox'а вовсе. program.exe успешно скрывается при запуске, а вот MsgBox - нет.

Спасибо!

P.S. Программа не AutoIt'овская.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
CreatoR, [?]
Откуда мне знать, что этот ID уникален в этот момент времени или что на другой системе эта программа (эта кнопка на MsgBox'е) будет иметь тот же ID?

[?]
Нужно ожидать окно с таким же заголовком
Проблема в том, что этот MsgBox не определяется как окошко :(
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Код:
#include <WinAPI.au3>

$sTitle = "MyTitle"
$sFileExe = "program.exe"

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle)
ControlClick($hWin, '', '[CLASS:TButton; INSTANCE:1]')

$hMsgBox = _WinWaitChild($hWin, $sTitle)
ControlClick($hMsgBox, "", "TButton6"); кнопка "Да"

Func _WinWaitChild($hParent, $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 _WinAPI_GetParent($aWins[$i][1]) = $hParent 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
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Тогда как то так:

Код:
$sTitle = 'MyTitle'
$sFileExe = 'program.exe'

Run($sFileExe, '', @SW_HIDE)
$hWin = WinWait($sTitle)
ControlClick($hWin, '', 'TButton1')

$sCtrlID = _CtrlWait($hWin, 'TButton', 'Да')
ControlClick($hWin, '', $sCtrlID); кнопка "Да"

Func _CtrlWait($hWnd, $sID, $sText, $iTimeout = 0)
	Local $iTimer = TimerInit()
	Local $aCtrls, $iIDs = 0
	
	While 1
		$aCtrls = StringSplit(WinGetClassList($hWnd), @LF)
		
		For $i = 1 To $aCtrls[0]
			If $aCtrls[$i] = $sID Then
				$iIDs += 1
				
				If ControlGetText($hWnd, '', $sID & $iIDs) = $sText And ControlCommand($hWnd, '', $sID & $iIDs, 'IsVisible') Then
					Return $sID & $iIDs
				EndIf
			EndIf
		Next
		
		If $iTimeout And TimerDiff($iTimer) >= ($iTimeout * 1000) Then
			ExitLoop
		EndIf
		
		Sleep(10)
	WEnd
	
	Return 0
EndFunc
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
CreatoR, правильно ли я понимаю, что, т.к. кнопка "Да" относится к классу TButton и имеет INSTANCE = 1, должно срабатывать вот так:
Код:
$hMsgBox = _WinWaitChild($hWin, $sTitle)
ControlClick($hMsgBox, "", "[CLASS:TButton; INSTANCE:1]"); кнопка "Да"
?
Но так не срабатывает. А "TButton6" Вы указали чисто так или таким образом можно сокращать? В таком виде тоже не сработало.
В общем, оно явно не подхватывается. Проверил детальнее, условие:
Код:
If _WinAPI_GetParent($aWins[$i][1]) = $hParent
не срабатывает... Не находится родитель.

[?]
Тогда как то так:
И так не робит :(

В общем, проблема-то решаема:
Код:
$sTitle = "MyTitle"
$sFileExe = "program.exe"

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle)
ControlClick($hWin, '', "[CLASS:TButton; INSTANCE:1]")
WinSetState ($sTitle, "", @SW_HIDE)
Sleep(1000)
ControlClick("", "", "[CLASS:TButton; ID: 6]"); кнопка "Да"
- таким образом, ничего не показывается и всё нажимается, но мне это всё не нравится по указанным выше причинам.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
А что скажете по поводу этого: [?]
Откуда мне знать, что этот ID уникален в этот момент времени или что на другой системе эта программа (эта кнопка на MsgBox'е) будет иметь тот же ID?
- бред или доля правды в этом есть? Просто если это бред, то, наверное, и заморачиваться не стоит.
 
A

Alofa

Гость
Еще вариант (не очень красиво, но суть понятна):

Код:
$sTitle = "MyTitle"
$TextWin1 = 'текст родительского окна'
$TextWin2 = 'текст окна MsgBox'
$sFileExe = "program.exe"

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle, $TextWin1)
ControlFocus($hWin, '', '[CLASS:Button; INSTANCE:1]')
ControlClick($hWin, '', '[CLASS:Button; INSTANCE:1]')
Sleep(1000)
$hWin = WinWait($sTitle, $TextWin2)
WinSetState ($hWin, '', @SW_HIDE)
$sControl = ControlGetFocus($hWin)
ControlClick($hWin, '', $sControl); кнопка "Да"
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
Alofa, благодарю за решение, но во-первых, такой метод тоже не отличается особой надежностью, а во-вторых, он просто не срабатывает, ибо "текст программы" во втором случае не сканируется (не воспринимается). Я попробовал убрать параметр текста вовсе и @SW_HIDE - всё равно не подхватывает.

[?]
Значит выкладывайте программу program.exe, будем разбираться.
Я бы уже давно выложил, но, к сожалению, не имею на это права :(
 
A

Alofa

Гость
Значит у вас так сделать нельзя?
bd7838a30d6330aa7ee4133b536b0a0c.png
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
Alofa, хм... Info текст показывает также, как и у Вас... Странно, пойду смотреть дальше. Может я чего мудрю :-\

Да, извиняюсь, Ваш код отрабатывает. Только пришлось класс "Button" переделать на "TButton". Однако этот Бокс не хочет прятаться никак :smile: Куда я только этот WinSetState() не впихивал... Ни в какую не хочет. Я там выше писал что всё ок, мол прячется, но на самом деле это не так. Оно прячется только вот в таком виде:
Код:
#include <WinAPI.au3>

$sTitle = "MyTitle"
$sFileExe = "program.exe"

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle)
ControlClick($hWin, '', '[CLASS:TButton; INSTANCE:1]')

$hMsgBox = _WinWaitChild($hWin, $sTitle); просто так вызываем функцию
WinSetState ($sTitle, "", @SW_HIDE)
Sleep(1000); пауза наобум
ControlClick("", "", "[CLASS:TButton; ID: 6]"); кнопка "Да"

Func _WinWaitChild($hParent, $sTitle = "")
    Local $aWins
   
    While 1
        If $sTitle Then
            $aWins = WinList($sTitle)
        Else
            $aWins = WinList()
        EndIf
       
        For $i = 1 To $aWins[0][0]
            If _WinAPI_GetParent($aWins[$i][1]) = $hParent Then
                Return $aWins[$i][1]
            EndIf
        Next
       
        Sleep(10)
    WEnd
   
    Return 0
EndFunc
- понимаю, что это бред, но вот так ничего вообще не показывается и всё отрабатывает.

Попытался сделать по аналогии для вашего решения (я убрал параметр текста для первой кнопки и изменил класс Button):
Код:
#include <WinAPI.au3>

$sTitle = "MyTitle"
$sFileExe = "program.exe"
$TextWin2 = 'текст окна MsgBox'

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle)
ControlFocus($hWin, '', '[CLASS:TButton; INSTANCE:1]')
ControlClick($hWin, '', '[CLASS:TButton; INSTANCE:1]')

$hMsgBox = _WinWaitChild($hWin, $sTitle); просто так вызываем функцию
$hWin = WinWait($sTitle, $TextWin2)
WinSetState ($hWin, '', @SW_HIDE)
Sleep(1000); пауза наобум
$sControl = ControlGetFocus($hWin)
ControlClick($hWin, '', $sControl); кнопка "Да"

Func _WinWaitChild($hParent, $sTitle = "")
; ...
EndFunc
- почему-то Бокс виден.

Таким образом, выше два рабочих варианта, но в обоих просто так задействована функция. Правда, в первом случае она как-то помогает скрыть Бокс.Я не понимаю какая мистика отключает его видимость, но я пробовал вместо вызова неиспользуемой функции делать паузу, но с ней Бокс всё равно показывался.
А во втором случае я по аналогии решил попробовать.

В общем, чер-те что :smile: Но в итоге всё равно хотелось бы отталкиваться от хендла, который не подхватывается. А почему - совершенно не ясно. Ну не может же этот Бокс быть обособленным...
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
The_Immortal [?]
доля правды в этом есть?
Есть, ID может изменяться если элементы создаются под условием.
Но так как этот элемент (по моему мнению) создаётся в момент инициализации программы, то скорее всего его ID будет таким же.
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
Alofa, [?]
При объявлении переменной надеюсь вы ввели правильный текст.
Ну да. А к чему Вы спрашиваете? Я выше написал, что Ваше решение рабочее :smile:

У меня сейчас задача более однозначно идентифицировать этот MsgBox.

В общем, в прошлый раз были глюки с ОС, поэтому и не срабатывало. Сейчас получилось вот так (в урезанном виде, без проверок и прочего):

Код:
#include <WinAPI.au3>

$sTitle = "MyTitle"
$sFileExe = "program.exe"
$TextWin = "Вы подтверждаете свои действия?"

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle)
ControlClick($hWin, '', '[CLASS:TButton; INSTANCE:1]')

$hMsgBox = _WinWaitChild($hWin, $sTitle, $TextWin)
$bState = WinSetState($hMsgBox, $TextWin, @SW_HIDE) ; пытаюсь скрыть бокс
MsgBox(0,"WinSetState: ",$bState) ; показывает "1", но по факту не скрывается
WinWait($hMsgBox, $TextWin, 1) ; чтобы всё успело появиться
ControlClick($hMsgBox, "", "[CLASS:Button]")

Func _WinWaitChild($hParent, $sTitle, $TextWin)
    Local $aWins

	$aWins = WinList($sTitle)

	For $i = 1 To $aWins[0][0]
		If _WinAPI_GetParent ($aWins[$i][1]) = $hParent And _WinAPI_GetClassName ($aWins[$i][1]) = "#32770" And ControlGetHandle ($sTitle, $TextWin, "")  Then
			Return ($aWins[$i][1])
		EndIf
	Next

    Return 0
EndFunc


В условии одновременно проверяется принадлежность к родителю, определяется соответствие класса MsgBox (может оно и не к чему), а также наличие определенного текста на контроле.

Всё замечательно, только вот одно но - не скрывается этот Бокс никак: на долю секунды (перед своим нажатием) он всё же виден :(

[?]
- как видно, пытаюсь скрыть так:

Код:
$hMsgBox = _WinWaitChild($hWin, $sTitle, $TextWin)
$bState = WinSetState($hMsgBox, $TextWin, @SW_HIDE) ; пытаюсь скрыть бокс
MsgBox(0,"WinSetState: ",$bState) ; показывает "1", но по факту не скрывается
WinWait($hMsgBox, $TextWin, 1) ; чтобы всё успело появиться
ControlClick($hMsgBox, "", "[CLASS:Button]")


Но не получается.

Думал также использовать вот такой вариант, но есть вероятность, что основное окно program.exe скроется, а этот Бокс всё равно вылезет на не скрытом (обычном) рабочем столе, т.е. будет виден... Засада какая-то!
 

InnI

AutoIT Гуру
Сообщения
4,922
Репутация
1,432
The_Immortal
не скрывается этот Бокс никак: на долю секунды (перед своим нажатием) он всё же виден
Так он всё-таки скрывается или нет? Если скрывается но промигивает, то эта "доля секунды" может составлять до 250 мс. См. опцию "WinWaitDelay".

показывает "1", но по факту не скрывается
Вы смотрите результат выполнения функции, а не фактическое состояние окна. Функция нашла окно, отправила ему сообщение, вернула 1. Всё. Результат - скрылось окно или нет - не проверяется.

Попробуйте вместо скрытия переместить окно за пределы экрана, например в координаты (-5000, -5000).

И хорошо бы увидеть информацию с последней вкладки Au3Info при наведении на нужную кнопку. Да скриншот не помешал бы.
 
Автор
T

The_Immortal

Новичок
Сообщения
84
Репутация
4
InnI, [?]
Так он всё-таки скрывается или нет?
Пожалуй не скрывается вовсе, т.к., что с использованием WinSetState, что без этой функции - Бокс одинаково мелькает.

[?]
хорошо бы увидеть информацию с последней вкладки Au3Info при наведении на нужную кнопку.
Вот Summary на кнопку "Да" в этом Боксе (который нужно делать невидимым):
[box title=TitleBox]
Код:
>>>> Window <<<<
Title:	MyTitle
Class:	#32770
Position:	508, 437
Size:	270, 162
Style:	0x94C801C5
ExStyle:	0x00010101
Handle:	0x00000000003C0710

>>>> Control <<<<
Class:	Button
Instance:	1
ClassnameNN:	Button1
Name:	
Advanced (Class):	[CLASS:Button; INSTANCE:1]
ID:	6
Text:	&Да
Position:	72, 96
Size:	88, 26
ControlClick Coords:	41, 20
Style:	0x50030000
ExStyle:	0x00000004
Handle:	0x0000000000110A50

>>>> Mouse <<<<
Position:	624, 579
Cursor ID:	0
Color:	0xE6E6E6

>>>> StatusBar <<<<

>>>> ToolsBar <<<<

>>>> Visible Text <<<<
&Да
&Нет
Вы подтверждаете свои действия?


>>>> Hidden Text <<<<
[/box]

[?]
Попробуйте вместо скрытия переместить окно за пределы экрана

Вроде получилось:

Код:
#include <WinAPI.au3>

$sTitle = "MyTitle"
$sFileExe = "program.exe"
$TextWin = "Вы подтверждаете свои действия?"

Run($sFileExe, "", @SW_HIDE)
$hWin = WinWait($sTitle)
ControlClick($hWin, '', '[CLASS:TButton; INSTANCE:1]')

$hMsgBox = _WinWaitChild($hWin, $sTitle, $TextWin)
WinMove($hMsgBox, $sTextWin, - 5000, -5000) ; перемещаем Бокс далеко влево и вниз
WinWait($hMsgBox, $TextWin, 1) ; чтобы всё успело появиться
ControlClick($hMsgBox, "", "[CLASS:Button]")

Func _WinWaitChild($hParent, $sTitle, $TextWin)
    Local $aWins

    $aWins = WinList($sTitle)

    For $i = 1 To $aWins[0][0]
        If _WinAPI_GetParent ($aWins[$i][1]) = $hParent And _WinAPI_GetClassName ($aWins[$i][1]) = "#32770" And ControlGetHandle ($sTitle, $TextWin, "")  Then
            Return ($aWins[$i][1])
        EndIf
    Next

    Return 0
EndFunc


А насколько это надежный способ? :smile: По крайней мере мой глаз не успевает заметить Бокс.
 

InnI

AutoIT Гуру
Сообщения
4,922
Репутация
1,432
The_Immortal
Вот всё, что вам нужно
Код:
$Opt = Opt("WinWaitDelay", 10)
$hWnd = WinWait("MyTitle", "Вы подтверждаете свои действия?")
WinMove($hWnd, "", -5000, -5000)
ControlClick($hWnd, "", "Button1")
Opt("WinWaitDelay", $Opt)
 
Автор
T

The_Immortal

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

Код:
$Opt = Opt("WinWaitDelay", 10) ; в $Opt попадает "предыдущее значение опции" - эм... Т.е. "250". А причем тут "10"?
$hWnd = WinWait("MyTitle", "Вы подтверждаете свои действия?")
WinMove($hWnd, "", -5000, -5000)
ControlClick($hWnd, "", "Button1")
Opt("WinWaitDelay", $Opt) ; после клика делает паузу в 250 мс... Но зачем? Ведь окно уже перемещено.
 

InnI

AutoIT Гуру
Сообщения
4,922
Репутация
1,432
The_Immortal
После каждой Win-функции AutoIt ожидает время, установленное опцией WinWaitDelay (по умолчанию 250 мс). Поэтому вы успеваете увидеть окно. Чтобы окно не успело прорисоваться мы уменьшаем время ожидания до 10 миллисекунд, а текущее сохраняем в переменной. После всех действий возвращаем время ожидания в то, которое было (сохранено в $Opt). Но если вам без разницы, можете в начале скрипта прописать и забыть
Код:
Opt("WinWaitDelay", 10)
Последствия бывают критичны, т.к. может не хватить времени для создания кнопки и клик не пройдёт. В таких случаях либо возвращаем задержку обратно (окно ведь уже "спрятано" за границами экрана), либо просто делаем Sleep перед кликом.
 
Верх