 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Wed Jan 11, 2006 11:17 pm Post subject: SendInput in Unicode mode |
|
|
The Windows API function, SendInput, permits sending Unicode characters (or a stream of characters) to the active window. The following code includes the function, SendInputU, which encapsulates and simplifies the use of SendInput, as well as, a demonstration (i.e., F1 hotkey). SendInput in Unicode mode is supported by Windows 2000 and XP. The SendInputU function has been tested with Notepad and Word 2003 on Windows XP.
pro: events are inserted serially (i.e., without interruption) into the target window's queue
con: events are context sensitive (i.e, they are affected by other input events)
note: respond if you understand the encoded message
| Code: |
F1::
SendInputU( "041F044004380432043504420020041C043804400021" )
return
EncodeInteger( p_value, p_size, p_address, p_offset )
{
loop, %p_size%
DllCall( "RtlFillMemory"
, "uint", p_address+p_offset+A_Index-1
, "uint", 1
, "uchar", ( p_value >> ( 8*( A_Index-1 ) ) ) & 0xFF )
}
SendInputU( p_text )
{
StringLen, len, p_text
INPUT_size = 28
event_count := ( len//4 )*2
VarSetCapacity( events, INPUT_size*event_count, 0 )
loop, % event_count//2
{
StringMid, code, p_text, ( A_Index-1 )*4+1, 4
base := ( ( A_Index-1 )*2 )*INPUT_size+4
EncodeInteger( 1, 4, &events, base-4 )
EncodeInteger( "0x" code, 2, &events, base+2 )
EncodeInteger( 4, 4, &events, base+4 ) ; KEYEVENTF_UNICODE
base += INPUT_size
EncodeInteger( 1, 4, &events, base-4 )
EncodeInteger( "0x" code, 2, &events, base+2 )
EncodeInteger( 2|4, 4, &events, base+4 ) ; KEYEVENTF_KEYUP|KEYEVENTF_UNICODE
}
result := DllCall( "SendInput", "uint", event_count, "uint", &events, "int", INPUT_size )
if ( ErrorLevel or result < event_count )
{
MsgBox, [SendInput] failed: EL = %ErrorLevel% ~ %result% of %event_count%
return, false
}
return, true
}
|
Last edited by shimanov on Fri Jan 13, 2006 10:41 am; edited 2 times in total |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4016 Location: Pittsburgh
|
Posted: Thu Jan 12, 2006 2:18 am Post subject: |
|
|
Fantastic! Thank you for figuring this out!
A few questions:
- If I replace the HotKey with: ^F1, !F1, !z or ^z the Russian Hello World! (Привет Мир!) message is not received by Ms Word. Others, like: F1, +F1, ^!F1, ^!z, #z work perfectly. What are the restrictions? How can I reset the context, so any HotKey works? (Sending Alt-Down/Up does not work.)
- What do I have to modify for sending ANSI coded strings, like äößü, each character encoded with a single byte? (AHK sends them as ALT-Numpad number combinations, which are interruptible, and ugly side effects can result. Not Unicode aware applications cannot handle the SendInputU Unicode stream.)
- The magic string contains 16-bit Unicodes "041F 0440 0438 0432 0435 0442 0020 041C 0438 0440 0021", which I can find with the Windows Character Map (or by Insert Symbol in Word). Are there any restrictions? |
|
| Back to top |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Fri Jan 13, 2006 10:09 am Post subject: |
|
|
| Laszlo wrote: | | Fantastic! Thank you for figuring this out! |
Sure.
| Quote: | | If I replace the HotKey with... |
I think the following method would be the simplest to ensure a known context:
| Code: |
^F1::
KeyWait, Ctrl
SendInputU( ... )
return
|
| Quote: | | What do I have to modify for sending ANSI coded strings, like äößü... Not Unicode aware applications cannot handle the SendInputU Unicode stream. |
SendInputU should work with strictly ANSI applications. What is an example of an application where this method fails?
| MSDN wrote: | | The system does automatic two-way translation [ANSI <-> Unicode] |
| Code: |
SendInputU( "00E4" ) ; ä
|
SendInput accepts specification of input with virtual key (VKC) and/or scan (SC) codes. Support for ANSI text would require conversion of ANSI characters to their corresponding VKC and/or SC, and a correct context.
| Quote: | | Are there any restrictions? |
There are no documented restrictions beyond those mentioned in my post. |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10467
|
Posted: Fri Jan 13, 2006 1:49 pm Post subject: |
|
|
(I should have posted this earlier)
Nice work. Something like this should probably be integrated into the Send command soon, since it seems of high value to many users. Thanks for your R&D. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4016 Location: Pittsburgh
|
Posted: Fri Jan 13, 2006 7:20 pm Post subject: |
|
|
ANSI code: Yes, it works in not Unicode aware applications I tried so far. The only difference I found is that sending "0008" in MS Word and Notepad works as BackSpace (deleting characters to left), but in MultiEdit it produces an unknown character placeholder, in NotePad2: the bold, inverted letters "BS". It probably has nothing to do with ANSI- or Unicode, just how an application handles non-printable characters. I just hoped a simulated Unicode hotstring could contain the right number of BackSpace characters encoded in the replacement string, but this does not seem to work universally.
In the mean time, I tried SendInputU in my keyboard remapping scripts. It works as expected, but a little too slow, due to the 20 DllCalls for each character plus one DllCall for send. I made the following modifications for a 5-fold speedup (in the cost of readability) | Code: | SendInputU( p_text ) ; 4 DllCalls/char + 1, reduced from 20/char + 1
{
event_count := ( StrLen(p_text)//4 )*2
VarSetCapacity( events, 28*event_count, 0 )
base = 0
Loop % event_count//2
{
StringMid code, p_text, 4*A_Index-3, 4
code = 0x4%code%
DllCall("RtlFillMemory", "uint", &events + base, "uint",1, "uint", 1)
DllCall("ntoskrnl.exe\RtlFillMemoryUlong", "uint",&events+base+6, "uint",4, "uint",code)
base += 28
DllCall("RtlFillMemory", "uint", &events + base, "uint",1, "uint", 1)
DllCall("ntoskrnl.exe\RtlFillMemoryUlong", "uint",&events+base+6, "uint",4, "uint",code|(2<<16))
base += 28
}
result := DllCall( "SendInput", "uint", event_count, "uint", &events, "int", 28 )
if ( ErrorLevel or result < event_count )
MsgBox SendInput failed`nErrorLevel = %ErrorLevel%`n%result% events of %event_count%
} | If only a single Unicode character is to be sent (like in keyboard remapping) a further factor of two speedup is possible (and desirable). The data structure is set up in the beginning of the script just once with a single call of SendUinit(), and subsequent SendU(Unicode) calls just overwrite the necessary part. | Code: | SendUinit()
{
Global SendUbuf, SendU4, SendU6
DllCall("LoadLibrary", "str", "ntoskrnl.exe")
SendU4 := 4 << 16 ; KEYEVENTF_UNICODE
SendU6 := 6 << 16 ; KEYEVENTF_KEYUP|KEYEVENTF_UNICODE
VarSetCapacity( SendUbuf, 56, 0 )
DllCall("RtlFillMemory", "uint",&SendUbuf, "uint",1, "uint", 1)
DllCall("RtlFillMemory", "uint",&SendUbuf+28, "uint",1, "uint", 1)
}
SendU( p_uchar ) ; 3 DllCalls, needs one prior call to SendUinit()
{
Global SendUbuf, SendU4, SendU6
DllCall("ntoskrnl.exe\RtlFillMemoryUlong", "uint",&SendUbuf+6, "uint",4, "uint", p_uchar | SendU4)
DllCall("ntoskrnl.exe\RtlFillMemoryUlong", "uint",&SendUbuf+34, "uint",4, "uint", p_uchar | SendU6)
Return DllCall( "SendInput", "uint", 2, "uint", &SendUbuf, "int", 28 )
} |
|
|
| Back to top |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Fri Jan 13, 2006 11:10 pm Post subject: |
|
|
to Chris:
Thanks. SendInputU implements a subset of the functionality available through the the SendInput function. However, I thought the integrated Unicode support would have the highest value.
to Laszlo:
| Code: |
EncodeInteger( 0x800000001, 5, &events, 0 )
|
|
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4016 Location: Pittsburgh
|
Posted: Fri Jan 13, 2006 11:25 pm Post subject: |
|
|
| shimanov wrote: | to Laszlo:
| Code: |
EncodeInteger( 0x800000001, 5, &events, 0 )
|
| Beg your pardon?
Edit: I got it: it makes the character 8 to work as a BS in every application. Thanks! It is great!
(You seem to have an answer for everything. What byte do I have to change, so my next paycheck will show the decimal point one position to the right?) |
|
| Back to top |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Sat Jan 14, 2006 12:56 am Post subject: |
|
|
| Laszlo wrote: | | Beg your pardon?... it makes the character 8 to work as a BS |
What was your first impression?
| Quote: | | What byte do I have to change, so my next paycheck will show the decimal point one position to the right? |
That would require subverting the system, which would be contrary to my principles. Although, there are conventional means to accomplish the same, which mostly depend on time, 1.0 => 10 years, and effort, 10.0% => 100%, resulting in a paycheck of $10,000 => $100,000. It's actually quite straightforward, and there are shortcuts, but they are few and far between. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4016 Location: Pittsburgh
|
Posted: Sat Jan 14, 2006 1:32 am Post subject: |
|
|
| shimanov wrote: | | What was your first impression? |
0. My reward as the first responder understanding the encoded message in the original post.
1. Secret message, which I have to decode to get the $10,000 first price.
2. A bug in my short and ugly version of your code.
3. Patch, to make SendInput context insensitive.
But, seriously, what does this magic string really do? Can I send this way DEL, CR, LF?
You mentioned the possibility to send scan codes. How? It would be useful if we could send ESC, Ctrl-C, Alt-Space, F11 or similar. |
|
| Back to top |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Sat Jan 14, 2006 3:41 am Post subject: |
|
|
| Laszlo wrote: | | My reward... |
Actually, the code was the reward. It wouldn't have any value if you didn't understand the result.
| Quote: | | ... the $10,000 first price. |
Sorry. I preside in a state where lotteries are illegal.
| Quote: | | my short and ugly version of your code |
I don't comment on aesthetics.
| Quote: | | Patch, to make SendInput context insensitive. |
Close, but not quite. However, it does exemplify a means to that end.
| Quote: | | what does this magic string really do? Can I send this way DEL, CR, LF? |
It simulates sending <Backspace> as input by specifying its virtual key code. As it happens, it is sufficient to only send that key's pressed state in order to be correctly recognized.
| Code: |
EncodeInteger( 0x1B00000001, 5, &events, 0 ) ; Esc
|
Yes. The array of inputs (i.e., events) can be constructed with differing specifications, which enables mixing ANSI and Unicode character events in a single stream. This can be accomplished in a straightforward manner by enhancing the p_text parser or combining events from SendInputA and SendInputU before calling SendInput. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4016 Location: Pittsburgh
|
Posted: Sat Jan 14, 2006 4:29 am Post subject: |
|
|
If I understand right:
- SendInput needs 2 consecutive blocks of data for each character it processes, 28 bytes each.
- Each block starts with 01 00 00 00
- If, in the first block, the next 2 bytes are 00 00, the 2 bytes after them are taken as Unicode. In this case, the second block has to have the same data up to here.
- If, in the first block, bytes 4 and 5 are not zero, they are taken as scan code. The following 4 bytes and all these 6 locations in the second block do not matter.
- The first block contains 04 in the byte position 8 (meaning key down).
- The second block contains 06 in the byte position 8 (meaning key up).
- All other bytes are 0.
Please correct where I am wrong. |
|
| Back to top |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Sat Jan 14, 2006 8:33 am Post subject: |
|
|
| Laszlo wrote: | | Please correct where I am wrong. |
| Code: |
EncodeInteger( 4, 4, &events, base+4 ) ; KEYEVENTF_UNICODE
EncodeInteger( 2|4, 4, &events, base+4 ) ; KEYEVENTF_KEYUP|KEYEVENTF_UNICODE
|
The documentation for SendInput can be found at MSDN. The constants are defined in the header files, which are include with the SDK. |
|
| Back to top |
|
 |
Lester Wong Guest
|
Posted: Thu May 03, 2007 9:39 am Post subject: |
|
|
| So why doesn't it work in Skype? |
|
| Back to top |
|
 |
TAB4217
Joined: 13 Sep 2007 Posts: 4 Location: Haiti
|
Posted: Thu Oct 18, 2007 10:59 pm Post subject: |
|
|
[code]
^F1::
KeyWait, Ctrl
SendInputU( ... )
return
***Why is KeyWait, Ctrl necessary? (An explanation would help me to understand the workings of AHK.)
Thank you.
TAB |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4016 Location: Pittsburgh
|
Posted: Fri Oct 19, 2007 12:44 am Post subject: |
|
|
The SendInputU function does not change the state of the modifier keys. If you use it in a hotkey subroutine, like Ctrl-F1, the sent keys will all behave as Ctrl-key combinations, while the user holds the Ctrl key down. You have to do something to fake a release of the Ctrl key, or wait until it is released by the user (KeyWait Ctrl).
Last edited by Laszlo on Thu Sep 04, 2008 6:27 am; edited 2 times in total |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|