I have the following script that sends ControlClick + ControlSend to a window in background (while I'm working in another window). It works ok as expected.
SetTitleMatchMode 2
$F1::
sleep 1000
ControlClick, x400 y470, Notepad
ControlSend,, text, Notepad
return
The problem is: when I add a Gui on the script, the ControlClick behavior becomes odd once I close the Gui (with Submit or Destroy): If I'm working in a window A (Chrome for example) the ControlClick don't work in background anymore: it activates the window B (Notepad) like in the WinActivate command instead.
Here's the script with the issue (same as the previous one but with a simple Gui):
SetTitleMatchMode 2
Gui, Add, Text,, box
Gui, Add, Button, default, OK
Gui, Show, W300 H300
return
GuiClose:
ButtonOK:
Gui, Submit
return
$F1::
sleep 1000
ControlClick, x400 y470, Notepad
ControlSend,, text, Notepad
return
I'm on Windows Vista 32 bits, Autohotkey v1.1.25.01
I can't understand why the Gui Submit is changing the ControlClick bahavior. How can I fix this and let the ControlClick run in background just like it was without the Gui Submit/Destroy?
ps: both windows were maximized.
ControlClick is known to not be very reliable in certain cases. Take a look at the Reliability section in the ControlClick documentation, there are some things you can try:
To improve reliability -- especially during times when the user is
physically moving the mouse during the ControlClick -- one or both of
the following may help:
Use SetControlDelay -1 prior to ControlClick. This avoids holding
the mouse button down during the click, which in turn reduces
interference from the user's physical movement of the mouse.
Specify the string NA anywhere in the sixth parameter (Options) as
shown below:
SetControlDelay -1
ControlClick, Toolbar321, WinTitle,,,, NA
NA avoids marking the target window as active and avoids
merging its input processing with that of the script, which may
prevent physical movement of the mouse from interfering (but usually
only when the target window is not active). However, this method might
not work for all types of windows and controls.
Related
I'm trying to have a gui close but leave the script running. This is because I want to do other actions if the user chooses to escape the GUI by either hitting esc or simply clicking the 'X' in the upper right. I don't understand how I would leave the script running but close the gui. GUI close doesn't seem to do anything when clicking esc or the X. I've scanned through the GUI docs and cannot figure it out. They always run exitapp, but I'm not ready to exitapp, I need to do other things.
Gui, Add, Text, ,To cancel, press ESCAPE or close this window.
Gui, Show, w320 h80, Downloads
GuiClose:
GuiEscape:
; Continue on to do other things here!!!!
WinActivate ahk_exe notepad++.exe
; do things...
exitapp
What you're describing sounds like what I wanted to do, too. It was a lot of slogging and piecemealing but I think what you are looking for is Gui, Cancel Take a look at the code below and see if that doesn't solve your problem.
It doesn't use ExitApp and it doesn't destroy the window if you want to pop it up later. You'll have to figure how to place the rest of your code, but I believe this is what you are asking.
;Set the GUI window
Gui, Add, Text,, Hit Escape to Clear window when it is active
#F9:: ;show the window with WindowsKey-F9
Gui, Show,
return
;set the escape key to clear GUI window
GuiEscape: ; Note: single colon here, not double
Gui, Cancel
Return
It's a little trickier if you have multiple GUI windows. You must name each:
;Set the two GUI windows. In example, First is labeled as FirstGUI and second as SecondGUI. Notice how you separate with single colon.
Gui, FirstGUI:Add, Text,, Hit Escape to Clear FirstGUI window when it is active
Gui, SecondGUI:Add, Text,, Hit Escape to Clear SecondGUI window when it is active
#F9:: ;show the two windows ("xCenter y700" is used to prevent the windows from stacking.)
Gui, FirstGUI:Show, xCenter y700, Window Title of First GUI
Gui, SecondGUI:Show, xCenter y900, Window Title of Second GUI
return
;set the escape key to clear for each Gui independently.
FirstGUIGuiEscape: ; Note that you must add the Gui Name (FirstGUI) to the beginning of the "GuiEscape:" command and also name it in the following:
Gui, FirstGUI:Cancel
Return
SecondGUIGuiEscape: ; And here you must add SecondGUI as noted above.
Gui, SecondGUI:Cancel
Return
Answering a little late but hope this works for you or someone else!
They assume in the documentation that by clicking x, you'd want the script to close.
So they show ExitApp as an example.
If you don't want to do that though, of course no need to do it.
I think what you're after is destroying the gui:
GuiClose:
GuiEscape:
Gui, Destroy
return
I am having some serious struggles fully grasping the control on activating windows and forcing their focus and foremost position.
In order to debug a larger script I made a separate script to test the use of WinActivate and again I am observing frustrating behaviour as it either all together ignores the title I have defined or is failing in some other way. In the smaller test script I am simply requesting that the window in which the hotkey was triggered be set as active after another action, specifically an input box
Below is the simple code for testing:
F10::
SetTitleMatchMode, 1
DetectHiddenWindows, Off
WinGetTitle, startTitle, A
msgbox % "Start Title = <" . startTitle . ">"
;WinActivate, startTitle
inputbox, mode, Test box, Testing,,260,160
sleep 500
WinActivate, startTitle
Return
This code does not properly activate the starting window. For example I execute the hotkey in an empty notepad window and upon submitting blank into the input box the focus becomes notepad++ on my second monitor. The second time I press the hotkey from within notepad (or another application) notepad does not lose focus. In a third execution I begin from notepad again and after the input box appears I switch the focus to another window. I again submit blank to the inputbox but that new window remains the focus and notepad is not activated or brought to the foremost position.
Can someone please explain to me what is going on with WinActivate?
I was having similar frustration with unexpected results making a windows script host file and I think I must be missing some fundamental detail in windows.
You are trying to activate a window that start with the literal text "startTitle".
You forgot(?) to either enter expression syntax with % or use the legacy way of referring to a variable %startTitle% (please don't use legacy).
Extra stuff:
You shouldn't specify SetTitleMatchMode and DetectHiddenWindows inside your hotkey statement. There is no need (unless there actually is) to set those every time you hit the hotkey. Just specify them at the top of your script once.
Both of them are useless for you though, below I'll show why. Also DetectHiddenWindows is already off by default.
WinGetTitle is not good to use for this. What you actually want to do is get the hwnd of the window you wish by using e.g. WinExist().
And then refer to the window by its hwnd. Much better than working with window titles, and impossible to match the wrong window as well. To refer to a window by its hwnd, you specify ahk_id followed by the hwnd on a WinTitle parameter.
And lastly, the concatenation operator . is redundant. Of course you may prefer to use it, but in case you didn't know, it can just be left out.
Here's your revised code:
F10::
_HWND := WinExist("A")
MsgBox, % "Start hwnd = <" _HWND ">"
InputBox, mode, Test box, Testing,,260,160
Sleep, 500
WinActivate, % "ahk_id " _HWND
Return
I have made some code that only appears ontop of OneNote and offers some buttons. The problem I have with it is that when I press the button it gets focus so the GUI disappears. I have found two possible solutions, but I'm not sure how to implement them.
My first idea would be to make the GUI unfocusable, but as stated I don't how that would work.
The second thought is that I could make
WinWaitNotActive - OneNote
Only pass when neither the OneNote nor the GUI are active, but I also don't know how to pass two possible programs into the command.
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance force
#Notrayicon
SetTitleMatchMode 2
Gui +LastFound -Caption +ToolWindow +Border + AlwaysOnTop
Gui Add, Button, gDublicate x-1 y-1 w60 h25, Dublicate
Gui Add, Button, gDelete x57 y-1 w60 h25, Delete
Gui Add, Button, gBackwards x115 y-1 w25 h25, ←
Gui Add, Button, gForwards x138 y-1 w25 h25, →
Loop{
WinWaitActive - OneNote
Gui, Show, xCenter y35 NoActivate h23 w162
WinWaitNotActive - OneNote
Gui Hide
}
return:
Dublicate:
WinActivate - OneNote
SendInput ^c
Sleep 50
SendInput ^v
return
Delete:
WinActivate - OneNote
SendInput {Del}
return
Backwards:
WinActivate - OneNote
SendInput ^z
return
Forwards:
WinActivate - OneNote
SendInput ^y
return
For your first idea, I think you can just use the extended window style WS_EX_NOACTIVATE. It can be applied to your GUI by adding +E0x08000000 to the options:
Gui +LastFound -Caption +ToolWindow +Border +AlwaysOnTop +E0x08000000
Alternatively, I explored your second idea too. You should be able to do it using ahk_group, like so:
GroupAdd OneNote, - OneNote
; Requires adding +HwndGuiHwnd to your GUI options
GroupAdd OneNote, ahk_id %GuiHwnd%
Loop{
WinWaitActive ahk_group OneNote
Gui, Show, xCenter y35 NoActivate h23 w162
WinWaitNotActive ahk_group OneNote
Gui Hide
}
I don't have OneNote and can't truly test these, but I believe either of them should work. You might, however, run into some inconsistency since WinWait[Not]Active works by continuously checking the foreground window. This involves calling GetForegroundWindow, which may return NULL in a small time frame between switching windows, resulting in WinWait[Not]Active returning unexpectedly. In this case you might want to abandon the WinWaitActive+WinWaitNotActive loop altogether in favor of a shell hook, which I wrote about here.
I want to simply display a tooltip when a window becomes active.
Why doesn't this work? It launches the tooltip as soon as the script is loaded.
#IfWinActive, Untitled - Notepad
{
TrayTip, Notepad Has Focus, test
Tab::
MsgBox Window Found
return
}
Tab detection works as expected, it shows the Message Box only if the window is active.
As per the #If... docs, #IfWinActive creates context-sensitive hotkeys and hotstrings. To be a bit more precise, this is what happens when you use #IfWin...:
Whenever you press a hotkey or type a hotstring, AHK looks up the corresponding #IfWin... definition (if available) and evaluates it (e.g. "Is notepad active?"). If it is true, the hotkey/hotstring label will be executed, otherwise the native key will be sent.
Looking at this procedure, you will recognize that executing arbitrary code below a #IfWin... statement won't work; AHK doesn't fire an event when a specified window becomes active/existent etc, it rather checks the conditions when a corresponding hotkey/hotstring is fired.
Ergo, you will have to write code that waits for notepad, shows a notification and possibly repeats this procedure:
#Persistent
SetTimer, WaitForNotepad, -1
Exit
WaitForNotepad:
WinWaitActive, ahk_class Notepad
TrayTip, Warning, Notepad is active!
WinWaitNotActive
SetTimer, WaitForNotepad, -1
return
Please note that this would also work without SetTimer in some kind of loop. But whenever you're waiting a potentially large amount of time, it is reasonable to use timers, since they virtually allow other threads to run in between.
You also noticed that I used the window class (ahk_class) instead of the window title, since it's usually more reliable.
I need to force Outlook which is in the background, to check for new emails every 2 seconds. So I wrote a following script but it doesn't do it. What's wrong? I don't want the script to disturb what I do and give the focus to the Outlook window. The "ahk_class rctrl_renwnd32" is correct, I checked it with "WinActivate, ahk_class rctrl_renwnd32" and it worked.
Loop
{
ControlSend,, {F9}, ahk_class rctrl_renwnd32
Sleep 2000 ; Wait 2 seconds
}
There is no error in your code. The problem might be in the receiving application. When I test this in Notepad, an F5 (Print time & date) is executed when the Notepad window is open somewhere on my second screen. As soon as I minimize Notepad, it will no longer execute F5, but it still accepts a string like A{Enter}B{Enter}C{Enter} when minimized.
!q::
ControlSend,, {F5}A{Enter}B{Enter}C{Enter}, ahk_class Notepad
Return
Solution, Try if this works when you keep the window on screen somewhere (no need to have the focus).
I have used Outlook in the past and remember that F9 took some time to execute. Running this every 2 seconds looks like overkill.
If getting your e-mail in time is THAT important that you are willing to "kill" the mail server with refresh requests, I would discuss a solution with your IT support.