1 (изменено: Drugoy, 2013-10-05 04:42:08)

Тема: AHK: ControlSend в неактивное окно

Здравствуйте.
Я написал скрипт, который позволяет переключаться между вкладками в окне Notepad++ прокруткой колеса мыши над панелью вкладок.

#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.

; Tab Wheel Scroll for Notepad++

#IfWinActive ahk_class Notepad++
~WheelUp::
MouseGetPos,,,,isItTabbar,1
If (isItTabbar = "SysTabControl325")
Send {Browser_Back}
Return

~WheelDown::
MouseGetPos,,,,isItTabbar,1
If (isItTabbar = "SysTabControl325")
Send {Browser_Forward}
Return

Скрипт работает, но мне захотелось его усовершенствовать: сейчас скрипт требует, чтобы окно Notepad++ было активно, а хочется избежать этого требования: ahk умеет же определять имя control'а под курсором, а с помощью ControlSend вроде бы можно посылать нажатие каких-то кнопок в неактивные окна.
Я перепробовал всё, но ControlSend в этом коде почему-то не даёт никакого результата:

~WheelUp::
MouseGetPos,,,id,isItTabbar,1
WinGetClass, class, ahk_id %id%
If (isItTabbar = "SysTabControl325") && (class = "Notepad++")
    ControlSend,,{Browser_Back},ahk_id %id%
Return

~WheelDown::
MouseGetPos,,,id,isItTabbar,1
WinGetClass, class, ahk_id %id%
If (isItTabbar = "SysTabControl325") && (class = "Notepad++")
    ControlSend,,{Browser_Forward},ahk_id %id%
Return

Условие выполняется, но ControlSend не делает ничего.

2

Re: AHK: ControlSend в неактивное окно

Я так понимаю, что это невозможно.
Подскажите тогда, кто-нибудь, пожалуйста, как сделать отправку {Browser_Back} и {Browser_Forward} в окно Notepad++ используя PostMessage?
Прочитал тьюториал по работе с этой командой, поставил и покрутил Winspector, но всё равно не понял ничего.

3

Re: AHK: ControlSend в неактивное окно

Drugoy пишет:

Прочитал тьюториал по работе с этой командой, поставил и покрутил Winspector, но всё равно не понял ничего.

Попытайтесь ещё раз , как альтернатива Winspector'у - Window Detective

global hScintilla
WM_APPCOMMAND:=0x319

~WheelUp::
if !hScintilla
GetNotepadScintilla()
else PostMessage WM_APPCOMMAND,0,0x10000,,ahk_id %hScintilla%
Return

~WheelDown::
if !hScintilla
GetNotepadScintilla()
else PostMessage WM_APPCOMMAND,0,0x20000,,ahk_id %hScintilla%
Return

GetNotepadScintilla()
{
	ControlGet,hScintilla,Hwnd,,Scintilla1,ahk_class Notepad++
	if ErrorLevel
	MsgBox Ошибка при получении дескриптора, проверьте запущен ли Notepad++
}

4 (изменено: vo1ty, 2012-11-05 13:30:55)

Re: AHK: ControlSend в неактивное окно

Есть только один нюанс. Если вы переоткроете NPP, то PostMessage будет отправляться не туда, то есть, по сути, вообще не будет, поэтому лучше каждый раз снова находить окно. ИМХО, это не сильно-то и ресурсозатратно. Можно, например, сделать так:

global hScintilla
WM_APPCOMMAND:=0x319

~WheelUp::
MouseGetPos,,,id,isItTabbar,1
WinGetClass, class, ahk_id %id%
If (isItTabbar = "SysTabControl325") && (class = "Notepad++")
{
    GetNotepadScintilla()
    PostMessage WM_APPCOMMAND,0,0x10000,,ahk_id %hScintilla%
}
Return

~WheelDown::
MouseGetPos,,,id,isItTabbar,1
WinGetClass, class, ahk_id %id%
If (isItTabbar = "SysTabControl325") && (class = "Notepad++")
{
    GetNotepadScintilla()
    PostMessage WM_APPCOMMAND,0,0x20000,,ahk_id %hScintilla%
}
Return

GetNotepadScintilla()
{
    ControlGet,hScintilla,Hwnd,,Scintilla1,ahk_class Notepad++
    if ErrorLevel
    MsgBox Ошибка при получении дескриптора, проверьте запущен ли Notepad++
}

5 (изменено: Drugoy, 2012-11-06 07:38:24)

Re: AHK: ControlSend в неактивное окно

Спасибо большое вам обоим за помощь!
Мне интересно понять как работает написанный вами код и как вы узнали нужную команду, а также wParam и lParam для неё.
Как вы отловили, что нужное нам событие для PostMessage это именно WM_APPCOMMAND 0x319 с wParam и lParam равными [0, 0x10000] и [0, 0x20000]? Я был бы чрезмерно благодарен какому-нибудь тьюториалу по использования Winspector'а или аналогичной программы для этих целей. В существующей статье - почему-то рассказывается, что отлавливать нужно было не WM_APPCOMMAND, а "WM_COMMAND, начинающееся с "Code: 0"".

vo1ty пишет:

Если вы переоткроете NPP, то PostMessage будет отправляться не туда

Это из-за того, что вы используете

PostMessage WM_APPCOMMAND,0,0x10000,,ahk_id %hScintilla%

и тут у меня возникает недопонимание, почему вы используете его так, ведь синтаксис PostMessage же такой:

PostMessage, Msg [, wParam, lParam, Control, WinTitle, WinText, ExcludeTitle, ExcludeText]

Почему Control вы оставили пустым, а для WinTitle использовали не ahk_id самого Notepad++, а ahk_id Scintilla1? Т.е. тут Scintilla рассматривается не как контрол, а как окно?
Почему тогда не работает

ControlSend,,{Browser_Forward},Scintilla1

?
Почему обязательно нужно обращаться к Scintilla1 по его хэндлу?
Обязательно ли посылать PostMessage контролам именно по их хэндлам?
Логично же было бы использовать, что-то вроде

PostMessage, WM_APPCOMMAND,0,0x10000,Scintilla1

или

PostMessage, WM_APPCOMMAND,0,0x10000,,Scintilla1

(я пока ещё не очень хорошо понимаю, когда к объекту стоит обращаться как к окну, а когда как к контролу).

6 (изменено: Drugoy, 2012-11-06 18:39:16)

Re: AHK: ControlSend в неактивное окно

Ещё вопрос по ControlSend'у.
Есть другая часть скрипта, которая позволяет переключаться между вкладками простым прокручиванием колеса над ними в редакторе AkelPad.

#NoEnv			; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input	; Recommended for new scripts due to its superior speed and reliability.

~WheelUp::
MouseGetPos,,,id,control,1
WinGetClass,class,ahk_id %id%
If (control = "SysTabControl321") && (class = "AkelPad4")
	ControlSend,,{Ctrl Down}{Shift Down}{Tab}{Shift Up}{Ctrl Up},ahk_id %id%
Return

~WheelDown::
MouseGetPos,,,id,control,1
WinGetClass,class,ahk_id %id%
If (control = "SysTabControl321") && (class = "AkelPad4")
	ControlSend,,{Ctrl Down}{Tab}{Ctrl Up},ahk_id %id%
Return

Проблема в том, что если открыть окно AkelPad с несколькими вкладками, затем переключиться на любое другое окно и покрутить колесом над вкладками AkelPad, то его окно почему-то активируется.
Такое происходит только один (1-ый) раз: если снова активировать другое окно и покрутить колесом над вкладками AkelPad - его окно больше не активируется.
Если же окно AkelPad закрыть и снова открыть, то глюк снова вернётся на 1 раз.
Почему так происходит? Как этого избежать?

7

Re: AHK: ControlSend в неактивное окно

1. Что делать, если ни WinSpector, ни WindowDetective не показывают вообще никаких сообщений ни для одного окна AkelPad?
2. Помогите разобраться с отправкой скролла в активное и не активное окно встроенного в винду просмотрщика .chm файлов (HH):
я уже настроил её для всего чего хотел, кроме прокручиваемого списка во вкладке "Указатель" в левой части окна:
https://i.imgur.com/KloPaTE.png
никак не получается сделать так, чтобы этот список прокручивался если окно .chm-просмотрщика неактивно, а пользователь крутит колесом мыши над этим списком.

Сейчас сделал так (в скрипте присутствуют части и не относящиеся к HH): менять надо только строки 27-28 на else (без if) что-то.

+ код скрипта
$WheelUp::
$WheelDown::
    MouseGetPos, m_x, m_y, id, control, 1
    hw_m_target := DllCall("WindowFromPoint", "int", m_x, "int", m_y)
    WinGetClass, class, ahk_id %id%
    WinGet, id_A, ID, A
    If (class == "Notepad++") && (control == "SysTabControl325")    ; Notepad++: ahk_class Notepad++    tab-bar classNN: SysTabControl325    listener-window classNN: Scintilla1
    {
        ControlGet, properTargetWin, Hwnd,, Scintilla1, ahk_id %id%
        PostMessage, 0x319, 0, (A_ThisHotkey == "$WheelUp") ? 0x10000 : 0x20000,, ahk_id %properTargetWin%
    }
    Else If (class == "CabinetWClass") && (control == "SysTreeView321")    ; Windows Explorer's left pane with a tree of folders.
        PostMessage, 0x20A, (A_ThisHotkey == "$WheelUp") ? 120 << 16 : -120 << 16, ( m_y << 16 )|m_x,, ahk_id %hw_m_target%
    Else If (control == "TSTabCtrlClass1")    ; Miranda IM, TabSRMM window's tab-bar classNN: TSTabCtrlClass1    listener-window classNN: RichEdit20W1
    {
        ControlGet, properTargetWin, Hwnd,, RichEdit20W1, ahk_id %id%
        PostMessage, 0x319, 0, (A_ThisHotkey == "$WheelUp") ? 0x10000 : 0x20000,, ahk_id %properTargetWin%
    }
    Else If (class == "HH Parent")    ; Windows built-in *.chm (help files) reader.
    {
        If (control == "SysTabControl321")    ; Left pane's either tab-bar or 2nd tab's contents.
        {
            ControlGetPos,, ya,,, SysTabControl321, ahk_id %id%
            WinGetPos,, yb,,, ahk_id %id%
            If (m_y < ya + yb + 20)    ; Hovering tab-bar.
                ControlSend,, % (A_ThisHotkey == "$WheelUp") ? ("{Ctrl Down}{Shift Down}{Tab}{Shift Up}{Ctrl Up}") : ("{Ctrl Down}{Tab}{Ctrl Up}"), ahk_id %id%
            Else If (id == id_A)    ; Hovering 2nd tab's contents when .chm reader's window is active.
                Send, % (A_ThisHotkey == "$WheelUp") ? "{WheelUp}" : "{WheelDown}"
        }
        Else
            PostMessage, 0x20A, (A_ThisHotkey == "$WheelUp") ? 120 << 16 : -120 << 16, ( m_y << 16 )|m_x,, ahk_id %hw_m_target%
    }
    Else If (class == "AkelPad4" && control == "SysTabControl321") || (class == "IEFrame" && control == "DirectUIHWND2")    ; AkelPad + forgot what else.
        ControlSend,, % (A_ThisHotkey == "$WheelUp") ? ("{Ctrl Down}{Shift Down}{Tab}{Shift Up}{Ctrl Up}") : ("{Ctrl Down}{Tab}{Ctrl Up}"), ahk_id %id%
    Else If (id != id_A) && (class != "Progman")    ; If the cursor hovers an inactive window (and which is not the desktop) - that inactive window should receive a scrolling event (without the activation of that window).
        PostMessage, 0x20A, (A_ThisHotkey == "$WheelUp") ? 120 << 16 : -120 << 16, ( m_y << 16 )|m_x,, ahk_id %hw_m_target%
    Else If (id == id_A) || (class == "Progman")    ; If the cursor hovers the active window or desktop - the regular scrolling event should get sent to the active window.
        Send, % (A_ThisHotkey == "$WheelUp") ? "{WheelUp}" : "{WheelDown}"
    currOpacity := hwndUnderCursor := x1 := y1 := id := id_A := winClass := maximized := winX1 := winY1 := x2 := y2 := winX2 := winY2 := IDactive := control := key := value := Class := properTargetWin := Hwnd := Height := m_x := m_y := ya := yb := hw_m_target := onTop := procName := XWin := YWin := ""
Return