使用 AutoHotkey 在 Adob​​e Reader 中水平滚动

使用 AutoHotkey 在 Adob​​e Reader 中水平滚动

我想使用 AutoHotkey 在 Adob​​e Reader X 中水平滚动文档。发送滚轮消息 (0x20e) 不起作用,发送滚动消息 (0x114) 也不起作用。我能找到的唯一方法是向滚动条箭头发送点击,但这会使水平滚动非常慢,此外还会使任何并发的垂直滚动严重滞后。此外,我注意到我的鼠标驱动程序 (UltraNav) 可以在 Adob​​e Reader X 中明显模态的对话框(例如“打开文件”对话框)下滚动,而上述三种方法都不能。那么有人知道我的鼠标驱动程序可能在做什么或有其他方法吗?

我刚刚发现了第四种方法,它对于前两种方法无法实现的大多数应用程序来说效果很好,即向滚动条发送箭头键。当它正确响应时,它还会响应按页滚动的 {PgUp} 和 {PgDn}。但是,它在模式对话框下仍然不起作用,因此鼠标驱动程序的作用对我来说仍然是个谜,尽管它似乎滚动了相同的量。此外,此方法在 Windows 资源管理器中不起作用(预期);发送到滚动条的键也会发送到主区域。例如,controlsend,%scrollbarname%,{Down},ahk_id %window%将成功滚动滚动条,但如果可能,还会导致当前选择位置向下移动。我找不到任何其他不发送鼠标点击来控制 Windows 资源管理器中水平滚动条的方法。

编辑

AutoHotkey 滚动、中键单击和鼠标加速这是我最初的目标,Adobe Reader 只是众多无法理解通常的滚轮信息的应用程序之一。

答案1

Adobe Reader 处理水平滚动的方式很奇怪。以下是我用来修复此问题的方法:

#IfWinActive, ahk_exe Acrobat.exe
F13::
  While GetKeyState("F13") {
    Send, {Shift down}{WheelUp}
    Sleep, 100
  }
  Send, {Shift up}
  Return

F14::
  While GetKeyState("F14") {
    Send, {Shift down}{WheelDown}
    Sleep, 100
  }
  Send, {Shift up}
  Return
#IfWinActive

注意:我将鼠标左键和右键的按键分配更改为 F 键,以便可以在 AutoHotKey 中拾取它们,因为我还使用 Logitech SetPoint

答案2

您可以单击(仅按下鼠标左键,不要松开)滚动条的开始处,即下图中红点所在的位置。然后将鼠标尽可能向下移动,即下图中绿点所在的位置。现在松开鼠标左键。滚动速度应该足够快。

在此处输入图片描述

以下是完整的 AutoHotkey 脚本代码:

CoordMode, Mouse, Screen
InitX := 
InitY :=
DestX := 
DestY :=
Click, Left, Down, %InitX%, %InitY%
Mousemove, %DestX%, %DestY%, 0
Click, Left, Up

变量 InitX 和 InitY 应保存坐标(分别为 x 和 y)初始点。上图中红点的坐标。

变量 DestX 和 DestY 应保存目标点的坐标(分别为 x 和 y)。上图中绿点的坐标。

已编辑

希望这能帮到你:http://ahkscript.org/boards/viewtopic.php?f=5&t=4028

从以下位置下载新版本的 AutoHotkeyhttp://ahkscript.org/(当前版本)。autohotkey.com 上的 AutoHotkey 已经过时了!

答案3

简短回答

对于 Adob​​e Reader X 中的水平滚动,请向滚动条的父级发送滚动消息,如sendscrolltoscrollbarparent代码中所示。许多其他方法都无法正常工作。这种方法不知何故提供了非常快的滚动速度,甚至比我原来的鼠标驱动程序还要好。

长答案

我找到了自己的答案,但忘记了这个问题。基本上,我对每一个疯狂的申请都使用了一种独特的方法。由于申请太多,我为所有申请都创建了一个单独的问题和答案(AutoHotkey 滚动、中键单击和鼠标加速),这里只给出与Adobe Reader相关的部分。

过程应该像这样。首先调用gettarget,它假设鼠标位置存储在 中mx,my,并根据鼠标下方的当前位置找到滚动事件的正确目标。然后scroll在添加要滚动到 的量后重复调用sx,sy

对于 Adob​​e Reader,即使是垂直滚动也依赖于将滚轮消息发送到正确的位置,这是不一致的,因此我最终对两种主要情况进行了硬编码,即滚动文档显示区域和滚动书签区域。为了找出是哪种情况,我检查鼠标下的控件的父级是否有一个名为的后代AVL_AVView4。如果有,那么这就是发送垂直滚轮消息的正确方法,由执行sendwheel。但对于水平滚动,事实证明,在两个地方向正确滚动条的父控件发送滚动消息都有效,由执行sendscrolltoscrollbarparent。正确的滚动条是scrollbar1鼠标下控件的父级的后代。

代码

#commentflag // ; Change to C++ comment style

global mx,my
global sx:=0
global sy:=0
global ctrl,window,parent
global methodx
global methody
global scrollbarx
global scrollbary

global max16bit:=32767

gettarget()
{
    ctrl:=getctrlat(mx,my)
    window:=getwindow(ctrl)
    class:=getclass(window)
    parent:=getparent(ctrl)
    parentname:=getnameatroot(parent)
    if( class=="AcrobatSDIWindow" )
    {
        if( regexmatch(parentname,"AVL_AVView")==1 )
        {
            ctrl:=getdescendant(parent,"AVL_AVView4")
            if( ctrl=="" )
            {
                ctrl:=getdescendant(parent,"AVL_AVView1")
            }
            methodx:="scrolltoscrollbarparent"
            scrollbarx:="scrollbar1"
            methody:="wheel"
        }
    }
}

scroll:
    critical on
    tx:=sx
    ty:=sy
    sx-=tx
    sy-=ty
    rx:=0
    ry:=0
    if( tx!=0 )
    {
        txi:=rtoz(tx)
        rx:=tx-txi
        if( txi!=0 )
        {
            if( methodx=="scrolltoscrollbarparent" )
            {
                sendscrolltoscrollbarparent(scrollbarx,"h",txi)
            }
        }
    }
    if( ty!=0 )
    {
        if( methody=="wheel" )
        {
            sendwheel("v",-ty)
        }
    }
    sx:=rx
    sy:=ry
return

sendwheel(dir,amount)
{
    t:=a_tickcount
    msg:=( dir=="v" ? 0x20a : 0x20e )
    flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2
    amount*=120
    while( amount>max16bit )
    {
        sendmessage msg,max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
        amount-=max16bit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
    while( amount<-max16bit )
    {
        sendmessage msg,-max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
        amount+=max16bit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
    sendmessage msg,round(amount)<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
}
sendscrolltoscrollbarparent(name,dir,amount)
{
    sb:=getdescendant(parent,name)
    sbp:=getparent(sb)
    t:=a_tickcount
    msg:=( dir=="v" ? 0x115 : 0x114 )
    flag:=( amount<0 ? 0 : 1 )
    loop % abs(amount)
    {
        sendmessage msg,flag,sb,,ahk_id %sbp%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}

rtoz(r)
{
    return ( r>0 ? floor(r) : ceil(r) )
}
getparent(handle)
{
    return dllcall("GetParent","uint",handle)
}
getname(root,handle)
{
    local CH,CN,S,P
    WinGet, CH, ControlListHwnd, ahk_id %root%
    WinGet, CN, ControlList, ahk_id %root%
    setformat integerfast,h
    handle+=0
    handle.=""
    setformat integerfast,d
    LF:= "`n",  CH:= LF CH LF, CN:= LF CN LF,  S:= SubStr( CH, 1, InStr( CH, LF handle LF ) )
    StringReplace, S, S,`n,`n, UseErrorLevel
    StringGetPos, P, CN, `n, L%ErrorLevel%
    Return SubStr( CN, P+2, InStr( CN, LF, 0, P+2 ) -P-2 )
}
getdescendant(handle,name)
{
    local CH,CN,S,P
    WinGet, CH, ControlListHwnd, ahk_id %handle%
    WinGet, CN, ControlList, ahk_id %handle%
    setformat integerfast,h
    handle+=0
    handle.=""
    setformat integerfast,d
    LF:= "`n",  CH:= LF CH LF, CN:= LF CN LF,  S:= SubStr( CN, 1, InStr( CN, LF name LF ) )
    StringReplace, S, S,`n,`n, UseErrorLevel
    StringGetPos, P, CH, `n, L%ErrorLevel%
    Return SubStr( CH, P+2, InStr( CH, LF, 0, P+2 ) -P-2 )*1
}
getnameatroot(handle)
{
    return getname(dllcall("GetAncestor","uint",handle,"uint",2),handle)
}
getnameaschild(handle)
{
    return getname(getparent(handle),handle)
}
getclass(handle)
{
    local class
    wingetclass class,ahk_id %handle%
    return class
}
getwindow(handle)
{
    return dllcall("GetAncestor","uint",handle,"uint",2)
}
getctrlat2(x,y,first,current)
{
    /*
        Pushes the following invisible container controls to the back because they are in front of their contents for no reason
            SysTabControl32 : The usual class that contains tabbed panes ( Mouse properties , ... )
            Static : A class occasionally used to contain tabbed panes ( Programmer's Notepad Options > Fonts and Colours > Advanced , ... )
            Button : A typical class used to contain a List Box ( Outlook Contact > Properties > General > Members , ... )
        Executes WindowFromPoint again to access the contents of such container controls
    */
    local handle,class,style
    class:=getclass(current)
    winget style,style,ahk_id %current%
    if( class=="SysTabControl32" or class=="Static" or ( class=="Button" and (style&0x7)==0x7 ) )
    {
        dllcall("SetWindowPos","uint",current,"uint",1,"int",0,"int",0,"int",0,"int",0,"uint",0x3)  // push it to the back where it belongs
        handle:=dllcall("WindowFromPoint","int",x,"int",y)
        //handle:=DllCall( "WindowFromPoint", "int64", (my << 32) | (mx & 0xFFFFFFFF), "Ptr") // for negative 64-bit
        if( handle==first )
        {
            return first
        }
        return getctrlat2(x,y,first,handle)
    }
    return current
}
getctrlat(x,y)
{
    local handle
    handle:=dllcall("WindowFromPoint","int",x,"int",y)
    //handle:=DllCall( "WindowFromPoint", "int64", (my << 32) | (mx & 0xFFFFFFFF), "Ptr") // for negative 64-bit
    return getctrlat2(x,y,handle,handle)
}

答案4

我的解决方案:

#IfWinActive, ahk_exe Acrobat.exe
WheelLeft::
    Send,{shift down}
    sleep,20
    Send,{WheelUp}
    sleep,20
    Send,{shift up}
    return
WheelRight::
    Send,{shift down}
    sleep,20
    Send,{WheelDown}
    sleep,20
    Send,{shift up}
    return
#IfWinActive

你可以增加数字 20 来减慢滚动速度

相关内容