The Autohotkey documentation writes:
SendInput is generally the preferred method to send keystrokes and mouse clicks because of its superior speed and reliability. Under most conditions, SendInput is nearly instantaneous, even when sending long strings. Since SendInput is so fast, it is also more reliable because there is less opportunity for some other window to pop up unexpectedly and intercept the keystrokes. Reliability is further improved by the fact that anything the user types during a SendInput is postponed until afterward.
If SendInput is generally preferred, what are the use cases where sent is better in ahk? When does Sent win the Sent vs. SentInput decision?
I generally use SendInput, as I like the nearly instantaneous input. However I have encountered a few practical limitations:
Some applications like games do not like such fast key strokes because there may be rules against botting.
Some applications can't handle receiving keystrokes that fast and they just get bogged down.
I had an application that allowed pressing tab to move the cursor between fields.
When tabbing into a field the application needed time to acknowledge the arrival of the cursor before it would accept any input. The SendInput command was just to fast for this and frequently lead to mixed results
Pressing tab multiple times was also problematic, and frequently some tabs would be missed by the application and the cursor would end on an unexpected field.
SendInput is too fast to replay something for debugging. For example when I want to watch how the text is being inserted into fields on very complex forms.
In theory with Send you could insert a blob of text and randomly press and hold the shift button to increase entropy while the characters were being typed. Arguably I can't think of why this would be useful.
You should consider switching between SendPlay and SendInput only if you have problems with current setup. SendInput cause problems if you type while a long macro was activated. Your commands might be mixed with macro causing undesired behavior. Tutorial says that SendPlay is "not supported in older games", but I never had problems with that. Someone might comment "you haven't used AHK enough". Well, maybe.
Also, before switching from SendPlay to SendInput you should try to divide your macro into two. For instance, "save control group, do stuff, recall control group" fails in Starcraft2. Splitting it into Send "save control group, do stuff"; Sleep 10; Send "recall control group"; works.
Related
I would like to remap the 'j' key so that it presses n when ergo is true, or y when it is false with AutoHotKey. When I remap normally using "j::n" for example, shift+j outputs a capital N, and so do other modifiers with the 'j' key. However, my code below only works when the letters are pressed without modifiers. Is there a way to get around this, and conditionally remap keys with AutoHotKey?
j::
if (ergo) ;inverted use of the ergo variable to make the code more efficient
Send {n}
else
Send {y}
return
You only want to wrap characters which have a special meaning in a send command in { }. Basically escaping, if you're familiar with that what is.
So you don't want to wrap n or y in { }. It can even result in undesired behavior.
There are quite a few approaches for this. Can't say which is best, since don't know what your full script is like.
I'll present two options that I'd guess to be most likely best approaches for you.
Firstly, the send command way like you tried. Just doing it right:
*j::
if (ergo)
SendInput, {Blind}n
else
SendInput, {Blind}y
return
So, usage of the *(docs) modifier so the hotkey works even if extra modifiers are being held down.
And then usage of the blind send mode so which ever modifier keys you may be holding when triggering the hotkey will not be released.
Also switched over to SendInput due to it being the recommended faster and more reliable send mode.
Second way would be creating a context sensitive hotkey with #If(docs).
#If, ergo
j::n
#If
j::y
This is a convenient and easy approach. But could possibly result in other problems.
Why? Well #If comes with some downsides which you can read more about here, but long story short:
You likely wont have any trouble unless you have a more complicated script.
When I remap normally using "j::n" for example, shift+j outputs a capital N, and so do other modifiers with the 'j' key. However, my code below only works when the letters are pressed without modifiers.
Looks like you are looking for the Wildcard * modifier.
From the docs:
Wildcard: Fire the hotkey even if extra modifiers are being held down. This is often used in conjunction with remapping keys or buttons.
So after this change, your code would look something like:
*j::
if (ergo) ;inverted use of the ergo variable to make the code more efficient
Send {n}
else
Send {y}
return
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.
SendInput %userInput% causes my computer to do very weird stuff. Sometimes it logs off, sometimes my arrows of the keyboard get disabled, sometimes it runs cmd in windows an infinite times...
UPDATE:
this is the thing that gets inputed in the command line and runs:
runas /user:administrator cmd
UPDATE:
I think I almost got the problem, as such I edited the question to leave out what I deem to be irrelevant now.
When SendInput is happening, and the user is still inputting data in the keyboard, such as pressing the win-key, then this can cause the system to log off because win-key + l is a shortcut for that. Likewise must be for all the other things that are happening. Another observation is that SendInput skips certain characters, like {enter} etc. It only processes them at the end, when all the regular characters are put into place. I notice that at the end, SendInput is still busy doing stuff, perhaps some exotic characters it delayed till the end. Because the user think the output is complete, he ends up pressing the shortcut key again which in combination with the current sendInput is causing the system to crash.
UPDATE:
It also goes bezerk if there is a "!" to be send with SendInput.
This is one string I pasted to the copyboard:
dsjkfhjdsfsjdh!!!!!!!!!!!####################$$$$$$$$$$$%%%%%%%%%^^^^^^^^^^^^^&&&&&&&&&&&&&*****(((((((((())))))))))))____++++++++++++++++=======------------000000000000000099999988888.
But the output is WITHOUT the exclamation marks. Like this:
dsjkfhjdsfsjdh#######$$$$$$$$$$%%%%%%%%%&&&&&&&&&&&&*****(((((((((())))))))))))____+======------------000000000000000099999988888.
Why is that? Are there any other characters? Exclamation marks are important I feel, and I don't want to remove them. Are there any workarounds?
UPDATE:
It is more complicated than that. When I copy paste the above characters WITHOUT the exclamation marks, it still does weird stuff.
Here is some of the code that eventually userInput combines and sends away with SendInput:
StringReplace, contents, save_selection, ``, ````, All ; Do this replacement first to avoid interfering with the others below.
StringReplace, contents, contents, `r`n, %A_SPACE%, All ; Using `r works better than `n in MS Word, etc.
StringReplace, contents, contents, `;, ```;, All
;* loc_title origanally contains browser specification. Remove it.
StringGetPos, pos_delim, loc_title, - , R
length := StrLen(loc_title)
count := length - pos_delim
StringTrimRight, loc_title, loc_title, count
You could use sendraw instead of sendinput.
Here is what the docs say:
Raw mode: The SendRaw command interprets all characters literally
rather than translating {Enter} to an ENTER keystroke, ^c to
Control-C, etc. However, the normal rules for escape sequences,
variable references and expressions still apply since these are
processed before the command executes. To use raw mode with SendInput,
SendPlay, or SendEvent, write {Raw} as the first item in the string;
for example: SendInput {Raw}abc.
Then you don't have to worry about the ! or other control modifiers.
The reason that the exclamation mark character is an issue is because SendInput treats it as a code to press the Alt key.
There are several similar codes:
^ = Ctrl
+ = Shift
# = Windows key
You can find the others listed in the documentation for SendInput.
It seems like you want to send the raw text and not have SendInput look for these codes. You should be able to put {Raw} at the beginning of the userinput variable and it will ignore any codes in the rest of the characters.
If it is typing too slowly, you could put SetKeyDelay,-1 in your script to remove the key sending delay.
A good workaround that I found was to avoid SendInput altogether. Rather use the clipboard to copy the string inside, to then paste it on your screen. It works flawlessly, and is a billion times faster. Why would a person utilize SendInput in the first place? However, the actual question still is standing, why does SendInput behave so strangely? What is inside that string: %userInput% that causes my system to crash? How can I find out?
So I have this game, called AirMech. It doesn't recognize mouse buttons as controls (yet) so I tried to use AutoHotkey to circumvent it until it's implemented.
#IfWinActive, AirMech
XButton1::Send c
Didn't work. So I tried SendGame, SendPlay and everything else, didn't work either. I googled it, and found out that some games don't recognize any Send commands at all.
Before giving up, I just tried a simple mapping:
#IfWinActive, AirMech
XButton1::c
It actually worked.
Is it expected than no Send command works, but the latter does? What if I wanted to trigger other actions ('c' plus a MsgBox, for instance)?
AutoHotkey has the ability to send keystrokes in a variety of different ways (SendRaw / SendInput / SendPlay / SendEvent). I'm not quite sure what approach the simple key::key mapping uses, but it must be one of them. My guess is that one of SendRaw, SendInput, SendPlay, or SendEvent will work the same as key::key.
Also #IfWinActive sometimes doesn't work exactly the way you expect, especially with fullscreen games. So I usually test my AHK scripts without the #IfWinActive to make sure they're working correctly. Once it's working, I introduce the conditional.
UPDATE
From http://www.autohotkey.com/docs/misc/Remap.htm:
When a script is launched, each remapping is translated into a pair of
hotkeys. For example, a script containing a::b actually contains the
following two hotkeys instead:
*a::
SetKeyDelay -1 ; If the destination key is a mouse button, SetMouseDelay is used instead.
Send {Blind}{b DownTemp} ; DownTemp is like Down except that other Send commands in the script won't assume "b" should stay down during their Send.
return
*a up::
SetKeyDelay -1 ; See note below for why press-duration is not specified with either of these SetKeyDelays. If the destination key is a mouse button, SetMouseDelay is used instead.
Send {Blind}{b Up}
return
My notes:
I suspect the reason a::b is working but a::Send b is not is because of how a::b breaks button down and button up handlers into two separate mappings. The game's gameloop probably polls the gameplay keys for "keydown" state, which would not be maintained consistently if AHK is synthesizing repeats. Remapping a_down->b_down and a_up->b_up probably makes AHK emulate more accurately the act of holding the key down, which may matter for programs which test for key state in particular ways (GetAsyncKeyState?).
The asterisk in the mapping means "Fire the hotkey even if extra modifiers are being held down."
I am writing an AHK script to automate data entry into a GUI form. There is a drop-down box with a few hundred entries. I know the value I want to select and when I manually type in the number the combo box changes to the appropriate value. When I send the same string in AHK (using Send) it chooses the first item on the list. I have played around with various values of SetKeyDelay (-1 through 2000).
SetKeyDelay 1000
Send %ItemNumber%
SetKeyDelay -1
Any suggestions here?
Thanks
Jonathan
Have you tried other send modes? (Like SendInput, SendPlay, etc...)
Also, if it's a standard Windows combobox, it might be more precise to use messages (like CB_SETCURSEL).
Turns out there was a line up top that was messing me up:
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SendInput ignores the value of SetKeyDelay; the above line was converting all of my "Send"s to "SendInput"s
Thanks-
Jonathan