I am working on a huge AHK-project for more than two years now. One of the biggest issues concerning stable operation is that send and click commands are being received by the correct window. If the sends drop in too early or too late or if the user interferes, you easily get a mess. This is why I have written the function winSend. In complex macros I replace Send, Click or PixelGetColor with winSend, winClick, winPixelGetColor. This way I gain much more stability with less code. I like to share it with you.
Thanks for any feedback.
winSend ("!e!f","myWinTitle")
=> Makes sure !e!f is being sent to the "myWinTitle"-window. winSend will wait for the window to appear. It will activate the window (if it is not active) before !e!f is being sent.
The function will return true (which is an errorcode) if myWinTitle does not exist for more than ~3 seconds:
Code: Select all
if winSend("!Enter","myWinTitle")
msgBox Window not found
You can address the window with a full parameter set within one string
winSend("!{Enter}",edWinTL,false,edWinClpE,true)
!Enter sent to the windows in variable edWinTL expects to open a 2nd Window with its name in variable edWinClpE. winSend waits for edWinClpE to open, but will not activate it (false). edWinClpE will be opened invisbly (true. 100% transparent, but fully working). Currently the function will wait endlessly for the 2nd window. This could easily be changed in code if there is a demand to so (I did not have this demand up to now, because my follow-up window are sure to pop up).
winSend("!e",edWinTL,true,edWinGeschw,,100,400,200,300)
Window edWinGeschw is made to appear (visible) at screen coordinates 100,400 and will be sized to 200x300
More new functions:
c := winPixelGetColor("myWinTitlte,....",100,200)
Returns savely the color of position 100,200 in myWinTitle. Will wait for the window and activate it before it reads the color. Returns -1 if myWindowTitle does not exist.
winClick("myWinTitle,...")
Perform a single click onto myWinTitle
winClick(edWinTL,100,200,2)
Perform 2 clicks at 100,200 onto the Window in variable edWinTL
winClick("notepad","Right")
Perform a right click onto the notepad window
winControlClick("Button1", "myWinTitle,...","LEFT",2)
Left clicks twice onto control Button1 in Window myWinTitle. Instead of LEFT you can use MIDDLE,X1,X2,WheelUp (or WU), WheelDown (WD), WheelRight (WR), WheelLeft (WL)
Copy all the Code (with comments in German - sorry):
Code: Select all
; winSend sends the string "senden" to the window "startFenster".
; "startFenster" can be qualified by e.g. "myWinTitle,myWinText,myExcludeTitle,myExcludeText" in the "startFenster" string.
; It waits for window "folgeFenster" and activates it if "aktivieren" is true.
; "folgeFenster" can be qualified by e.g. "myWinTitle,myWinText,myExcludeTitle,myExcludeText" in the "folgeFenster" string.
; Window "folgeFenster" is being opened invisible (100% transparent) if "unsichtbar" is true.
; Window "folgeFenster" might be opened at screenposition "xpos,ypos" with width "breite" and height "hoehe"
winSend(senden:= "", startFenster:="A", aktivieren:=true, folgeFenster:="", unsichtbar:=false, xpos := -1, ypos := -1, breite := -1, hoehe := -1)
{ Loop,4
{ W%A_index% :=
WF%A_index% :=
P%A_index% := 0
}
WClose := false
i := 1
Loop, parse, startFenster, `,
{ if Instr(A_Loopfield,"Close")
WClose := true
else
{ W%i% := Trim(A_Loopfield)
i := i + 1
}
}
Loop, parse, folgeFenster, `,
{ WF%A_index% := Trim(A_Loopfield)
}
if (SubStr(senden,1,14) = "PixelGetColor,") or (Substr(senden,1,6) = "Click,") or (SubStr(senden,1,13) = "ControlClick,")
{ Loop, parse, senden, `, ; Parameter aus Senden-String holen
{ P%A_index% := Trim(A_Loopfield) ; Vorsicht: P1="Click" bzw. "PixelGetColor" bzw. "ControlClick"
}
cmp := A_CoordModePixel
cmm := A_CoordModeMouse
CoordMode,Pixel,Relative
CoordMode,Mouse,Relative
}
Loop ; Warte und aktiviere Fenster ggf. mehrfach
{ fehler := 0
ifWinActive,%W1%,%W2%,%W3%,%W4% ; Startfenster ist aktiv
{ c := f_winSend01_senden(senden,W1,W2,W3,W4 ; Senden/Klicken/PixelGetColor ausführen. c = Farbe bei PixelGetColor
,P1,P2,P3,P4)
if not folgeFenster ; Kein Folge-Fenster erwartet
{ if WClose
break
ifWinActive,%W1%,%W2%,%W3%,%W4% ; Start-Fenster nach dem Senden noch aktiv ?
break ; Ja => OK und Ende
} ; (Nein, nicht mehr aktiv => Bleibe in der Schleife)
else ; Folge-Fenster erwartet
{
if aktivieren ; Folge-Fenster soll aktiviert werden
{ ifWinExist,%WF1%,%WF2%,%WF3%,%WF4% ; Folge-Fenster ist schon da (von vorherigem Loopdurchlauf mit erfolgloser Aktivierung)
WinActivate,%WF1%,%WF2%,%WF3%,%WF4% ; Erneuter Aktivierungsversuch
else ; Folge-Fenster noch nicht da
{ if not f_winSend02_folgeFensterExist(W1 ; Warte bis 2s auf Fenster, ggf. positionieren und transparent machen
,W2,W3,W4,WF1,WF2,WF3,WF4,unsichtbar
,xpos,ypos,breite,hoehe)
continue ; Folge-Fenster weiter nicht da => Neuer Versuch über Schleife
}
WinWaitActive,%WF1%,%WF2%,2,%WF3%,%WF4% ; Folge-Fenster sollte aktiv hochkommen, Bestehendes wurde neu aktiviert
if not Errorlevel ; Fenster ist aktiviert
break ; => OK und raus
}
else ; Folge-Fenster braucht nicht aktiviert zu werden
{ if f_winSend02_folgeFensterExist(W1 ; Warte bis 2s auf Fenster, ggf. positionieren und transparent machen
,W2,W3,W4,WF1,WF2,WF3,WF3,unsichtbar
,xpos,ypos,breite,hoehe)
break ; Folge-Fenster ist da => OK und raus
}
}
}
else ; Startfenster ist nicht aktiv
{ starttime1 := A_Tickcount
Loop
{ ifWinExist,%W1%,%W2%,%W3%,%W4% ; Existiert Startfenster überhaupt (noch) ?
break
} Until ((A_Tickcount - starttime1) > 2000) ; Nach 2s ist anzunehmen, dass es inzwischen geschlossen wurde
IfWinNotExist,%W1%,%W2%,%W3%,%W4% ; (Mit Loop, da WinWait mit 2s Timeout manchmal eine Dauerschleife machte)
{ fehler := 1 ; NOTOK
break ; => Ende
}
WinActivate,%W1%,%W2%,%W3%,%W4% ; Startfenster ist da. Versuche, das Fenster zu aktivieren
ifWinActive,%W1%,%W2%,%W3%,%W4% ; Schnelle Prüfung
{ c := f_winSend01_senden(senden,W1,W2,W3,W4 ; Senden/Klicken/PixelGetColor ausführen. c = Farbe bei PixelGetColor
,P1,P2,P3,P4)
if not folgeFenster ; Kein Folge-Fenster erwartet
{ if WClose
break
ifWinActive,%W1%,%W2%,%W3%,%W4% ; Startfenster nach dem Senden noch aktiv ?
break ; Ja => OK und Ende
} ; (Nein, nicht mehr aktiv => Bleibe in der Schleife)
else ; Folge-Fenster erwartet
{ if aktivieren ; Folge-Fenster soll aktiviert werden
{ ifWinExist,%WF1%,%WF2%,%WF3%,%WF4% ; Folge-Fenster ist schon da (von vorherigem Loopdurchlauf mit erfolgloser Aktivierung)
WinActivate,%WF1%,%WF2%,%WF3%,%WF4% ; Erneuter Aktivierungsversuch
{ if not f_winSend02_folgeFensterExist(W1 ; Warte bis 2s auf Fenster, ggf. positionieren und transparent machen
,W2,W3,W4,WF1,WF2,WF3,WF4,unsichtbar
,xpos,ypos,breite,hoehe)
continue ; Folge-Fenster weiter nicht da => Neuer Versuch über Schleife
}
WinWaitActive,%WF1%,%WF2%,2,%WF3%,%WF4% ; Neues Fenster sollte aktiv hochkommen, Bestehendes wurde neu aktiviert
if not Errorlevel ; Fenster ist aktiviert
break ; => OK und raus
}
else ; Folge-Fenster braucht nicht aktiviert zu werden
{ if f_winSend02_folgeFensterExist(W1 ; Warte bis 2s auf Fenster, ggf. positionieren und transparent machen
,W2,W3,W4,WF1,WF2,WF3,WF4,unsichtbar
,xpos,ypos,breite,hoehe)
break ; Folge-Fenster ist da => OK und raus
}
}
}
}
fehler := 3 ; false setzen, wenn Looptime ohne Erfolg abläuft
}
CoordMode,Mouse,%cmm%
CoordMode,Pixel,%cmp%
if (P1 = "PixelGetColor")
{ if fehler
Return -fehler ; Errorcode negativ zurückgeben
else
Return c ; Farbe zurückgeben
} else
Return fehler
}
;Hilfsroutine 1 zu winSend. Sendet Sequenzen oder macht PixelGetColor
f_winSend01_senden(senden, W1, W2, W3, W4, P1, P2, P3, P4)
{ c := 0
if (P1 = "PixelGetColor")
PixelGetColor,c,%P2%,%P3% ; c (color) wird auf den gefundenen Farbwert gesetzt
else if (P1 = "Click") ; Für Mausclick muss Fenster kurzfristig enabled werden
{ WinGet,s,Style,%W1%,%W2%,%W3%,%W4%
if (s & 0x8000000) ; Startfenster ist disabled
WinSet,Enable,,%W1%,%W2%,%W3%,%W4%
Click,%P2%,%P3%,%P4%
if (s & 0x8000000) ; und wieder zurücksetzen
WinSet,Disable,,%W1%,%W2%,%W3%,%W4%
}
else if (P1 = "ControlClick") ; Für Mausclick muss Fenster kurzfristig enabled werden
{ WinGet,s,Style,%W1%,%W2%,%W3%,%W4%
if (s & 0x8000000) ; Startfenster ist disabled
WinSet,Enable,,%W1%,%W2%,%W3%,%W4%
ControlClick,%P2%,%W1%,%W2%,%P3%,%P4%,,%W3%,%W4%
if (s & 0x8000000) ; und wieder zurücksetzen
WinSet,Disable,,%W1%,%W2%,%W3%,%W4%
}
else
Send %senden% ; Sende, was zu senden ist
Return c
}
;Hilfsroutine 2 zu winSend. Wartet auf Erscheinen des Folgefensters, ggf. wird Größe und Position justiert und es unsichtbar gemacht
f_winSend02_folgeFensterExist(W1, W2, W3, W4, WF1, WF2, WF3, WF4, unsichtbar, xpos, ypos, breite, hoehe)
{ WinGet,s,Style,%W1%,%W2%,%W3%,%W4%
Loop,13000 ; 13.000x dauert ca. 2s (keine Sleeps einbauen, da diese unkalkulierbar sind)
{ ifWinExist,%WF1%,%WF2%,%WF3%,%WF4%
goto g_winSend02
}
ifWinNotExist,%WF1%,%WF2%,%WF3%,%WF4%
Return false
g_winSend02:
if (s & 0x8000000)
WinSet,Disable,,%W1%,%W2%,%W3%,%W4%
if(xpos >= 0) or (breite > 0) ; Wenn x-position des Fensters oder Breite mitgegeben und das Fenster hat
{ WinGetPos,x,y,b,h,%WF1%,%WF2%,%WF3%,%WF4% ; ... hat diese Einstellung nicht, dann in nur einem Move-Befehl einstellen
if (xpos = -1) ; Nicht angetastete Einstellungen bleiben bestehen
{ xpos := x
ypos := y
}
if (breite = -1)
{ breite := b
hoehe := h
}
if ((x != xpos) or (y != ypos)) and ((b != breite) and (h != hoehe))
{ WinSet,Transparent,0,%WF1%,%WF2%,%WF3%,%WF4% ; Für die Bewegung der Fenster zunächst unsichtbar machen und dann bewegen
WinMove,%WF1%,%WF2%,xpos,ypos,breite,hoehe,%WF3%,%WF4%
}
else if (x != xpos) or (y != ypos)
{ WinSet,Transparent,0,%WF1%,%WF2%,%WF3%,%WF4%
WinMove,%WF1%,%WF2%,xpos,ypos,,,%WF3%,%WF4%
}
else if (b != breite) or (h != hoehe)
{ WinSet,Transparent,0,%WF1%,%WF2%,%WF3%,%WF4%
WinMove,%WF1%,%WF2%,,,breite,hoehe,%WF3%,%WF4%
}
else if unsichtbar ; Fenster muss nicht neu positioniert werden
WinSet,Transparent,0,%WF1%,%WF2%,%WF3%,%WF4%
if not unsichtbar ; Neu positionierte Fenster wieder sichtbar machen, wenn verlangt
WinSet,Transparent,255,%WF1%,%WF2%,%WF3%,%WF4%
} else if unsichtbar ; Nicht zu positionierenden Fenster unsichtbar machen, wenn verlangt
WinSet,Transparent,0,%WF1%,%WF2%,%WF3%,%WF4%
Return true
}
winPixelGetColor(Window:="A", x:=0, y:=0)
{ Return winSend("PixelGetColor," . x . "," . y, Window)
}
winClick(Window:="A", a:="", b:="", c:="")
{ Return winSend("Click," . a . "," . b . "," . c, Window)
}
winControlClick(cntrl, Window:="A", button:="LEFT", count:=1)
{ Return winSend("ControlClick," . cntrl . "," . button . "," . count, Window)
}