WinActivate does not work as expected. Re-activating focus to the starting window - autohotkey

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

Related

Close GUI but continue script in AHK

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

AutoHotKey WinActive returns wrong value when changing window focus

In my AutoHotKey script I'm using #IfWinActive to detect if the Roblox window is in focus, and then press the number 1 button whenever left clicking the mouse, like this:
#IfWinActive, Roblox
LButton::
MouseClick, Left
SendInput, {1}
return
#IfWinActive
It works great, except for when I'm clicking out of the Roblox window back to another window. It still fires this code on the first click, resulting in it typing the number 1 into Notepad (or whatever window I switch focus to).
I figured that when I'm clicking on Notepad the focus is still on the Roblox window, which is why the code still fires. So I tried changing the code to this:
#IfWinActive, Roblox
LButton::
Sleep, 100
if WinActive("Roblox")
{
MouseClick, Left
SendInput, {1}
}
return
#IfWinActive
Assuming that by the time the Sleep finished the focus would have shifted to the Notepad window and If WinActive("Roblox") would return false, but it still returns true and types 1 into Notepad.
I also tried using StartTimer and a label, thinking that maybe the Sleep wasn't asynchronous, but that has the same problem as well.
Anybody know how to get around this issue? Thanks in advance!
The main problem in this case is that the hotkey is fired immediately after LButton is pressed down and the Roblox window is still active.
The only solution I see is to fire the hotkey upon release of the LButton using the tilde prefix (~) to prevent AHK from blocking the key-down/up events:
#IfWinActive, Roblox
~LButton Up:: SendInput, 1
#IfWinActive
There are a couple of ways we can achieve this. TL;DR for solution, check the yellow part of this post.
Firstly I'll address the problems in your code:
Usage of MouseClick over Click. Technically nothing wrong, but Click is said to be more reliable in some situations and easier to use. Looks cleaner as well.
Wrapping 1 in {} is not needed and does nothing for you here. In some cases you may even produce unwanted behavior by doing this. In a send command, {} is used to escape keys that have special meaning, or to define keys that you can't just type in. More about this from the documentation.
Having a somewhat of a bad WinTitle that you're matching against. Again, nothing technically wrong, but right now you match any window that starts with the word Roblox. Shouldn't be too hard accidentally match the wrong window.
A quick and a very effective solution would be matching against the process name of your Roblox window.
So #IfWinActive, ahk_exe Roblox.exe or in an if-statement if (WinActive("ahk_exe Roblox.exe")) (assuming that's the process' name, I have no idea)
For an absolutely fool proof way could match against the hwnd of the Roblox window. However, that's maybe a bit overkill and you couldn't really use it with #IfWinActive either. An example I'll write below will use this that though.
However, problems 1 and 2 can be entirely avoided by doing this neat way of remapping a key (remapping is pretty much what you're doing here).
~LButton::1
Ok, so why does that work?
key::key is just the syntax to easily do a basic remap, and with ~ we specify that the hotkey isn't consumed when it fires.
Cool, but now onto the actual problem you're having.
So what went wrong with the sleeping thing? Well since you're consuming the hotkey, all you're actually doing is firing the hotkey, waiting 100ms, then checking if Roblox is active. Well yes, it will still be active since nothing was ever done to switch focus away from it.
If you were to not consume the left clicking action, it would work, but it's definitely not a good idea. You do not want to sleep inside a hotkey statement. AHK does not have true multithreading and unless you would've specified a higher #MaxThreadsPerHotkey for your hotkey, all subsequent presses of the hotkey would be totally ignored for that 100ms.
So yes, with specifying a higher amount of threads that can run for that hotkey, it would kind of make this solution work, but it's still bad practice. We can come up with something better.
With timers you can avoid sleeping in the hotkey statement. Sounds like you tried the timers already, but I can't be sure it went right since code wasn't provided so I'll go over it:
#IfWinActive, ahk_exe Roblox.exe
~LButton::SetTimer, OurTimersCallbackLabel, -100 ;-100 specifies that it runs ONCE after 100ms has passed
#IfWinActive
OurTimersCallbackLabel:
if (WinActive("ahk_exe Roblox.exe"))
SendInput, 1
return
And now onto the real solution, to which #user3419297 seems to have beat me to, just as I'm writing this line of text.
Using the up event of your LButton press as the hotkey.
#IfWinActive, ahk_exe Roblox.exe
~LButton Up::SendInput, 1
#IfWinActive
This way the down event has already switched focus of the window and our hotkey wont even fire.
Note that here we unfortunately can't use the key::key way of remapping I described above.
Bonus:
Here's something that could be used if the up event of our keypress wouldn't be desirable, or somehow the window switching of the active window was delayed.
RobloxHwnd := WinExist("ahk_exe Roblox.exe")
#If, RobloxUnderMouse()
~LButton::1
#If
RobloxUnderMouse()
{
global RobloxHwnd ;specify that we're using the variable defined outside of this function scope
;could've also ran the code to get Roblox's hwnd here every time, but that would be wasteful
MouseGetPos, , , HwndUnderMouse ;we don't need the first two parameters
return RobloxHwnd == HwndUnderMouse ;are they the same hwnd? (return true or false)
}
Here we're first storing the hwnd of our Roblox to the variable RobloxHwnd.
Note that Roblox would need to be running before we run this script, and if you restart robox, script would need to be restarted as well.
So adding some way of updating the value of this variable on the fly would be good, maybe under some hotkey.
Then by using #If we're evaluating an expression (in our case, running a function and evaluating its return value) every time we're about to attempt to fire the hotkey. If the expression evaluates to true, we fire the hotkey.
Usage of #If is actually not recommended, and it is good practice to avoid using if at all possible. However, you wont encounter any problems in a script this small, so using #If is going to be very convenient here.
If you were to have a bigger script in which there's a lot of code running often, you'd be likely to run into problems.

How do I force Windows to ignore an AutoHotKey and instead pass that hotkey directly to the active window?

My cPanel has a text editor that allows multi-cursor functionality using Ctrl-Alt-Up, Ctrl-Alt-Down, Ctrl-Alt-Right, and Ctrl-Alt-Left. Unfortunately, Windows has default hotkeys for these key combinations that rotate the display on your screen. When I try to use these key combinations in the text editor, Windows hijacks them before they can get to the active window.
I first searched to see if there's any reasonably easy way to turn off specific Windows default hotkeys. My search only turned up results that turn off all hotkeys. I then decided to download AutoHotKeys and see if I could write a script to achieve what I was looking to do. Below are some examples; I'll stick with Ctrl-Alt-Right just to select one out of the four:
First block of code is the same in all 4 attempts:
#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%
SetTitleMatchMode, 2 ;this and the next line should only perform the scripted
#IfWinActive Opera ;hotkey if the active window's title contains 'Opera'
Attempt 1: Hotkey correctly bypasses Windows default display-rotating action, and sends the keystrokes to the text editor in Opera. However, the text editor enters the string "ight" wherever the cursor is. There is no Ctrl-Alt-R hotkey in the text editor, and it must not be a Windows default hotkey either. So it sends Ctrl-Alt-R to Opera, which does nothing. Then it sends "ight" which is typed out. (This is the "most successful" of the 4 attempts, but definitely does not achieve the desired outcome):
^!Right::
Send, ^!Right
return
Attempt 2: Only differs from #1 with SendPlay instead of Send. The Hotkey again correctly bypasses Windows default display-rotating action. However, the text editor does not appear to do anything at all:
^!Right::
SendPlay, ^!Right
return
Attempt 3: Back to just Send, but now with curly braces around the second "Right". Now the hotkey doesn't even bypass Windows default display-rotating action (this truly boggles my mind because I would have thought that the result of Attempt #1 proves that the active Opera window was found and the keystroke Ctrl-Alt-Right is correctly being read by the ^!Right in the first line. I'm so confused why adding the curly braces in the 2nd line is negating these things that seem as if they should already have occurred):
^!Right::
Send, ^!{Right}
return
Attempt 4: Only differs from #3 with SendPlay instead of Send. The Hotkey again correctly bypasses Windows default display-rotating action. However, the text editor does not appear to do anything at all:
^!Right::
SendPlay, ^!{Right}
return
To summarize:
----------| Curly Braces | No Curly Braces |
----------|------------------|-----------------------------|
Send | Win Dflt Action | types "ight" in text editor |
SendPlay | no action | no action |
So here's the final question, based on this table, it would appear to me that Send is definitely the way to go. If (as Attempt 3 appears to indicate) the curly brace is causing the display-rotation, how do I send this keystroke to the active window without Windows hijacking it? Obviously, #IfWinActive Opera is working correctly when the curly braces aren't used. Perhaps there is another directive that prevents or bypasses Windows' default action entirely?
If you don't wish to have the screen rotation functionality, this can be disabled in your graphics panel. I believe it's associated with Intel graphics application. It might be something like, right-click the desktop, select graphics options, choose hotkeys, then click disable. This is just guessing as I don't have that on the computer I'm using, but I've encountered it on others' computers before.
As for your hotkeys, you need the curly braces around the word "right" or it will just send the individual characters, just like actually typing it. Another issue is that you need a $ in front of your hotkey since it is self-referencing; that is, it's sending the same keystrokes that activate it.
If you haven't had a chance to check out the help file, I definitely recommend it.
https://www.autohotkey.com/docs/Hotkeys.htm#Symbols

traytip when window becomes active?

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.

autohotkey long text and in a virtual machine

So I'm trying to learn autohotkey scripts and the documentation is lacking at best. First, can authotkey read commands and perform actions and such inside a virtual machine? I have a windows host and a linux virtual machine running eclipse. I'd like to get a hostring (or a keyboard macro, either is fine) to put in some long (10+ lines) of text. Can that actually work in a VM or do I have to run autohotkey inside the VM for it to work?
As for implementing this, I have 2 problems. First, how do I display multiple lines of text from a keyboard macro? I know about the Send command, but I haven't figured out how that works. I have this:
:*:insert::
(
Text to
insert
goes here
and more here
)
And this works fine except in notepad++, it inserts consecutively more tabs, so it will look like
Text to
insert
goes here
and more goes here
And so in my many line macro, by the end it's several pages scrolled off the screen.
As for keyboard macro, changing the above to
#c::
Send{Raw} (
stuf
to send
)
Return
This gives syntax errors and I have no idea what the correct way of doing that would be. Should I just stick with using hotstrings?
You could try to modify the clipboard and use control + v to paste it into the proper place.
Try:
#c::
{
clipboard := "yourtext`nMultiline`nYet another line"
send, {control down}v{control up}
return
}
The first 'insert' hotstring is correct,
however, you would get the same result that you describe,
if you performed manually, the keypresses that the hotstring is sending.
To get the output you want,
you need to change these two settings:
Settings, Preferences...,
Auto-Completion,
untick: Enable auto-completion on each input
Settings, Preferences...,
MISC.,
untick: Auto-indent
the '#c' hotstring is amended below:
#c::
Send {Raw}
(
stuf
to send
)
Return