I have a script that assign F1 for a global task:
f1::Run D:\Download
A program needs to use that key, so I put this:
#IfWinActive, ahk_exe inkscape.exe
F1::send {f1}
return
However when I press it, this error hits:
If yes, nothing happens. If no, the script exits.
Do you know what happens?
The problem is that your hotkey keeps triggering itself over and over a again in a loop. The $ modifier will fix it. That way the hotkey wont get triggered when the source of the key press is a Send command.
However, you don't actually need this at all.
You should use the #IfWinNotActive directive.
#IfWinNotActive, ahk_exe inkscape.exe
F1::Run, D:\Download
#IfWinNotActive
Alternatively, you could just not create a context sensitive hotkey, and use the ~ modifier. That way the hotkey will always retain its normal functionality when you press it.
~F1::Run, D:\Download
Related
I'm using Win+Alt as a hotkey in my script. (Actually, since when they go to whack Win+Alt, either key might be pressed first, I actually use four hotkeys to trigger the same code: LWin+LAlt, LWin+RAlt, RWin+LALt, and RWin+RAlt.) This works fine:
~<#LAlt::
~<!LWin::
~>#LAlt::
~>!LWin::
~<#RAlt::
~<!RWin::
~>#RAlt::
~>!RWin::
; wait for all four of these keys to be up
KeyWait LAlt
KeyWait RAlt
KeyWait LWin
KeyWait RWin
; ... do the actual stuff here
Return
However, there are some programs that use Win+Alt+something to trigger a function. For example, OneNote uses Win+Alt+N to create a Quick Note. Since I'm waiting for those keys to be up before proceeding, my script blocks them from working.
How can I allow these hotkeys while still allowing Win+Alt to trigger my script?
i.e. hitting ctrl+s should activate ctrl+s...the below does not work. Neither does the simpler
SendInput, ^s. Goal is to have ctrl+s save current doc and then load another via more code, the saving part never works tho. The bad code, depending on where i put sleep or no sleep, either returns s or shift s (in 1 code editor anyways) or nothing. I basically want a hotkey that mimics itself.
F4::ExitApp
<^s::
send, {lctrl down}
SLEEP 300
SEND s
SLEEP 300
SEND {lctrl up}
return
I would think that the issue your program is running into is that having the ^s send another ^s inside of itself is creating an infinite recursive loop in which nothing is ever able to run past the place you invoke ^s. To prevent this, we can use the $ modifier as so:
$<^s::
SendInput ^s
return
From the relevant section of the Modifier section of the docs:
This is usually only necessary if the script uses the Send command to
send the keys that comprise the hotkey itself, which might otherwise
cause it to trigger itself. The $ prefix forces the keyboard hook to
be used to implement this hotkey, which as a side-effect prevents the
Send command from triggering it. The $ prefix is equivalent to having
specified #UseHook somewhere above the definition of this hotkey.
Edit: it seems to work fine for me even if I remove the $ modifier. Testing the following code shows me there appears to be no problems regarding code execution before, after, or during the SendInput statement.
<^s::
MsgBox no
SendInput, ^s
MsgBox yes
return
Maybe check your version or installation of AHK?
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.
I mapped {F2} to open/start "OneNote"
I want to use the XButton2 to take the function of F2 when Excel is the active application.
However, pressing of the XButton2 always activtates OneNote.
I tried everything to make this work.
F2::
IF WinExist("ahk_exe ONENOTE.EXE") {
WinActivate
}
RETURN
#IfWinActive ahk_class XLMAIN
$XButton2::
send {F2}
return
#IfWinActive
Your issue is that you're triggering the F2 hotkey by sending F2.
To avoid this, you'll want to use the $ modifier on your F2 hotkey like so:
$F2::
Seems like you actually have it on your XButton2 hotkey? Maybe you were on the right track but have it on the wrong hotkey? Either way, it isn't doing anything for you, so you can remove it.
(Alternative #UseHook can be specified somewhere above, so the keyboard hook is implemented for every hotkey after it)
And now a few miscellaneous fixes about your script. In AHK simple remaps are actually done like this:
XButton2::F2
Sweet clean little one-liner. (One-liner hotkeys don't need a return under them either)
So here would be your finished product:
$F2::
IF WinExist("ahk_exe ONENOTE.EXE") {
WinActivate
}
RETURN
#IfWinActive ahk_class XLMAIN
XButton2::F2
#IfWinActive
Personally I'd also omit the brackets, since they're not required for single line if-statements. But that's just preference of course. And also not sure why you've capitalized return.
I want to remap alt+e when caps is on in autocad.
And when capslock is not on, alt+e should open menu edit.
I use script like this
<!e::
if(GetKeyState( "CAPSLOCK", "T" ))
{
SendInput erase{space}wp{space}
}
else
{
Send !e
}
When I turn on capslock, remap key is OK.
When I turn off capslockand alt+e, menu edit opened, but closed immediately.
Thanks.
You will want a $ at the beginning of your hotkey to prevent the endless loop that the !e in your else block will trigger. You will also want to add a Return at the end of the hotkey to prevent the script from continuing into what is below this hotkey.
$!e::
if GetKeyState( "CapsLock", "T" )
Sendinput, erase{space}wp{space}
else
Sendinput, !e
Return
(Brackets are only required when if/else blocks are more than one line.)
Beyond that, the likely issue is that it's an alt hotkey that is also set to send alt.
I say this is an issue because if you press and hold alt, it activates menus,
and then the script sends alt, which will be in conflict with that.
As Ricardo said, the ideal way to script this is with the #IF command (only included with AHK_L).
#If GetKeyState("CapsLock", "T") and WinActive("AutoCAD")
!e:: SendInput, erase{space}wp{space}
#If
Notice that you can add the WinActive() function to the #If command's expression.
Try it without that first, and also realize that the application's title needs to be exactly "AutoCAD" at all times for that to work. I would recommend finding AutoCad's ahk_class,
with AHK's window spy, instead of using the title.
If it still does not work, it is likely that AHK is sending faster than AutoCAD would like to receive.
Info on how to deal with that can be found here.
Try to change your else block to this:
Send, {ALTDOWN}e{ALTUP}
I do not rely on these symbols to send keystrokes in AutoHotKey.