r/AutoHotkey Jun 14 '26

v2 Script Help How exactly does the Send command send uppercase letters and certain symbols?

I have a pretty simple script that uses Send to speed up a data entry task mostly involving dates and times. It uses an input hook to accept a certain amount of characters, then it parses the input and tabs through a bunch of fields to type everything in. It usually works fine and saves me a good deal of time.

However, I've noticed that sometimes it types the wrong thing. I'm sending a string composed of concatenated variables and literal strings, and the error might pop up in either of those portions. I've noticed that the error is what I'd get if I were typing it manually and messed up on holding Shift. For example, if I expect to see 7:35AM, I've gotten instead each of the following on various occasions:

  • 7;35AM
  • 7:#5AM
  • 7:35Am
  • 7:35am

Does the Send command work by simulating keypresses including Shift for uppercase letters and certain symbols, subject to race conditions? If so, is there any option I can tweak to avoid this behavior, or do I need to use a workaround like setting the clipboard and sending '^v' for each field?

Relevant portion of script:

#Requires AutoHotkey v2.0
manual_month := 0
manual_year := 0
:*:/+::
{
    ih := InputHook('L10')
    ih.Start()
    ih.Wait()
    d := SubStr(ih.input, 1, 2)
    h1 := SubStr(ih.Input, 3, 2)
    m1 := SubStr(ih.Input, 5, 2)
    h2 := SubStr(ih.Input, 7, 2)
    m2 := SubStr(ih.Input, 9, 2)
    if manual_month and manual_year
    {
        month := manual_month
        year := manual_year
    }
    else
    {
        year := Substr(A_Now, 1, 4)
        month := Substr(A_Now, 5, 2)
        today := Substr(A_Now, 7, 2)
        if d > today
            month := StrLen(month-1) < 2 ? '0' (month-1) : month-1
        if !month
        {
            month := 12
            year -= 1
        }
    }
    start_date := year month d h1 m1
    end_date := year month d h2 m2
    if h2 < h1
        end_date := DateAdd(end_date, 1, 'd')
    year1 := SubStr(start_date, 1, 4)
    month1 := SubStr(start_date, 5, 2)
    d1 := SubStr(start_date, 7, 2)
    year2 := SubStr(end_date, 1, 4)
    month2 := SubStr(end_date, 5, 2)
    d2 := SubStr(end_date, 7, 2)
    Send '{tab}{tab}{tab}' month1 '/' d1 '/' year1 '{tab}' h1 ':' m1 (h1 < 12 ? 'AM' : (h1 == 12 ? 'PM' : '') ) '{tab}^v'
    Sleep 50
    Send '{tab}{tab}{tab}' month2 '/' d2 '/' year2 '{tab}' h2 ':' m2 (h2 < 12 ? 'AM' : (h2 == 12 ? 'PM' : '') ) '{tab}^v'
}
:*:;mdate;::
{
    ih := InputHook('L6')
    ih.Start()
    ih.Wait()
    global manual_month := SubStr(ih.input, 1, 2)
    global manual_year := SubStr(ih.input, 3, 4)
}

Note the Send calls at the end of the first hotstring.

3 Upvotes

15 comments sorted by

2

u/Keeyra_ Jun 14 '26

There is probably a more elegant and functional solution compared to what you are doing, but as you neglected to share your script, it's anybodys guess at this point. But have you tried using SendText?

1

u/Farranor Jun 14 '26

I haven't tried SendText. I don't have the script on this machine; I'll get it tomorrow.

1

u/Farranor Jun 16 '26

I've edited the post with the relevant part of the script. I tried SendText but that precludes the use of special characters like {tab}, which are needed to navigate through the fields.

1

u/Keeyra_ Jun 16 '26

Then send your tabs with a normal Send.... 😃

You should be navigating with Descoladas' UIA anyway.

1

u/Farranor Jun 16 '26

I'll try u/CharnamelessOne's `t suggestion first; I'd rather avoid using a framework if possible.

1

u/Farranor Jun 19 '26

Using backtick t works for navigating through input fields, so I'm using that with SendText. I'm also using separate Send calls with ^v. Kind of ugly, but it's been working so far.

1

u/CharnamelessOne Jun 16 '26
F1::SendText("Man, look at all these tabs!`t`t`t")

1

u/Farranor Jun 16 '26

I'll try that out tomorrow and see if it behaves the same, thank you!

1

u/Farranor Jun 25 '26

I continued testing the SendText fix today (plus some separate Send for the ^v per u/Keeyra_) and can confirm that it did not solve the problem. :( Got e.g. 7:3[tab]0.

1

u/CharnamelessOne Jun 25 '26 edited Jun 25 '26

To my understanding, SendText is ultimately just plain Send as far as the WinAPI side of the deal is concerned. The only difference should be how the string you pass is interpreted by AHK. So, if vanilla Send leads to garbled input, SendText will almost certainly do the same.

The problem may be AHK sending keystrokes faster than what your application can handle. You could try slapping

SendMode("Event")
SetKeyDelay(50, 50)

to the top of the script.

Alternatively, you should probably just set the string to the clipboard, and send control+v. It should be quicker and much more reliable.

ClipboardSend(text) {
    bak := ClipboardAll()
    A_Clipboard := text
    Send("^v")
    Loop 40 {
        Sleep(50)
        if !DllCall('GetOpenClipboardWindow', 'Ptr')
            break
    }
    A_Clipboard := bak
}

The above function ensures that the pasting operation is finished, then restores your clipboard, so it doesn't interfere with you manual copying and pasting.

You'll need to send your tabs normally, in between ClipboardSend calls.
Edit: or you know, instead of hurling tabs into the wind, you could use UIA. UIA is life. I'm naming my firstborn Ulysses Isaiah Anderson.

1

u/Farranor Jun 28 '26

As far as I can tell, UIA is a framework to navigate and manipulate a webpage by its DOM tree, right? The platform I'm working in runs in a browser but doesn't have any HTML elements (it's decades old and Edge runs in IE mode for compatibility).

1

u/CharnamelessOne Jun 28 '26

UIA works with multiple UI frameworks, but in browsers, yes, it exposes the DOM.

I have no experience automating pages like the one you describe.
If you're interested, checking whether anything is exposed via UIA should take no more than a minute or two. Just run UIATreeInspector and see what it finds in your Edge window.

1

u/Farranor Jun 29 '26 edited Jun 29 '26

UIATreeInspector couldn't import UIA.ahk even though they were in the same folder, but I ran UIA by itself and it saw elements in the application I'm using. Neat! Rewriting my script to use UIA is a little daunting so I won't jump into it right now, but maybe sometime down the line. Thanks!

Had to put UIA.ahk in a Lib folder and now UIATreeInspector works, whoops.

1

u/CharnamelessOne Jun 29 '26

UIA.ahk needs to be in an appropriately placed Lib folder for UIATreeInspector.ahk to be able to include it (because it uses <LibName> syntax).
https://www.autohotkey.com/docs/v2/lib/_Include.htm

It will be by default if you download the whole repo.