AutoHotkey 中的脚本同时按下 Windows 按钮和左箭头

问题描述

我想在 autohotkey 中编写一个脚本,以便每次我在 PC 上打开我的字典应用程序时,同时按下 Windows+LeftArrow 键并作为结果,它会捕捉显示器左侧的窗口。

我试过了:

#IfWinActive Oxford Advanced Learner's Dictionary
Send,#{Left}
return

还有这个:

#IfWinActive Oxford Advanced Learner's Dictionary
Send,{LWinDown}{Left}{LWinup}
return

但是当我打开应用程序时,他们中的任何一个都注意到了。

编辑:

正如@Charlie Armstrong 所建议的,真正的问题是:如何在每次启动某个程序时运行一段代码所以 #IfWinActive 可能对以下情况没有用。

解决方法

一种方法是定期检查是否创建了新进程/窗口,并检查它是否是我们想要与之交互的进程/窗口。

第一个示例基于创建/销毁进程时的 COM 通知。

; help for question: https://stackoverflow.com/q/66394326/883015
; by joedf (16:04 2021/02/28)

MyWatchedWindowTitle := "Oxford Advanced Learner's Dictionary"
NewProcess_CheckInterval := 1 ; in seconds
SetTitleMatchMode,2 ;this might not be needed,makes the check for "contains" instead of "same" winTitle
hWnds := []
gosub,initialize_NewProcessNotification
return

; Called when a new process is detected
On_NewProcess(proc) {
    global hWnds
    global MyWatchedWindowTitle
    
    ; get the window handle,if possible
    if (hwnd:=WinExist("ahk_pid " proc.ProcessID)) {
        WinGetTitle,wTitle,ahk_id %hwnd%
        
        ; check if there is a visible window
        if (wTitle)
        {
            ; if so,check if it's a window we want to interact with
            if (InStr(wTitle,MyWatchedWindowTitle))
            {
                ; check if we've interacted with this specific window before
                if (!ArrayContains(hWnds,hwnd)) {
                    ; we havent,so we do something with it
                    hWnds.push(hwnd) ; keep in memory that we have interacted with this window ID before.
                    DoSomething(hwnd) ; the keys we want to send to it
                }
            }
        }
    }
}

DoSomething(hwnd) {
    ; size and move window to the left
    SysGet,MonitorWorkArea,MonitorWorkArea
    posY := 0
    posX  := 0
    width := A_ScreenWidth // 2
    height := MonitorWorkAreaBottom
    WinMove,ahk_id %hwnd%,%posX%,%posY%,%width%,%height%
    
    ; multi-montitor support,more examples,and more complete snapping functions can be found here:
    ; https://gist.github.com/AWMooreCO/1ef708055a11862ca9dc
}

ArrayContains(haystack,needle) {
    for k,v in haystack
    {
        if (v == needle)
            return true
    }
    return false
}



initialize_NewProcessNotification:
;////////////////////////////// New Process notificaton ////////////////////////
; from Lexikos' example
; https://autohotkey.com/board/topic/56984-new-process-notifier/#entry358038

; Get WMI service object.
winmgmts := ComObjGet("winmgmts:")

; Create sink objects for receiving event noficiations.
ComObjConnect(createSink := ComObjCreate("WbemScripting.SWbemSink"),"ProcessCreate_")
ComObjConnect(deleteSink := ComObjCreate("WbemScripting.SWbemSink"),"ProcessDelete_")

; Set event polling interval,in seconds.
interval := NewProcess_CheckInterval

; Register for process creation notifications:
winmgmts.ExecNotificationQueryAsync(createSink,"Select * from __InstanceCreationEvent"
    . " within " interval
    . " where TargetInstance isa 'Win32_Process'")

; Register for process deletion notifications:
winmgmts.ExecNotificationQueryAsync(deleteSink,"Select * from __InstanceDeletionEvent"
    . " within " interval
    . " where TargetInstance isa 'Win32_Process'")

; Don't exit automatically.
#Persistent
return

; Called when a new process is detected:
ProcessCreate_OnObjectReady(obj) {
    proc := obj.TargetInstance
    /*
    TrayTip New Process Detected,% "
    (LTrim
        ID:`t" proc.ProcessID "
        Parent:`t" proc.ParentProcessID "
        Name:`t" proc.Name "
        Path:`t" proc.ExecutablePath "
        
        Command line (requires XP or later):
        
        " proc.CommandLine
    )
    */
    On_NewProcess(proc)
}

; Called when a process terminates:
ProcessDelete_OnObjectReady(prm) {
    /*
    obj := COM_DispGetParam(prm,9)
    proc := COM_Invoke(obj,"TargetInstance")
    COM_Release(obj)
    TrayTip Process Terminated,% "
    (LTrim
        ID:`t" COM_Invoke(proc,"Handle") "
        Name:`t" COM_Invoke(proc,"Name")
    )
    COM_Release(proc)
    */
}

第二个示例可能更简单一些,它会定期检查与搜索到的 WinTitle 匹配的新窗口。

; help for question: https://stackoverflow.com/q/66394326/883015
; by joedf (16:17 2021/02/28)

#Persistent

MyWatchedWindowTitle := "Oxford Advanced Learner's Dictionary"
SetTitleMatchMode,makes the check for "contains" instead of "same" winTitle
SetTimer,checkForNewWindow,1000 ;ms
hWnds := []
return

checkForNewWindow() {
    global hWnds
    global MyWatchedWindowTitle
    
    ; first check if there is at least one window that matches our winTitle
    if (hwnd:=WinExist(MyWatchedWindowTitle)) {
        ; get all window matches
        WinGet,wArray,List,%MyWatchedWindowTitle%
        
        ; loop through all windows that matched
        loop % wArray
        {
            hWnd := wArray%A_Index%

            ; check if we've interacted with this specific window before
            if (!ArrayContains(hWnds,hwnd)) {
                ; we havent,so we do something with it
                hWnds.push(hwnd) ; keep in memory that we have interacted with this window ID before.
                DoSomething(hwnd) ; the keys we want to send to it
            }
        }
    }
}

DoSomething(hwnd) {
    ; size and move window to the left
    SysGet,v in haystack
    {
        if (v == needle)
            return true
    }
    return false
}
,

我认为您最大的问题是 AHK 似乎不适用于捕捉窗口(根据我的快速研究和测试)。不过,有效的是 WinMove

我假设您是从快捷方式图标启动程序,但我建议使用键盘快捷方式启动程序,然后从脚本定位窗口。下面是一些示例代码,它打开 Notepad2.exe,等待 200 毫秒,然后移动窗口并调整其大小:

^+!n:: ; Control+Shift+Alt+N to Open Notepad
Run C:\Program Files\Notepad2\Notepad2.exe
sleep,200
WinMove,Notepad2,10,20,800,600
return