AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

SendInput in Unicode mode
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Wed Jan 11, 2006 11:17 pm    Post subject: SendInput in Unicode mode Reply with quote

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
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Thu Jan 12, 2006 2:18 am    Post subject: Reply with quote

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
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Fri Jan 13, 2006 10:09 am    Post subject: Reply with quote

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
View user's profile Send private message
Chris
Site Admin


Joined: 02 Mar 2004
Posts: 10467

PostPosted: Fri Jan 13, 2006 1:49 pm    Post subject: Reply with quote

(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
View user's profile Send private message Send e-mail
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Fri Jan 13, 2006 7:20 pm    Post subject: Reply with quote

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
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Fri Jan 13, 2006 11:10 pm    Post subject: Reply with quote

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
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Fri Jan 13, 2006 11:25 pm    Post subject: Reply with quote

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
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Sat Jan 14, 2006 12:56 am    Post subject: Reply with quote

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
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Sat Jan 14, 2006 1:32 am    Post subject: Reply with quote

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
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Sat Jan 14, 2006 3:41 am    Post subject: Reply with quote

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
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Sat Jan 14, 2006 4:29 am    Post subject: Reply with quote

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
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Sat Jan 14, 2006 8:33 am    Post subject: Reply with quote

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
View user's profile Send private message
Lester Wong
Guest





PostPosted: Thu May 03, 2007 9:39 am    Post subject: Reply with quote

So why doesn't it work in Skype?
Back to top
TAB4217



Joined: 13 Sep 2007
Posts: 4
Location: Haiti

PostPosted: Thu Oct 18, 2007 10:59 pm    Post subject: Reply with quote

[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
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4016
Location: Pittsburgh

PostPosted: Fri Oct 19, 2007 12:44 am    Post subject: Reply with quote

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
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group