1

Тема: AHK: Как правильно использовать ControlSend для управления ползунками?

Есть такой код:

#SingleInstance, Force
DetectHiddenWindows, On

F1::
F2::
    WinGetActiveTitle, title    ; Store the title of the active window in a var 'title'
    Run, C:\Windows\SysWOW64\SndVol.exe    ; Exec SndVol
    WinWaitActive, Микшер громкости
    WinGet, SndVolPID, PID
    WinGet, controls, ControlList
; Вместо всего этого блока хотелось бы использовать ControlSend, но не знаю как
    Send {Tab 2}
    If (A_ThisHotkey = "F1")
        Send {PgUp}
    Else
        Send {PgDn}
;
    Process, Close, %SndVolPID%
    Return

В нём, как видно, используется Send. А необходимо перейти на использование ControlSend, но у меня не получилось этой командой заставить двигаться ползунки volume-бара (любого).

2

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

У меня с этим кодом ползунок на месте остаётся.

3 (изменено: Drugoy, 2013-04-16 15:12:41)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Ваш код, только циферку контрола последнюю менял (смотрел по WinSpy).

4

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Хм, да, был. Без него всё ок. Не знал, что он может помешать (кстати, почему?)

5 (изменено: creature.ws, 2013-04-16 16:12:42)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Единственный признак, который уникально идентифицирует окно — ahk_id (он же дескриптор окна, он же hwnd, он же window handle). Задавая какой-либо команде autohotkey иной критерий поиска окна, вы подразумеваете, что команда будет применена к первому найденному окну, соответствующему указанным данным.
WinWait ahk_exe SndVol.exe без DetectHiddenWindows, On находит «нужное» окно, иначе:

DetectHiddenWindows On
WinGet list, List, ahk_exe SndVol.exe
while WinExist("ahk_id" list%A_Index%) {
    WinGetClass winClass
    classList .= winClass . "`n"
}

MsgBox % classList

Можно использовать пример из третьего сообщения с добавленнием DetectHiddenWindows Оn
и WinWait ahk_class #32770 ahk_exe SndVol.exe

6

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Спасибо вам обоим, помогли разобраться.

7

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

creature.ws пишет:

Можно использовать пример из третьего сообщения с добавленнием DetectHiddenWindows Оn
и WinWait ahk_class #32770 ahk_exe SndVol.exe

Да не, это не нужно, т.к. я эту строку добавлял в надежде, что можно будет ControlSend в спрятанное окно посылать, но если использовать "Run SndVol.exe,, Hide" то "WinGet, controls, ControlList" ничего не видит. И даже если Hide заменить на Min - такой же результат.

8 (изменено: creature.ws, 2013-04-17 17:00:11)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

WinGet, controls, ControlList" ничего не видит


DetectHiddenWindows On
Run SndVol.exe,, hide
WinWait ahk_class #32770 ahk_exe SndVol.exe
Sleep 300
WinGet controls, ControlList
WinClose
MsgBox % controls
+ argh

Не правда ли, логично сделано, WinGet var, List возвращает псевдомассив дескрипторов, а WinGet var, ControlList сохраняет в переменной список имён классов дочерних окон через разделитель `n.

9

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

serzh82saratov пишет:

А зачем? Что вы хотите регулировать "неглядя", Системные звуки, или ещё что-то.

хотел сделать управлению громкостью для активного приложения.
Но уже понял, что проще будет это сделать через стороннюю утилиту nircmd.

10 (изменено: Drugoy, 2013-04-17 20:00:48)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

creature.ws пишет:

Не правда ли, логично сделано, WinGet var, List возвращает псевдомассив дескрипторов, а WinGet var, ControlList сохраняет в переменной список имён классов дочерних окон через разделитель `n.

Не понял сути этого пассажа: у меня в коде и использовался ControlList.
Разница между моим кодом и предложенным вами лишь в уточнении ahk_class, я думал, что ahk_exe достаточно.
С "ahk_class #32770" ещё проблема в том, что это достаточно распространённый класс, встречается много где.
Спасибо за код.

11 (изменено: creature.ws, 2013-04-18 10:49:25)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Неожиданное развитие беседы.
Вы код из сообщения #10 запускали? Список окон видели? При использовании ahk_exe SndVol.exe без ahk_class #32770 первым будет найдено скрытое окно с класcом tooltips_class32

В 10-м сообщении используется ahk_class #32770 ahk_exe SndVol.exe, с учётом вашей фразы

помогли разобраться

я предположил, что вышеупомянутые факты вами обработаны, потому проблемы с WinGet controls, ControlList могли быть связаны лишь с ошибочным ожиданием, что ControlList возвращает псевдомассив, по аналогии с List.
Как оказалось, мои предположения были слишком оптимистичными.

ещё проблема в том, что это достаточно распространённый класс

А что, у SndVol.exe есть еще окна с подобным классом? Иначе, в чём проблема?

12

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Меня немного запутало то, что я получал пустой messagebox, т.е. думал, что сама команда не срабатывает.
На деле же, команда срабатывала, просто у скрытых окон процесса sndvol.exe нет никаких контролов, потому messagebox и пустой.
На этот раз со всем разобрался, спасибо вам обоим.

Хотя я не знаю что значит "громкость активного приложения", не посвятите?

У нас же с Вами одинаковые ОС, разве вы не видите в списке sndvol.exe справа столбцы с управлением громкостью для отдельных приложений? Моё окно выглядит вот так:
http://storage1.static.itmages.ru/i/13/0417/h_1366227223_9147432_0274000f6e.png
Ну, а под активным - понимается то, чьё окно в данный момент активно (я уже, правда, заранее знаю, что почему-то список sndvol.exe сам по себе динамичный (меняется между вызовами), и что там некоторые вещи, вроде не отображаются (лисий плагин флеша, например)).

13 (изменено: Drugoy, 2013-04-18 03:11:21)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

serzh82saratov пишет:

Ничего не пойму??? В 15 посте приведён код, он получает все контролы и из скрытого окна!

Всё уже ок, говорю же, просто не допонял сначала.

serzh82saratov пишет:

Ну и... ???
Как вы "указываете" скрипту какой контрол из них дёргать?

Вот даже знаете что лисий ползунок мона подёргать, и знаете что окно его сейчас есть. Как вы номер ползунка указываете, т.е. 32%какой-то%?

Вот Вам готовый скрипт:

#SingleInstance, Force
DetectHiddenWindows, On

F1::
F2::
    WinGetActiveTitle, title    ; Store the title of the active window in a var 'title'
    If (RegExMatch(title, ".* - Mozilla Firefox") = true)    ; Fix for Mozilla Firefox
        title := "Mozilla Firefox"
    Run SndVol.exe ;,, Hide
    WinWait ahk_class #32770 ahk_exe SndVol.exe
    WinGet, controls, ControlList
    ; msgbox % controls
    Loop, parse, controls, `n
    {
    ; msgbox %A_LoopField%
        If (A_Index = thisControl)
        {
            ControlSend, %A_LoopField%, % A_ThisHotkey = "F1" ? "{PgUp}" : "{PgDn}"
            thisControl :=
        }
        Else If (RegExMatch(A_LoopField, "Static.*") = true)
        {
            ControlGetText, thisControlText, %A_LoopField%
            ; MsgBox Current control: '%A_LoopField%'`nText: '%thisControlText%'`nTitle we search for: '%title%'
            If (RegExMatch(thisControlText, title ": громкость$") = true)
            {
                thisControl := A_Index + 1
                ; Msgbox Title '%title%' matches '%thisControlText%'!`nA_LoopField: '%A_LoopField%'
            }
        }
    }
    ; WinClose
    Return

А сейчас я застрял над проблемой с окнами мирандовского TabSRMM:

If (RegExMatch(thisControlText, title ": громкость$") = true)

это условие с ними почему-то никогда не выполняется, хотя если раскомменировать строки с MsgBox-ами (который я использую для debugging'а) - то видно, что они совпадают.
С Mozilla Firefox, Media Player Classic - Home Cinema и foobar2000 скрипт нормально срабатывает, даже в hide режиме.

14

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

А не подскажете всё-таки решение проблемы с Miranda NG'шным TabSRMM-ом?
Я не понимаю, почему title с припиской ": громкость" равны содержимому thisControlText, но условие

If (RegExMatch(thisControlText, title ": громкость$") = true)

не выполняется. А с другими окнами других программ - нормально выполняется.

15 (изменено: creature.ws, 2013-04-18 21:24:54)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

MsgBox % "true = true:`t"       (true = true)
        . "`n""1"" = true:`t`t" ("1" = true)
        . "`n1 = true:`t`t"     (1 = true)
        . "`n123 = true:`t"     (123 = true)
        . "`n!!123 = true:`t"   (!!123 = true)

RegExMatch возвращает позицию символа в строке, или ноль, если шаблон в строке не присутствует.
Число отличное от нуля можно интерпретировать как true, но число само по себе не равно true.
Если подстрока соответствующая шаблону поиска не нужна, а важен сам факт нахождения шаблона — используйте оператор ~=.

Прочитав абзац выше и запустив код, вы уже всё поняли, но всё же:

If RegExMatch(thisControlText, title ": громкость$")
If thisControlText ~= title ": громкость$"

16

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Того:

If thisControlText ~= "\Q" title "\E: громкость$"

17

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

creature.ws
1. С Вашим кодом всё по-прежнему: со всеми окнами работает, кроме окна TabSRMM у Miranda-NG.
2. Ваш вариант мне больше нравится (я пока плоховато разбираюсь в правильности составления условий), но и RegExMatch здесь тоже должен бы срабатывать, но почему-то этого не происходит.

serzh82saratov
Перед контролом, отвечающим за ползунок управления громкостью какого-то приложения, находится контрол "StaticN" (вместо N - число), с текстом "Имя_приложения: громкость". Петля (loop) в моём коде как раз и ищет совпадение title активного окна с текстом у всех "StaticN" контроллов, находя нужный. Как только она его нашла - мы знаем его порядковый номер, но т.к. контрол, отвечающий за ползунок - следующий в списке контроллов, то мы должны добавить единицу к найденному порядковому номеру и сохранить это всё в какую-то переменную:

thisControl := A_Index + 1

На следующей итерации петли - мы проверяем, соответствует ли текущая итерация петли (A_Index) числу в той переменной, и если соответствует - то это и есть контрол, отвечающий за ползунок и мы его и посылаем ControlSend, а потом просто удаляем ту переменную (после этого стоит сделать и разрыв петли (Break), который я не добавил в код, т.к. занимался debugging-ом).
В принципе, можно даже ничего не записывать в переменную "thisControl", а просто создавать её и проверять само наличие. Это сэкономит целых несколько байт памяти.

Слова "Miranda NG'шным TabSRMM-ом" мне вообще ни о чём не говорят sad Вы бы хоть значения title привели, рабочее и нет.

А что конкретно не понятно? Miranda NG - это мультипротокольный мессенеджер для разных сетей. TabSRMM - плагин от этой программы, отвечающий за окна чат-контейнеров. Без этого (или аналогичного) плагина - некому было бы создавать окна чата. Title у окна выглядит, например, вот так: "Drugoy (Online)". Ради дебага пробовал заранее вбивать этот текст в переменную и искать совпадение текста "StaticN" с текстом из этой переменной - тогда всё срабатывает.
А "$" - это не часть title'а, это часть RegExp синтаксиса, означает, что данное выражение должно находиться в конце строки. Его можно удалить, без него всё так же работает.

18 (изменено: Drugoy, 2013-04-18 22:24:17)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Edit2: попробовал

If thisControlText ~= "\Q" title "\E: громкость$"

Всё заработало как надо, спасибо!

19

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

serzh82saratov пишет:

Дело и правда что ли в каких-то "мета-символах", о которых я ничего не знаю.

Регулярные выражения (RegEx) - краткий справочник пишет:

Экранированные (escaped) символы: большинство литер (например, abc123) внутри шаблона понимаются буквально. Но чтобы литеры, которые используются как метасимволы (\.*?+[{|()^$), понимались в обычном смысле, их надо предварять влево наклоненной чертой. Например, \. означает буквально точку, и \\ означает буквально левую наклонную.

Всё по-прежнему.

Не понял.

Это я не понял сначала - уже подправил то сообщение: всё работает, спасибо.
Про метасимволы - не подумал, что нужно экранировать и те, что внутри переменной title.

20

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

(GetText = StaticText ": громкость") ? trackbar := A_Index + 1 

Я протестую .

(GetText = StaticText ": громкость") ? trackbar := A_Index + 1 : ""

Если хочется лаконично записать выражение типа if () then ()

(GetText = StaticText ": громкость") && trackbar := A_Index + 1 

21

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

serzh82saratov пишет:

осмелюсь предложить такой код:

У предложенного Вами скрипта другой функционал: он управляет только "системными звуками", мой же скрипт - управляет громкостью приложения, чьё окно в данный момент активно.

Плюс, в мой скрипт можно легко добавить управление "Системными звуками", например, вот так:

    Else If (title = "Program Manager")
        title := "Системные звуки"

И тогда управлять громкостью системных звуков можно будет если перед нажатием хоткея активировать рабочий стол.
(Можно добавить такую же привязку и к Панели задач, например)

22 (изменено: Drugoy, 2013-04-19 04:40:46)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

serzh82saratov пишет:

Кто же ВАМ не даёт изменить в моём коде переменную StaticText таким же образом как и в своём коде переменную title??? Какая-то религия не позволяет?

Видимо вы очень уж сильно полюбили свой с таким трудом собранный скрипт...

Похоже Вы не читаели то, что я написал, когда описывал работу своего скрипта и не понимаете, что в моём коде эта переменная получает значение соответствующее заголовку активного (в момент нажатия хоткея) окна: т.е. я не вписываю её в код руками, как это делаете Вы в своём коде.
А для "Mozilla Firefox" просто пришлось делать исправление, т.к. окно имеет не такой заголовок, каким оно отображается в SndVol.exe.

Но в принципе, если в Вашем коде исправить

Else
    Return

на

Else
    WinGetActiveTitle, StaticText

то он начинает работать так же, как и мой код.
Спасибо за то, что прикрутили подсказку с текущим уровнем громкости, вот только мне больше хочется её видеть не в виде TrayTip, а в виде OSD прогрессбара.

23

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

А расскажите, пожалуйста, работу этих двух строк:

    ControlSend,, % A_ThisHotkey = "#2" ? "{PgUp}" : "{PgDn}", % "ahk_id " H := RegExReplace(ListHwnd, "S).*" Hwnd "`n(.*?)`n.*", "$1")
    SendMessage, 0x0400,,,, ahk_id %H%

Что это за SendMessage?
Что делает RegExReplace(ListHwnd, "S).*" Hwnd "`n(.*?)`n.*", "$1")?
Такое тяжело читается, когда писал не сам.

24

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Разобрался с RegExReplace, а вот как Вы установили, что 0x0400 это Trackbar constants (Slider) - я не понял: по той ссылке 0х0400 в табличке соотствует двум вещам: WM_USER и TBS_DOWNISLEFT и ничто из этого по названию вроде бы не похоже на постоянный трэкбара (слайдера).
В ahk всё что связано с WinAPI - для меня пока что остаётся магией, даже GDI+ с трудом понимаю и далеко не весь.
Спасибо за код и особенно за разъяснения.

25

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

WM_USER                 := 0x0400
; Messages 
TBM_GETPOS              := (WM_USER)

Константа WM_USER = 0x400. В контексте «окна с ползунком» сообщение с номером 0x400 можно назвать TBM_GETPOS. Отправляя с помощью SendMessage сообщение с номером 0x400 окну типа msctls_trackbar32 в ErrorLevel вы получите положение  ползунка.

26 (изменено: Drugoy, 2013-04-19 22:59:06)

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Я захотел приделать сюда 3-ий хоткей: mute.
Для него получать дескриптор окна надо другим правилом:

ControlGet, thisHwnd, Hwnd,, % thisTitle ": отключить звук"

потом

ControlClick,, ahk_id %thisHwnd%

Только вот не знаю как научиться различать состояние muted/unmuted: чтобы показывать для них разные тултипы.

#SingleInstance, Force
DetectHiddenWindows, On
SetTitleMatchMode, 3

; Choose step value (percents)
Step := 10

#Volume_Mute::
#Volume_Up::
#Volume_Down::
    WinGetClass, Class, A
    If (Class = "MozillaWindowClass")
        thisTitle := "Mozilla Firefox"
    Else If (Class ~= "(Progman|Shell_TrayWnd)")
        thisTitle := "Системные звуки"
    Else
        WinGetActiveTitle, thisTitle
    Run, SndVol.exe,, Hide
    WinWait, ahk_class #32770 ahk_exe SndVol.exe
    If (A_ThisHotkey = "#Volume_Mute")    ; Hotkey dependent code
        ControlGet, thisHwnd, Hwnd,, % thisTitle ": отключить звук"
    Else
        ControlGet, thisHwnd, Hwnd,, % thisTitle ": громкость"
    WinGet, ListHwnd, ControlListHwnd
    WinGet, List, ControlList
    If (A_ThisHotkey = "#Volume_Mute")    ; Hotkey dependent code
        ControlClick,, ahk_id %thisHwnd%
    Else
    {
        thisHwnd := RegExReplace(ListHwnd, "S).*" thisHwnd "`n(.*?)`n.*", "$1")
        SendMessage, 0x0400,,,, ahk_id %thisHwnd%
        volLvl := A_ThisHotkey = "#Volume_Up" ? Step : -Step    ; Hotkey dependent code
        volLvl := ErrorLevel - volLvl
        volLvl := volLvl > 100 ? 100 : volLvl < 0 ? 0 : volLvl
        SendMessage, 0x0400 + 34, , % volLvl,, ahk_id %thisHwnd%
        TrayTip, % thisTitle, % 100 - volLvl "%", 1, 1
    }
    WinClose
    Return

27

Re: AHK: Как правильно использовать ControlSend для управления ползунками?

Слишком много непонятного кода для пары таких простых вещей. Спасибо, обойдусь без этого или попробую что-то ещё.