<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[Серый форум &mdash; AutoHotkey: правильно нажимаем кнопки]]></title>
	<link rel="self" href="http://forum.script-coding.com/extern.php?action=feed&amp;tid=84&amp;type=atom" />
	<updated>2006-07-31T12:52:09Z</updated>
	<generator>PunBB</generator>
	<id>http://forum.script-coding.com/viewtopic.php?id=84</id>
		<entry>
			<title type="html"><![CDATA[AutoHotkey: правильно нажимаем кнопки]]></title>
			<link rel="alternate" href="http://forum.script-coding.com/viewtopic.php?pid=203#p203" />
			<content type="html"><![CDATA[<p>Речь пойдет о том, как правильно скриптом нажимать всяческие кнопки, разнообразных окон. Например, кнопки диалогов подтверждений.<br />Эта инфа частично воспроизводит мой пост на прежнем форуме.<br />Поехали. Возьмем для примера Total Commander (неважно, используете вы Total Commander или нет - детали, про которые мы будем говорить, понятны и так). Как известно, при копировании, ТС выдает запрос на подтверждение этого действия. И избавиться от этого подтверждения средствами самого ТС нет никакой возможности. Допустим, нас это достало и мы хотим, чтобы за нас кнопку &quot;ОК&quot; нажимал скрипт. Казалось бы &quot;чего проще&quot;? Пишем скрипт.</p><div class="codebox"><pre><code>#Persistent ; скрипт будет запущен постоянно
SetTimer, Auto_Window, 200 ; переходить к указанной подпрограмме через каждые 0.2 секунды
Return ; закончить автовыполняющуюся часть

Auto_Window: ; метка входа таймера автоматизации окон
; TOTAL COMMANDER: ПОДТВЕРДИТЬ КОПИРОВАНИЕ
IfWinActive, ahk_class TInpComboDlg ; если активно окно, где мы подтверждаем копирование/перемещение, то...
    Send, {Enter} ; собственно подтверждение
Return ; конец подпрограммы по таймеру</code></pre></div><p>Работает? Работает. Теперь представьте ситуацию: скрипт определил, что нужное окно сейчас активно и вот-вот нажмет Enter. Но прежде, чем он успел это сделать, выскочило окно какой-нибудь другой проги (маловероятно, что у вас кроме ТС не запущено других прог, а вот то, что кто-то из них чего-то у вас спросит - очень даже вероятно. Например, файрволл выскочит с сообщением, что &quot;Файл &#039;Офигеть-какой-вирус&#039; запрашивает входящий доступ. Разрешить?&quot;...). А тут как раз наш скрипт дошел до команды &quot;Send, {Enter}&quot;, и выполнил её. Пока вы поймете что произошло, упомянутый файл уже успеет бухнуть в командную строку &quot;format c:&quot; или еще чего по-тихому наворочать. Ситуацию я, конечно, утрирую, но только чтобы подчеркнуть смысл: всякие Send&#039;ы лучше, по-возможности, направлять в целевое место.</p><p>Это можно сделать несколькими способами:<br /><strong>Первый способ</strong> - вместо команды &quot;Send, {Enter}&quot; написать:<br /></p><div class="codebox"><pre><code>{
    ControlFocus, TButton5 ; установить фокус на кнопку ОК
    ControlSend,, {Enter} ; подтвердить
}</code></pre></div><p>Тогда можно почти не сомневаться, что всё сработает как надо. (О надежности способа см. ниже).<br />Кстати, конкретно в этом случае, можно не использовать команду &quot;ControlFocus, TButton5&quot;, т.к. фокус и так на кнопке &quot;ОК&quot;, но я всё же её написал, потому что в другом случае, фокус может быть и не на нужной кнопке, а команда &quot;ControlSend,, {Enter}&quot; нажимает ту кнопку, которая имеет фокус.<br />Логично было бы предположить, что будет работать команда &quot;ControlSend, TButton5, {Enter}&quot;, однако она почему-то не работает (т.е. работает, но только потому, что TButton5 - кнопка &quot;с фокусом&quot;, а если её изменить на, например, TButton4, то все равно будет нажиматься TButton5).</p><p><strong>Второй способ</strong> - использовать вместо команды &quot;Send, {Enter}&quot; команду &quot;ControlClick&quot;:<br /></p><div class="codebox"><pre><code>ControlClick, TButton5 ; подтвердить</code></pre></div><p><strong>Третий способ</strong> - вместо команды &quot;Send, {Enter}&quot; посылаем кнопке не ENTER, а SPACE (вот гадство!):<br /></p><div class="codebox"><pre><code>ControlSend, TButton4, {Space} ; подтвердить</code></pre></div><p><strong>Теперь о надежности этих способов:</strong><br /><strong>Способ 1</strong>. Иногда в силу разных причин (винда занята каким-то важным для нее процессом, антивирус чрезмерно бдит и т.д.), нужный контрол не успевает получить фокус, и последующая команда нажмет не ту кнопку. Можно или мириться с ненадежностью скрипта (но это не наш выбор), или повесить цикл, в котором будет устанавливаться и проверяться фокус.<br /></p><div class="codebox"><pre><code>Loop ; убеждаемся, что подтверждение принято
{
    ControlFocus, TButton5 ; ставим фокус на нужный контрол
    If ErrorLevel = 0 ; если контрол получил фокус, то...
        Break ; конец цикла
    Sleep, 10*A_index ; постепенно увеличивающаяся пауза, чтобы контрол успевал среагировать
}
ControlSend,, {Enter} ; собственно подтверждение</code></pre></div><p><strong>Способ 2</strong>. У него страдания те же (нужный контрол не успевает получить фокус), но зависят от движений мыши. Т.е. если в ответственный момент клика, вы (по злому умыслу, например), двинете мышью, то, если момент выбран правильно <img src="//forum.script-coding.com/img/smilies/smile.png" width="15" height="15" /> клик промахнется. Лечить можно так-же как и в первом случае, а можно немного видоизменить проверку:<br /></p><div class="codebox"><pre><code>Loop ; убеждаемся, что подтверждение принято
{
    ControlClick, TButton5 ; подтвердить
    Sleep, 10*A_index ; постепенно увеличивающаяся пауза, чтобы контрол успевал среагировать
    IfWinNotExist ; если (последнее найденное) окно уже НЕ существует, то...
        Break ; конец цикла
}</code></pre></div><p><strong>Способ 3</strong>. По моему наблюдению (быть может недостаточному, неквалифицированному или предвзятому), <strong>этот способ работает надежнее всего</strong>. Правда и здесь не все 100% надежности, но мне так и не удалось выяснить отчего он иногда не срабатывает. Я рекомендую использовать этот способ, но в особо ответственных скриптах подстаховаться нижеприведенным методом:<br /></p><div class="codebox"><pre><code>Loop ; убеждаемся, что подтверждение принято
{
    ControlSend, TButton5, {Space} ; подтвердить
    Sleep, 10*A_index ; постепенно увеличивающаяся пауза, чтобы контрол успевал среагировать
    IfWinNotExist ; если (последнее найденное) окно уже НЕ существует, то...
        Break ; конец цикла
}</code></pre></div><p>Таким образом, самый надежный вариант целиком выглядит так:<br /></p><div class="codebox"><pre><code>#Persistent ; скрипт будет запущен постоянно
SetTimer, Auto_Window, 200 ; переходить к указанной подпрограмме через каждые 0.2 секунды
Return ; закончить автовыполняющуюся часть

Auto_Window: ; метка входа таймера автоматизации окон
; TOTAL COMMANDER: ПОДТВЕРДИТЬ КОПИРОВАНИЕ
IfWinExist, ahk_class TInpComboDlg ; если СУЩЕСТВУЕТ окно, где мы подтверждаем копирование/перемещение, то...
    Loop ; убеждаемся, что подтверждение принято
    {
        ControlSend, TButton5, {Space} ; подтвердить
        Sleep, 10*A_index ; постепенно увеличивающаяся пауза, чтобы контрол успевал среагировать
        IfWinNotExist ; если (последнее найденное) окно уже НЕ существует, то...
            Break ; конец цикла
    }
Return ; конец подпрограммы по таймеру</code></pre></div><p>Да, немного громоздко, но зато надежно!<br /><strong>Обратите внимание</strong>, у такого способа есть еще одно преимущество. Можно проверять не АКТИВНОСТЬ окна (а команда Send высылает клавиши ТОЛЬКО в активное окно), а СУЩЕСТВОВАНИЕ окна. Т.е. если какое нибудь окно в неподходящий момент перекроет то окно с которым мы работаем, то это нисколько не помешает сработать скрипту правильно.<br />В связи с изложенным, общее правило таково: <strong>если вместо команды Send, есть возможность использовать ControlSend или ControlSetText, то лучше (надежнее) их и использовать</strong>. И не ленитесь вводить дополнительные проверки нестандартных ситуаций (глюкующие проги-скрипты вряд ли кого-нибудь радуют). <img src="//forum.script-coding.com/img/smilies/smile.png" width="15" height="15" /><br />К описанным способам, добавлю, что пробовал я нажимать кнопки и через SendMessage, но выигрыша в надежности по сравнению с третьим способом не заметил, а вот усложнение - заметное. Пока в поиске. Если кто нашел - поделитесь, думаю буду рад не я один.</p>]]></content>
			<author>
				<name><![CDATA[Androgen]]></name>
				<uri>http://forum.script-coding.com/profile.php?id=4</uri>
			</author>
			<updated>2006-07-31T12:52:09Z</updated>
			<id>http://forum.script-coding.com/viewtopic.php?pid=203#p203</id>
		</entry>
</feed>
