AutoHotkey 滚动、中键单击和鼠标加速

AutoHotkey 滚动、中键单击和鼠标加速

问题

假设有一台 Windows 7 机器,尤其是一台笔记本电脑。

  1. 没有鼠标驱动程序允许按住鼠标右键并移动鼠标进行滚动,同时保留鼠标右键功能。如果您没有中键,这很有用。
  2. 鼠标驱动程序对于某些应用程序不起作用,而像 UltraNav 这样的一些驱动程序对于大多数可以工作的程序来说感觉很不稳定。
  3. ThinkPad 的 UltraNav 驱动程序不允许中间按钮发挥中键单击的功能,同时还不允许通过按住中间按钮并移动鼠标来进行滚动。
  4. UltraNav 驱动程序也不允许在滚动的同时进行单击和拖动,这在编辑文档时非常有用。
  5. 滚轮鼠标无法实现加速滚动,而且 UltraNav 驱动程序的实现效果很差。
  6. 使用默认鼠标驱动程序,最快的指针速度可能仍然太慢且令人疲劳,特别是对于 ThinkPad 指点杆而言。

我想要一个 AutoHotkey 脚本来解决所有这些问题,但我在网上找到的任何东西都无法解决其中的一些问题。经过两年的时间,我开发了一个完整的解决方案。当然,我使用的一些技术是从互联网上的各种帖子改编而来的,但通常每篇帖子只有一两种方法,因此它们不适用于许多应用程序。

欢迎评论!不过,请理解,如果它不适合您,我可能没有时间实施建议或排除故障。谢谢!

答案1

我设计了自己的 AutoHotkey 脚本,使用低级鼠标钩子来解决上述所有问题。代码太长,请参阅其他答案以获取代码。

重要说明

即使您使用的是 64 位 Windows,也请使用 32 位版本的 AutoHotkey,否则它在某些地方将无法工作!使用任务计划程序以用户的最高权限登录时运行脚本,否则它将无法在以完全管理员权限运行的应用程序中为管理员用户工作。如果您安装了 UltraNav,请为鼠标中键选择“平滑”,否则它会因某些未知原因干扰脚本,并且滚动将不起作用。

鼠标钩子偶尔会出现问题,特别是当您尝试在应用程序更改其 GUI 时滚动时。我怀疑这是因为我没有以实时优先级运行它,但我认为这太冒险了,因为如果它卡住了,那么可能就没有办法阻止它。目前它设置为以高优先级运行。如果出现任何问题,请按顺序尝试以下热键。

LCtrl-RCtrl:重新加载。 RCtrl-LCtrl:禁用鼠标挂钩。(如果您的鼠标现在可以工作,请重新加载。) RCtrl-Esc:禁用整个脚本。(您需要再次运行它。)

特征

  1. 默认情况下,按住鼠标中键并移动鼠标时会执行滚动。您可以设置rightbuttonscroll:=1为使用鼠标右键(尤其是对于没有鼠标中键的笔记本电脑),在这种情况下,要获得原始的右键单击拖动功能,可能必须在此之前再按一次 Ctrl/Shift/Alt,具体取决于特定的应用程序。(在大多数应用程序中,这些组合中的至少一个与原始的右键单击相同。)
  2. 如果没有鼠标中键,也可以设置leftrighttomiddle:=1,然后在clicklimit(毫秒) 内同时按下鼠标左键和右键将产生按下中键的效果,而同时松开鼠标左键和右键将产生释放中键的效果。要保持生成的中键按下,只需保持两个真实按钮中的一个按下即可。
  3. 滚动功能独立于其他鼠标按钮,因此,例如,只要应用程序支持,就可以在选择操作期间按住鼠标左键随时滚动。(出于某些奇怪的原因,某些应用程序不允许在选择时滚动。)
  4. 默认情况下,在 内按下并释放滚动按钮(无论是鼠标中键还是鼠标右键)后clicklimit,将产生原始滚动按钮点击。如果滚动按钮按住的时间超过 ,则启用滚动,释放滚动按钮将不会产生任何点击。要启用即使在初始延迟期间的滚动,请设置scrollbeforeclick:=1。使用此设置,即使已经进行了轻微滚动,只要不超过 ,也可以进行点击scrolllimit
  5. 滚动略有粘滞,这意味着如果在滚动过程中意外释放滚动按钮的时间少于resetdelay,则将被视为未发生该操作,滚动将继续。具体而言,这意味着在滚动结束时意外点击不会产生原始滚动按钮点击。
  6. 点击也有点粘滞,这意味着如果resetdelay在释放鼠标按钮后按下鼠标按钮,则假定
  7. 有些应用程序在处理滚动时非常迟缓,因此我们无法向它们发送太多命令来执行滚动。interval是发送给应用程序的滚动相关命令批次之间的间隔。在每个间隔中,用户的所有滚动操作都合并为一个。减少间隔可使滚动对用户更具响应性,但可能会使设计不良的应用程序受阻。如果需要精细控制,可以始终在开始时设置默认值,gettarget并根据应用程序进行调整。
  8. 某些应用程序确实需要花费太长时间来处理滚动命令,因此如果某个命令未在 内得到处理timelimit,则该间隔内批次中所有剩余的命令都将被中止。
  9. 可以通过编辑来微调滚动scrollamount,根据鼠标速度更改滚动速度,或scrolladjust根据累积滚动量更改滚动方式。默认情况下,如果某个方向的滚动量是另一个方向的 5 倍以上,则滚动会捕捉到垂直或水平方向。
  10. 您还可以微调鼠标速度和加速度。LCtrl-Ralt-D按 显示当前鼠标速度,LCtrl-Ralt-S按 降低速度,LCtrl-Ralt-F按 增加速度。它将更改鼠标设置中的指针速度。编辑moveadjust以更改指针移动转换为鼠标移动的方式。默认情况下,低速保持不变,高速乘以 3,两者之间有平滑的过渡,而且每个鼠标移动都分布在当前移动和下一个移动中,因为在高指针速度下,硬件或默认鼠标驱动程序会产生不稳定的输出。确保moveadjust运行速度快!

方法

我发现各种应用程序中有 11 种不同的滚动方法。有些应用程序只响应其中一种,这很烦人。这些方法在代码本身的注释中进行了描述,但没有可靠的方法来确定要使用的正确方法,因此必须手动自定义gettarget以选择每个不适用于默认滚轮消息的应用程序的方法。按照它们出现的顺序尝试这些方法scroll。对于每种方法,您可能必须尝试根据需要更改消息的目标ctrl,window,parent

要切换调试,请按LCtrl-AppMenu。它显示鼠标的原始移动和调整量,包括正常移动和滚动。如果您按住滚动按钮而不移动鼠标,它会显示鼠标下的目标和 选择的控件的祖先gettarget。我有时还必须使用 Spy++ 来查看控件层次结构并猜测将消息发送到哪里。

评论

64 位 AutoHotkey 导致我的脚本无法在 Programmer's Notepad 上运行,而 32 位 AutoHotkey 却运行正常,我不知道为什么。其他应用程序似乎没有这样的问题。如果这是我的脚本中的错误,我会很高兴知道!

答案2

代码太长,因此分为两个答案,请参阅第三个答案以获取说明。

代码第 1 部分

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

// Settings //

global rightbuttonscroll:=0 // set to 1 iff the right button is to be used for scrolling instead of the middle button
global scrollbeforeclick:=1 // set to 1 iff scrolling is allowed before the original button click otherwise scrolling is delayed for (clicklimit)
global leftrighttomiddle:=0 // set to 1 iff pressing both left+right buttons together is to be used to generate the middle button press
global scrolllimit:=3.5     // maximum scroll allowed between scroll button press and release for the original button click to be generated
global clicklimit:=210      // maximum time allowed between scroll button press and release for the original button click to be generated
global resetdelay:=210      // minimum time required between scroll button release and press for the original button click to be allowed
global interval:=35         // minimum interval between scrolling updates
global timelimit:=70        // maximum time allowed for continuing the scrolling update in each direction
global dbg:=false

// Initialization //

#singleinstance,force
#keyhistory 0
#usehook on
process priority,,H
setkeydelay 1
setcontroldelay -1
coordmode mouse,screen

global buttondown:=( rightbuttonscroll==1 ? 0x204 : 0x207 )
global buttonup:=( rightbuttonscroll==1 ? 0x205 : 0x208 )
global buttonoriginal:=( rightbuttonscroll==1 ? "RButton" : "MButton" )

global speed
global handling:=0
global scrolling:=0
global scrolldrag:=0
global scrollsticky:=0
global clicksticky:=0
global leftphysical:=0
global rightphysical:=0
global lefttopress:=0
global righttopress:=0
global middlepressed:=0

global mx,my
global dx,dy
global lx:=0
global ly:=0
global sx:=0
global sy:=0
global totalx:=0
global totaly:=0
global ctrl,window,parent
global methodx
global methody
global scrollbarx
global scrollbary

global max16bit:=32767
global sbinfo
varsetcapacity(sbinfo,28)
numput(28,sbinfo,0)
numput(23,sbinfo,4)

global mousehook:=dllcall("SetWindowsHookEx","int",14,"uint",RegisterCallback("handlemouse","fast"),"uint",0,"uint",0)

message("AutoHotkey loaded")
return

// Reload & Debug & Exit & Disable Mousehook //

LCtrl & RCtrl::reload
LCtrl & AppsKey::dbg:=!dbg
RCtrl & Esc::exitapp
RCtrl & LCtrl::dllcall("UnhookWindowsHookEx","uint",mousehook)

// Message //

messageoff:
    tooltip
return

message(text)
{
    tooltip % text  //,a_screenwidth-strlen(text)*7,a_screenheight-42
    settimer messageoff,-1000
}

// Mouse Movement //

moveadjust(byref x,byref y)
{
    movespread(x,y)
    z2:=x**2+y**2
    r:=(70+z2*4)/(70+z2)
    x:=rtoz(x*r)
    y:=rtoz(y*r)
}
movespread(byref x,byref y)
{
    nx:=rtoz(x/2)
    ny:=rtoz(y/2)
    x-=nx
    y-=ny
    x+=lx
    y+=ly
    lx:=nx
    ly:=ny
    settimer movereset,-210
}
movereset:
    lx:=0
    ly:=0
return

getspeed()
{
    DllCall("SystemParametersInfo","Int",112,"Int",0,"UIntP",speed,"Int",0)
}
setspeed()
{
    DllCall("SystemParametersInfo","Int",113,"Int",0,"UInt",speed,"Int",2)
}
showspeed()
{
    message("Mouse speed = " . speed)
}

<^>!d::
    getspeed()
    showspeed()
return
<^>!s::
    getspeed()
    if( speed>1 )
    {
        speed:=speed-1
    }
    setspeed()
    showspeed()
return
<^>!f::
    getspeed()
    if( speed<20 )
    {
        speed:=speed+1
    }
    setspeed()
    showspeed()
return

// Mouse Scrolling //

/*
    Usage
        Combination : Function
            ScrollButton-Release : ScrollButton-Click  (where the intervening time and scroll do not exceed the limits set)
            ScrollButton-Modifiers-Move : Modifiers-Scroll  (where Modifiers can be any combination of Shift and Ctrl and Alt)
        If rightbuttonscroll = 1 :
            LButton-RButton : MButton  (where the intervening time does not exceed the limit set)
            RButton-LButton : MButton  (where the intervening time does not exceed the limit set)
    Methods
        Send wheel messages (0x20a/0x20e) to the control
            The wheel amount is only 16-bit which is at most only 32767/120 wheel notches
            Some applications do not handle the wheel amount correctly so you may need to send integer multiples of wheel notches or many wheel messages
            Microsoft Word apparently assumes that wheel messages are always posted and behaves incorrectly unless PostMessage is used
        Send scrollbars' thumb position to the control
            This works for many scrollable controls but not all
        Send scroll messages (0x115/0x114) to the control unless its scrollbar thumb position is already at the end
            Many applications respond rather slowly to scroll messages
        Send scroll messages to the scrollbars
            This works for some applications and should be slightly faster than scroll messages to the parent
            Many applications respond rather slowly so wheel messages are preferable if they work
        Send scroll messages to the scrollbars' parents
            This is supposed to be a standard way to control scrollbars that are separate from the control
            Many applications respond rather slowly so wheel messages are preferable if they work
        Send keys (Up/Down/Left/Right/PgUp/PgDn) to the scrollbars
            This is equivalent to clicking the arrows or the empty space on either side of the thumb track
            Some applications respond slowly so keys should not be sent too fast
        Send keys to the control
            Only for rare cases that do not respond to anything else
        Call Office function SmallScroll
            This function only exists for Office applications and even then it is broken in some like Powerpoint
    Customization
        scrollamount(x)
            Can assume that x is non-negative
            Must return non-negative output
        scrolladjust(x,y)
            Can modify scroll amounts (x,y)
        gettarget()
            Can assume that (mx,my) is the current mouse position
            Must set ctrl to the handle of the control that is to be scrolled
            Must set parent to getparent(ctrl) if scrolltoscrollbarparent() or keystoscrollbar() is used
*/

scrollamount(x)
{
    return x**1.5/8
}

scrolladjust(byref x,byref y)
{
    ax:=abs(x)
    ay:=abs(y)
    z:=sqrt(x**2+y**2)
    if( ax>ay*5 )
    {
        x:=( x>0 ? z : -z )
        y:=0
    }
    if( ay>ax*5 )
    {
        y:=( y>0 ? z : -z )
        x:=0
    }
}

gettarget()
{
    gosub messageoff
    ctrl0:=getctrlat(mx,my)
    window:=getwindow(ctrl0)
    class:=getclass(window)
    title:=gettitle(window)
    ctrl:=ctrl0
    loop
    {
        ctrlname:=getnameaschild(ctrl)
        ctrlclass:=getclass(ctrl)
        parent:=getparent(ctrl)
        parentname:=getnameatroot(parent)
        parentclass:=getclass(parent)
        if( ctrl!=window and ( regexmatch(ctrlclass,"^(Button|T?ComboBox|CtrlNotifySink|SysLink)$")==1 or parentclass=="ComboBox" ) )
        {
            ctrl:=parent
            continue
        }
        break
    }
    gp:=getparent(parent)
    ggp:=getparent(gp)
    gpname:=getnameaschild(gp)
    ggpname:=getnameaschild(ggp)
    methodx:="wheel"        // needed for: Firefox , Gimp , ...
    methody:="wheel"        // needed for: Firefox , File Chooser , Explorer , Word , Outlook , IE , ...
    scrollbarx:=""
    scrollbary:=""
    if( ctrlclass=="ComboLBox" )            // Standard Combo Boxes dropdown list
    {
        methodx:="thumbpos"
        methody:="thumbpos"
    }
    if( ctrlclass=="ListBox" )          // Standard List Boxes
    {
        methodx:="thumbpos"
        methody:="thumbpos"
    }
    if( ctrlclass=="ScrollBar" )            // Standard ScrollBar controls
    {
        methodx:="scrolltoscrollbarparent"  // "scroll" works for most places but not some ( Character Map , ... )
        methody:="scrolltoscrollbarparent"  // "scroll" works for most places but not some ( Character Map , ... )
        scrollbarx:="scrollbar1"
        scrollbary:="scrollbar1"
    }
    if( class=="OpusApp" )          // Microsoft Word
    {
        methodx:="office"
        methody:="postwheel"
        if( gpname=="_WwB1" )
        {
            ctrl:=getdescendant(window,"_WwG1")
        }
    }
    if( class=="XLMAIN" )           // Microsoft Excel
    {
        methodx:="office"
        if( gpname=="EXCEL71" )
        {
            ctrl:=gp
        }
    }
    if( class=="PPTFrameClass" or class=="PP12FrameClass" )         // Microsoft Powerpoint
    {
        methody:="wheelsingle"
        ctrlnameatroot:=getnameatroot(ctrl)
        if( ctrlnameatroot=="NetUIHWND3" or ctrlnameatroot=="NetUIHWND4" )
        {
            ctrl:=getdescendant(window,"paneClassDC1")
            ctrlname:="paneClassDC1"
            parent:=getparent(ctrl)
        }
        if( ctrlname=="paneClassDC1" )
        {
            methodx:="scrolltoscrollbarparent"
            methody:="scrolltoscrollbarparent"  // Powerpoint scroll up is broken when there are 9 slides at 100% zoom in normal mode
            scrollbarx:="NUIScrollbar2"
            scrollbary:="NUIScrollbar1"
            //methody:="office" // Powerpoint does not update the view pane immediately and so it is disorienting
        }
        if( ctrlnameatroot=="NetUIHWND5" )
        {
            ctrl:=getdescendant(window,"paneClassDC2")
        }
    }
    if( class=="rctrl_renwnd32" )           // Microsoft Outlook
    {
        methodx:="office"
        gpclass:=getclass(gp)
        ctrlnameatroot:=getnameatroot(ctrl)
        if( ctrlclass=="SUPERGRID" )
        {
            methodx:="scroll"
            methody:="scroll"
        }
        if( gpclass=="SUPERGRID" )
        {
            methodx:="scroll"
            methody:="scroll"
            ctrl:=gp
        }
        if( gpname=="_WwB1" )
        {
            ctrl:=getdescendant(window,"_WwG1")
        }
    }
    if( ctrlclass=="OUTEXVLB" )         // Microsoft Outlook { Address Book , Group membership , ... }
    {
        methodx:="thumbpos"
        if( regexmatch(title,"Global Address List")==0 )
        {
            methody:="scroll"
        }
    }
    if( title=="Symbol" and regexmatch(class,"bosa_sdm_(msword|Microsoft Office Word 12.0|XL9|Mso96)")==1 )         // Microsoft Office Insert Symbol
    {
        controlget v,visible,,ScrollBar1,ahk_id %ctrl%
        if( v==0 )
        {
            ctrl:=getdescendant(window,"Edit1")
            methody:="thumbpos"
        }
    }
    if( class=="wndclass_desked_gsk" )          // Microsoft Visual Basic
    {
        if( ctrlclass=="VbaWindow" )
        {
            methodx:="scrolltoscrollbarparent"
            parent:=ctrl
            scrollbarx:="scrollbar2"
        }
    }
    if( regexmatch(ctrlclass,"^RichEdit20W(PT)?$")==1 )         // Windows Text Areas
    {
        methodx:="wheel"
        methody:="scroll"
    }
    if( class=="AcrobatSDIWindow" )         // Adobe Reader
    {
        if( regexmatch(parentname,"AVL_AVView")==1 )
        {
            ctrl:=getdescendant(parent,"AVL_AVView4")
            if( ctrl=="" )
            {
                ctrl:=getdescendant(parent,"AVL_AVView1")
            }
            methodx:="scrolltoscrollbarparent"
            scrollbarx:="scrollbar1"
            methody:="wheel"
        }
    }
    if( ggpname=="SHELLDLL_DefView1" )          // Windows Explorer Scrollbars
    {
        ctrl:=gp
        ctrlname:=gpname
        parentname:=ggpname
    }
    if( ctrlname=="DirectUIHWND1" )
    {
        if( parentname=="SHELLDLL_DefView1" )           // Windows Explorer (including Standard File Choosers)
        {
            methodx:="scrolltoscrollbar"    // "scrolltoscrollbarparent" also works
            scrollbarx:="scrollbar1"
            controlget v,visible,,ScrollBar2,ahk_id %ctrl%
            methody:=( v==1 ? "wheel" : "" )
        }
        if( class=="CabinetWClass" )
        {
            if( parentname=="XBabyHost1" )          // Control Panel
            {
                methody:="scrolltoscrollbar"    // "scrolltoscrollbarparent" also works
                scrollbary:="scrollbar1"
                if( title=="Personalization" )
                {
                    scrollbary:="scrollbar3"
                }
            }
        }
    }
    if( ctrlclass=="CharGridWClass" )           // Character Map
    {
        methody:="scrolltoscrollbarparent"
        scrollbary:="scrollbar1"
    }
    if( class=="ConsoleWindowClass" )           // Console Window
    {
        methodx:="thumbpos"
        methody:="thumbpos"
    }
    if( class=="WordPadClass" )         // WordPad
    {
        methodx:="scroll"
        methody:="scroll"   // WordPad scroll down is broken when scrolling horizontally at the same time
    }
    if( class=="ATL:006AD5B8" )         // Programmer's Notepad
    {
        methodx:="scroll"
    }
    if( class=="SWT_Window0" or ctrlclass=="Internet Explorer_Server" )         // Eclipse
    {
        methodx:="scroll"
    }
    if( ctrlclass=="TSynEdit" )         // TSynEdit ; Dev C++ , ...
    {
        methodx:="thumbpos"
        methody:="thumbpos"
    }
    if( ctrlclass=="TListView" )            // TlistView ; Dev C++ , ...
    {
        methodx:="scroll"
    }
    if( ctrlclass=="TPSSynEdit" )           // TPSSynEdit ; PSPad , ...
    {
        methodx:="thumbpos"
    }
    if( class=="QWidget" or class=="Qt5QWindowIcon" )
    {
        if( regexmatch(title,"LyX")==1 )            // Lyx
        {
            methodx:=""
                // prevents horizontal scrolling from becoming vertical scrolling in the edit pane
                // but disables horizontal scrolling everywhere else
        }
        if( regexmatch(title,"TeXworks$")>0 )           // TeXWorks
        {
            methodx:="wheelint"
            methody:="wheelint"
        }
    }
    if( class=="gdkWindowToplevel" )            // Gimp
    {
        methody:="wheelsingle"  // Gimp performs horizontal scrolling when the mouse is scrolled over the horizontal scrollbar
    }
    if( class=="SunAwtDialog" )         // Java AWT Dialogs ; GeoGebra , Logisim , ...
    {
        methody:="wheelint"
    }
    if( regexmatch(ctrlname,"IupCanvas")==1 )           // IupCanvas
    {
        methodx:="scroll"
    }
    if( class=="SunAwtFrame" )
    {
        if( regexmatch(title,"GeoGebra|.*\.ggb$")==1 )          // GeoGebra
        {
            methody:="wheelint"
        }
        if( regexmatch(title,"Logisim")==1 )            // Logisim
        {
            methodx:="keys" // performs scrolling only if the drawing area has the focus
        }
    }
    if( class=="MSPaintApp" )           // MSPaint
    {
        if( parentname=="MSPaintView1" )
        {
            methodx:="thumbpos"
            methody:="thumbpos"
        }
    }
    if( class=="ATL:643E3490" )         // Real World Paint
    {
        if( ctrlclass=="RWViewImageEdit" )
        {
            methodx:="scroll"
            methody:="scroll"
        }
    }
    if( ctrlclass=="DSUI:PagesView" )           // PDF-XChange Viewer (also as a browser plugin)
    {
        methodx:="scroll"
        methody:="wheelint"
    }
    if( ctrlclass=="PuTTY" )            // PuTTY
    {
        methody:="scroll"
    }
if( dbg )
{
p:=getparent(ctrl)
gp:=getparent(p)
ggp:=getparent(gp)
message( "Root class = " class 
    . "`nRoot title = " title 
    . "`nTarget = [" ctrl0 "]" 
        . "`n`t(as child) " getnameaschild(ctrl0) 
        . "`n`t(at root)  " getnameatroot(ctrl0) 
    . "`nControl = [" ctrl "]" 
        . "`n`t(at child) " getnameaschild(ctrl) 
        . "`n`t(as root)  " getnameatroot(ctrl) 
    . "`nControl ancestors = " 
        . "`n`t < [" p "] " getnameatroot(p) 
        . "`n`t < [" gp "] " getnameatroot(gp) 
        . "`n`t < [" ggp "] " getnameatroot(ggp) 
    . "`nMethod = (" methodx "," methody ")" 
    . "`nScrollbars = (" scrollbarx "," scrollbary ")" )
}
}

scroll:
    critical on
    if( getwindow(getctrlat(mx,my))!=window )
    {
        scrolling:=0
    }
    if( scrolling==0 )
    {
        return
    }
    settimer scroll,-%interval%
    if( scrollbeforeclick!=1 and scrolldrag==0 )
    {
        sx:=0
        sy:=0
        return
    }
    tx:=sx
    ty:=sy
    sx-=tx
    sy-=ty
    totalx+=tx
    totaly+=ty
    if( totalx**2+totaly**2>scrolllimit )
    {
        scrolldrag:=1
    }
    scrolladjust(tx,ty)
    rx:=0
    ry:=0
    comobjerror(false)
    if( tx!=0 )
    {
        if( methodx=="wheel" )
        {
            sendwheel("h",tx)
        }
        else if( methodx=="postwheel" )
        {
            postwheel("h",tx)
        }
        else
        {
            txi:=rtoz(tx)
            rx:=tx-txi
            if( txi!=0 )
            {
                if( methodx=="wheelint" )
                {
                    sendwheel("h",txi)
                }
                else if( methodx=="wheelsingle" )
                {
                    sendwheelsingle("h",txi)
                }
                else if( methodx=="thumbpos" )
                {
                    sendthumbpos("h",txi)
                }
                else if( methodx=="scroll" )
                {
                    sendscroll("h",txi)
                }
                else if( methodx=="scrolltoscrollbar" )
                {
                    sendscrolltoscrollbar(scrollbarx,txi)
                }
                else if( methodx=="scrolltoscrollbarparent" )
                {
                    sendscrolltoscrollbarparent(scrollbarx,"h",txi)
                }
                else if( methodx=="keys" )
                {
                    sendkeys("h",txi)
                }
                else if( methodx=="keystoscrollbar" )
                {
                    sendkeystoscrollbar(scrollbarx,txi)
                }
                else if( methodx=="office" )
                {
                    Acc_ObjectFromWindow(ctrl,-16).SmallScroll(0,0,(txi>0?txi:0),(txi<0?-txi:0))
                }
            }
        }
    }
    if( ty!=0 )
    {
        if( methody=="wheel" )
        {
            sendwheel("v",-ty)
        }
        else if( methody=="postwheel" )
        {
            postwheel("v",-ty)
        }
        else
        {
            tyi:=rtoz(ty)
            ry:=ty-tyi
            if( tyi!=0 )
            {
                if( methody=="wheelint" )
                {
                    sendwheel("v",-tyi)
                }
                else if( methody=="wheelsingle" )
                {
                    sendwheelsingle("v",-tyi)
                }
                else if( methody=="thumbpos" )
                {
                    sendthumbpos("v",tyi)
                }
                else if( methody=="scroll" )
                {
                    sendscroll("v",tyi)
                }
                else if( methody=="scrolltoscrollbar" )
                {
                    sendscrolltoscrollbar(scrollbary,tyi)
                }
                else if( methody=="scrolltoscrollbarparent" )
                {
                    sendscrolltoscrollbarparent(scrollbary,"v",tyi)
                }
                else if( methody=="keys" )
                {
                    sendkeys("v",tyi)
                }
                else if( methody=="keystoscrollbar" )
                {
                    sendkeystoscrollbar(scrollbary,tyi)
                }
                else if( methody=="office" )
                {
                    Acc_ObjectFromWindow(ctrl,-16).SmallScroll((tyi>0?tyi:0),(tyi<0?-tyi:0))
                }
            }
        }
    }
    comobjerror(true)
    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
}
postwheel(dir,amount)
{
    msg:=( dir=="v" ? 0x20a : 0x20e )
    flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2
    amount*=120
    while( amount>max16bit )
    {
        postmessage msg,max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%
        amount-=max16bit
    }
    while( amount<-max16bit )
    {
        postmessage msg,-max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%
        amount+=max16bit
    }
    postmessage msg,round(amount)<<16|flags,mx|my<<16,,ahk_id %ctrl%
}
sendwheelsingle(dir,amount)
{
    t:=a_tickcount
    msg:=( dir=="v" ? 0x20a : 0x20e )
    flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2
    loop % abs(amount)
    {
        sendmessage msg,(amount<0?-120:120)<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
sendthumbpos(dir,amount)
{
    msg:=( dir=="v" ? 0x115 : 0x114 )
    sb:=dllcall("GetScrollInfo","uint",ctrl,"int",(dir=="v"?1:0),"uint",&sbinfo)
    if( sb )
    {
        sbmin:=numget(sbinfo,8,"int")
        sbmax:=numget(sbinfo,12,"int")
        sbpos:=numget(sbinfo,20,"int")
        if( amount>max16bit )
        {
            amount=max16bit
        }
        if( amount<-max16bit )
        {
            amount=-max16bit
        }
        pos:=sbpos+amount
        if( pos<sbmin )
        {
            pos:=sbmin
        }
        if( pos>sbmax )
        {
            pos:=sbmax
        }
        sendmessage msg,pos<<16|4,,,ahk_id %ctrl%,,,,timelimit
    }
}
sendscroll(dir,amount)
{
    t:=a_tickcount
    msg:=( dir=="v" ? 0x115 : 0x114 )
    flag:=( amount<0 ? 0 : 1 )
    loop % abs(amount)
    {
        sb:=dllcall("GetScrollInfo","uint",ctrl,"int",(dir=="v"?1:0),"uint",&sbinfo)
        if( sb )
        {
            sbmin:=numget(sbinfo,8,"int")
            sbmax:=numget(sbinfo,12,"int")
            sbpage:=numget(sbinfo,16,"uint")
            sbpos:=numget(sbinfo,20,"int")
            if( ( sbpos==sbmin and amount<0 ) or ( sbpos+sbpage==sbmax+1 and amount>0 ) )
            {
                return
            }
        }
        sendmessage msg,flag,,,ahk_id %ctrl%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
sendscrolltoscrollbar(name,amount)
{
    t:=a_tickcount
    flag:=( amount<0 ? 0 : 1 )
    loop % abs(amount)
    {
        sendmessage 0x115,flag,,%name%,ahk_id %ctrl%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
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
        }
    }
}
sendkeys(dir,amount)
{
    t:=a_tickcount
    key:=( dir=="v" ? ( amount<0 ? "{Up}" : "{Down}" ) : ( amount<0 ? "{Left}" : "{Right}" ) )
    loop % abs(amount)
    {
        controlsend, ,%key%,ahk_id %ctrl%
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
sendkeystoscrollbar(name,amount)
{
    t:=a_tickcount
    key:=( amount<0 ? "{Up}" : "{Down}" )
    controlget e,enabled,,%name%,ahk_id %parent%
    if( e==1 )
    {
        loop % abs(amount)
        {
            controlsend %name%,%key%,ahk_id %parent%
            if( a_tickcount-t>=timelimit )
            {
                break
            }
        }
    }
}

答案3

代码太长,因此分为两个答案,请参阅第三个答案以获取说明。

代码第 2 部分

scrollbuttonreset:
    scrollsticky:=0
return

scrollbuttoncannotclick:
    scrolldrag:=1
    righttopress:=0
return

clickreset:
    clicksticky:=0
return

scrollbuttondown:
    critical on
    mousegetpos mx,my
    gettarget()
    sx:=0
    sy:=0
    totalx:=0
    totaly:=0
    settimer scroll,-%interval%
    settimer scrollbuttonreset,off
return

scrollbuttonup:
    critical on
    if( scrolldrag==0 and scrollsticky==0 )
    {
        handling++
        sendevent {Blind}{%buttonoriginal% down}
        sendevent {Blind}{%buttonoriginal% up}
        handling--
        scrolling:=0
    }
    scrolldrag:=0
    righttopress:=0
    settimer scrollbuttondown,off
    settimer scrollbuttoncannotclick,off
    scrollsticky:=1
    settimer scrollbuttonreset,-%resetdelay%
return

leftdown:
    critical on
    lefttopress:=0
    handling++
    sendevent {Blind}{LButton down}
    handling--
return

rightdown:
    critical on
    righttopress:=0
    handling++
    sendevent {Blind}{RButton down}
    handling--
return

middledown:
    critical on
    scrolling:=0
    scrolldrag:=0
    middlepressed:=1
    settimer scrollbuttondown,off
    settimer scrollbuttoncannotclick,off
    handling++
    sendevent {Blind}{MButton down}
    handling--
return

leftup:
    critical on
    handling++
    sendevent {Blind}{LButton up}
    handling--
    clicksticky:=1
    settimer clickreset,-%resetdelay%
return

rightup:
    critical on
    handling++
    sendevent {Blind}{RButton up}
    handling--
    clicksticky:=1
    settimer clickreset,-%resetdelay%
return

middleup:
    critical on
    middlepressed:=0
    handling++
    sendevent {Blind}{MButton up}
    handling--
    clicksticky:=1
    settimer clickreset,-%resetdelay%
return

leftclick:
    critical on
    gosub leftdown
    gosub leftup
return

rightclick:
    critical on
    gosub rightdown
    gosub rightup
return

// Mouse handler //

handlemouse(nCode,wParam,lParam)
{
    critical on
    if( a_ispaused==1 )
    {
        exitapp // something goes wrong if it remains paused
    }
    o:=0
    if( handling==0 && nCode>=0 )
    {
        if( wParam==0x201 )
        {
            leftphysical:=1
        }
        else if( wParam==0x204 )
        {
            rightphysical:=1
        }
        else if( wParam==0x202 )
        {
            leftphysical:=0
        }
        else if( wParam==0x205 )
        {
            rightphysical:=0
        }
        if( wParam==0x200 )
        {
            // Handle mouse move //
            mousegetpos mx,my
            x:=numget(lParam+0,0,"int") // the "+0" is necessary!
            y:=numget(lParam+0,4,"int") // the "+0" is necessary!
            dx:=x-mx
            dy:=y-my
            if( scrolling==0 )
            {
                // Click immediately on mouse drag //
                if( leftrighttomiddle==1 )
                {
                    if( lefttopress==1 )
                    {
                        settimer leftdown,-0
                    }
                    if( righttopress==1 )
                    {
                        settimer rightdown,-0
                    }
                }
                // Adjust mouse movement //
                moveadjust(dx,dy)
                handling++
                mousemove dx,dy,0,R
                handling--
if( dbg )
{
message( "Origin = " mx " " my "`nMove = " x-mx " " y-my "`n         -> " dx " " dy )
}
                // Release mouse buttons if out of sync with physical state //
                if( getkeystate("LButton")>leftphysical )
                {
                    settimer leftup,-0
                }
                if( getkeystate("RButton")>rightphysical )
                {
                    settimer rightup,-0
                }
                o:=1
            }
            else
            {
                // Handle mouse move when scrolling //
                if( dx!=0 or dy!=0 )
                {
                    sx+=( dx>0 ? scrollamount(dx) : -scrollamount(-dx) )
                    sy+=( dy>0 ? scrollamount(dy) : -scrollamount(-dy) )
if( dbg )
{
message( "Origin = " mx " " my "`nMove = " dx " " dy "`n         -> " round(sx,2) " " round(sy,2) )
}
                }
                o:=1
            }
        }
        else if( scrolling==0 )
        {
            if( wParam==buttondown and middlepressed==0 and lefttopress==0 )
            {
                // Handle scroll button down //
                if( getkeystate("Ctrl")==0 and getkeystate("Shift")==0 and getkeystate("Alt")==0 )
                {
                    // Handle scroll start //
                    scrolling:=1
                    scrolldrag:=0
                    if( buttondown==0x204 )
                    {
                        righttopress:=1
                    }
                    settimer scrollbuttondown,-0
                    settimer scrollbuttoncannotclick,-%clicklimit%
                    o:=1
                }
            }
        }
        else if( scrolling==1 )
        {
            if( wParam==buttonup )
            {
                // Handle scroll button up //
                scrolling:=0
                settimer scrollbuttonup,-0
                o:=1
            }
        }
        if( leftrighttomiddle==1 and o==0 )
        {
            if( middlepressed==1 )
            {
                // Release middle button only when both left and right buttons are released //
                if( leftphysical==0 and rightphysical==0 )
                {
                    settimer middleup,-0
                }
                o:=1
            }
            else if( scrolldrag==0 )
            {
                // Process left+right=middle //
                if( wParam==0x201 and scrollsticky==0 )
                {
                    // Handle left button down //
                    if( righttopress==0 )
                    {
                        if( clicksticky==1 )
                        {
                            settimer leftdown,-0
                        }
                        else
                        {
                            lefttopress:=1
                            settimer leftdown,-%clicklimit%
                        }
                    }
                    else
                    {
                        righttopress:=0
                        settimer rightdown,off
                        settimer middledown,-0
                    }
                    o:=1
                }
                else if( wParam==0x204 and scrollsticky==0 )
                {
                    // Handle right button down //
                    if( lefttopress==0 )
                    {
                        if( clicksticky==1 )
                        {
                            settimer rightdown,-0
                        }
                        else
                        {
                            righttopress:=1
                            settimer rightdown,-%clicklimit%
                        }
                    }
                    else
                    {
                        lefttopress:=0
                        settimer leftdown,off
                        settimer middledown,-0
                    }
                    o:=1
                }
                else if( wParam==0x202 )
                {
                    // Handle left button up //
                    if( lefttopress==1 )
                    {
                        settimer leftdown,off
                        settimer leftclick,-0
                    }
                    else
                    {
                        settimer leftup,-0
                    }
                    o:=1
                }
                else if( wParam==0x205 )
                {
                    // Handle right button up //
                    if( righttopress==1 )
                    {
                        settimer rightdown,off
                        settimer rightclick,-0
                    }
                    else
                    {
                        settimer rightup,-0
                    }
                    o:=1
                }
            }
        }
    }
    // Pass on any other mouse events //
    if( o==0 )
    {
        o:=dllcall("CallNextHookEx","uint",mousehook,"int",nCode,"uint",wParam,"uint",lParam)
    }
    return o
}

// Utilities //

rtoz(r)
{
    return ( r>0 ? floor(r) : ceil(r) )
}
getparent(handle)
{
    return dllcall("GetParent","uint",handle)
}
getancestor(handle,steps)
{
    if( steps==0 )
    {
        return handle
    }
    if( steps>0 )
    {
        return getancestor(getparent(handle),steps-1)
    }
    return ""
}
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
}
gettitle(handle)
{
    local title
    wingettitle title,ahk_id %handle%
    return title
}
getposition(handle,byref left,byref top,byref right,byref bottom)
{
    local rect
    varsetcapacity(rect,16)
    dllcall("GetWindowRect","uint",handle,"uint",&rect)
    left:=numget(rect,0,"int")
    top:=numget(rect,4,"int")
    right:=numget(rect,8,"int")
    bottom:=numget(rect,12,"int")
}
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", (y << 32) | (x & 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", (y << 32) | (x & 0xFFFFFFFF), "Ptr") // for negative 64-bit
    return getctrlat2(x,y,handle,handle)
}
getwindow(handle)
{
    return dllcall("GetAncestor","uint",handle,"uint",2)
}

Acc_Init()
{
    Static h
    If Not h
        h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromWindow(hWnd, idObject = -4)
{
    local o
    Acc_Init()
    o:=DllCall("oleacc\AccessibleObjectFromWindow"
        , "Ptr", hWnd
        , "UInt", idObject&=0xFFFFFFFF
        , "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81
            ,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64")
            ,"Int64") 
        ,"Ptr*", pacc)
    if( o==0 )
        Return ComObjEnwrap(9,pacc,1)
}

相关内容