User
User

Reputation: 53

Script in AutoHotkey to Press Windows button and Left Arrow simultaneously

I want to write a script in autohotkey so that every time I open my dictionary application on PC, keys Windows+LeftArrow being pressed at the same time and as the result, it snaps the windows on the left side of monitor.

I tried this:

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

Also this one:

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

But for either of them noting happened when I opened the application.

EDIT:

As suggested by @Charlie Armstrong the real question is: How do I make a block of code run every time I start a certain program? So #IfWinActive might not be useful for.

Upvotes: 1

Views: 689

Answers (2)

Mike
Mike

Reputation: 312

I think your biggest issue is that AHK doesn't seem to work well for snapping windows (according to my quick research and testing). What does work well, though, is WinMove.

I assume you're launching the program from a shortcut icon, but I would suggest using a keyboard shortcut that launches the program and then positions the window from the script. Here is some sample code that opens Notepad2.exe, waits for 200 milliseconds, and then moves the window and resizes it:

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

Upvotes: 1

Joe DF
Joe DF

Reputation: 5548

One way is periodically check if new process/window is created and to check if that is a process/window we want to interact with.

This first example is based on COM notifications when a process has been created/destroyed.

; 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, 0, 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)
    */
}

This second example, which is perhaps a bit simpler, checks periodically for new windows that match the searched 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, 2 ;this might not be needed, 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, 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
}

Upvotes: 1

Related Questions