How can I make AutoHotkeys's functions stop working as soon as the .exe is closed? - autohotkey

I'm testing AutoHotkeys as a way to block user's usage of Ctrl, Alt and Windows Key while an application is running. To do this, I compiled the code:
LAlt::return
RAlt::return
LControl::return
RControl::return
RWin::Return
LWin::Return
into an .exe using the compiler that comes with AutoHotkeys.
My problem is that normally when I close the .exe file (either by code using TerminateProcess(,) or manually) the keys are not released immediately. The Windows Key, for example, may take something like 10 seconds to be finely "unlocked" and become able to be used again, and for me this is unacceptable.
So I got two questions:
Is there a way to fix this problem? How can I make the keys to be released as soon as the .exe is closed?
Would there be any improvement if I tryed to get the same functionality by code? Or if I create the hooks by myself I would get the same problem I'm having with AutoHotkeys?
Thanks,
Momergil

AutoHotkey has a built-in command ExitApp for terminating your scripts.
This example makes Esc your termination hotkey:
Esc::ExitApp
It seems like the delay you are experiencing might be related to how long it's taking the process to close.
You could try making the hotkeys conditional with the #If command*
(i.e. they are only blocked when Flag = 1).
Then you can have the script quickly change the context just before ExitApp by using OnExit. The OnExit subroutine is called when the script exits by any means (except when it is killed by something like "End Task"). You can call a subroutine with a hotkey by using the GoSub command.
Flag := 1
OnExit, myExit
Esc::GoSub, myExit
#If Flag
LAlt::return
LCtrl::return
x::return
#If
myExit:
Flag := 0
Exitapp
* The #If command requires Autohotkey_L.
The other option that will be more verbose, but work for AHK basic, is the hotkey command.
Another option is to have AutoHotkey run the target application, and upon application exit, AutoHotkey exits as well. Here's an example with Notepad. When the user closes Notepad, the script gracefully exits.
RunWait, Notepad.exe
ExitApp ; Run after Notepad.exe closes
LAlt::return
RAlt::return
LControl::return
RControl::return
RWin::Return
LWin::Return

I would use winactive to disable these keys. In this example the modyfier keys are disabled for "Evernote". As soon as you switch to another program the keys are restored and when you switch back to Evernote the modifier keys are disabled again.
SetTitleMatchMode, 2 ; Find the string Evernote anywhere in the windows title
#ifWinActive Evernote
LAlt::return
RAlt::return
LControl::return
RControl::return
RWin::Return
LWin::Return
#ifWinActive

Related

How to start a program if not already started, put on focus if already started?

I would like to make a ahk script to start apps if they are not currently started and maximize them if they are. Is it possible using AHK ?
CapsLock & w::
Run firefox.exe
Return
Something like this but make it so that if I press CapsLock & w and then minimize firefox, pressing CapsLock & w would bring it back maximized / in focus. Any ideas? Thanks!
Sure, this is very easy and doable with AHK.
Here's a very easy and straight forward example
CapsLock & w::
if (WinExist("ahk_exe notepad.exe"))
{
WinActivate, ahk_exe notepad.exe
WinMaximize, ahk_exe notepad.exe
}
else
Run, notepad.exe
return
ahk_exe (docs) is used to refer to windows by their process. It's very convenient.
The code that you are looking for is somewhat similar to the example they give in the docs for WinActivate
So modifying that example for your purpose, and adding conditionals would give you:
CapsLock & w::
if WinExist("ahk_exe firefox.exe")
if WinActive("ahk_exe firefox.exe")
WinMinimize
else
WinActivate
else
Run firefox.exe
Return
Take note that this script will currently only minimize a Firefox window if it is the currently active window. If you need it to minimize a Firefox in the background, the script would potentially be a bit more complex since you could possibly have multiple Firefox windows open, and you would need to provide conditions and logic to handle cases like those. However, if you need this functionality, describe what behavior you would like to occur if this condition occurs, and I can work on it.

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.

Automatically reload AutoHotkey script when modified

When testing AutoHotkey scripts, I sometimes forget to reload my scripts after making changes. This leads to me accidentally testing old, outdated versions of my scripts.
Instead of manually reloading the script, I would like to have scripts automatically reload if they have been modified.
How can I make AutoHotkey reload the current script any time a .ahk file is modified?
Somewhere near start of the script, in the auto-execute section
#SingleInstance force
FileGetTime ScriptStartModTime, %A_ScriptFullPath%
SetTimer CheckScriptUpdate, 100, 0x7FFFFFFF ; 100 ms, highest priority
Anywhere in the script (usually somewhere at the bottom):
CheckScriptUpdate() {
global ScriptStartModTime
FileGetTime curModTime, %A_ScriptFullPath%
If (curModTime == ScriptStartModTime)
return
SetTimer CheckScriptUpdate, Off
Loop
{
reload
Sleep 300 ; ms
MsgBox 0x2, %A_ScriptName%, Reload failed. ; 0x2 = Abort/Retry/Ignore
IfMsgBox Abort
ExitApp
IfMsgBox Ignore
break
} ; loops reload on "Retry"
}
This is how I've done it:
#If WinActive("AHK.ahk - Notepad") or WinActive("*AHK.ahk - Notepad")
~^s::
Reload
Return
#If
Checks if the current window is the script that I want autoreloaded whenever I hit Ctrl-S.
The ~ means the default action of Ctrl-S (saving the file) is preserved, and then we simply reload it.
I'm still new at AHK but here's what I've come with.
If you're using Notepad++ to edit AHKS this will run any ahk that's open and currently in focus in Notepad++ on saving with Ctrl-S and won't effect any other file types.
This script only has to be in one running ahk to work on all ahks being modified in Notepad++.
This can be used on multiple text editor programs too like win Notepad just get rid of the ++ associated with Notepad++
~^s:: ; Saves and Runs ANY AHK open in Notepad++
Sleep 150
WinGetTitle, Title, A
Needle := "ahk - Notepad++"
IfInString, Title, %Needle%
{
StringReplace, xxx, Title,- Notepad++, , All
run %xxx%
return
}
else
return

Autohotkey and ExitApp: how to enable and disable ESC key to work just in a part of the script?

I have a very long script and I want to stop it with ESC key if it goes astray.
So I have put in the script this line:
Esc::ExitApp
But, of course, sometimes I have to press ESC key even when not running the script and when I need AutoHotkey, it's gone.
How do I tie ExitApp to ESC key just for a part of the script that's running?
You have to add ExitApp as a command in your script! It is a special kind of label, not a function. If the label doesn't exist in your script, you can't call it.
To make it exist, just add another line with the label on it. -->>**actually, I was wrong to say that. In the docs it gives the example that you can use simply Esc::ExitApp just like that to exit the running script ExitApp exists in the script even if you don't create it first because it's built into AutoHotkey. However, with your new code from your comment, you can use a custom label (which does have to exist in your script before you can call it):
Esc::goto DoSomething
DoSomething:
MsgBox, 4, , Are we exiting?
IfMsgBox, No
return ;after this return, the next line will not be called.
ExitApp ;as soon as this is called, the script ends completely and no more of the script will ever be called
return
this code is working:
Esc::goto ExitThisProgramQuestion
ExitThisProgramQuestion:
MsgBox, 4, , Are we exiting?
IfMsgBox, No
return
ExitApp
return

Hotkey to restart autohotkey script?

Say I have an autohotkey script C:\path\to\my\script running. Is there a way to define a hotkey that re-starts it?
In order to prevent duplicate instances, I normally do not re-launch a script but use the build-in function Reload. I launch this with Ctrl+Win+Alt+R and use Ctrl+Win+Alt+E to edit the main AHK script.
^#!r::Reload
Actually, my script looks like this:
^#!r::
Send, ^s ; To save a changed script
Sleep, 300 ; give it time to save the script
Reload
Return
^!#e::Edit
As a matter of fact, all the way at the top of my script I have this to give me a visual and audio indication that the script was restarted:
#SingleInstance Force
#installKeybdHook
#Persistent
Menu, Tray, Icon , Shell32.dll, 25, 1
TrayTip, AutoHotKey, Started, 1
SoundBeep, 300, 150
Return
Make a hotkey that runs a script, which in this case is the same script and then exit.
somehotkey::
Run, C:\path\to\my\script.ahk
ExitApp
return
I found this to be the safest option of them all, because it takes care that the correct script is reloaded when you have multiple scripts running simultaneously, which was a recurring issue for me. The combination of the following also ensures that only one instance of a script will ever run at a time. The ScriptFullPath variable includes the name of the script.
#SingleInstance Force ;put this at the top of the script
^r::run, %A_ScriptFullPath%