API - SendInput

Just starting out? Need help? Post your questions and find answers here.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by crono.

Does anybody know how to use the API function SendInput?
I need to send some keystrokes to a window ( like writing some text in notepad )
A example would be very nice :)

here is the link to the MSDN Site where is the description:
http://msdn.microsoft.com/library/defau ... dinput.asp

kind regards,
Alexander Schoenfeld

Email: [url]mailto:alexander.schoenfeld@chronologic.de[/url]
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by PB.

> Does anybody know how to use the API function SendInput?
> I need to send some keystrokes to a window ( like writing some text in
> notepad ) A example would be very nice :)

A quick search of these forums reveals this link:

viewtopic.php?t=1678


PB - Registered PureBasic Coder
SunSatION
User
User
Posts: 85
Joined: Tue Jun 21, 2005 7:26 pm
Location: Malta

Post by SunSatION »

Emmmm... this link does not exist anymore :(
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Post by Michael Vogel »

I'd tried to get SendInput_() running, but it's more complicate than expected :x

I would like to get a function which is able to write "any" text in remote apps, for instance I'd like to do an automatically login within a telnet session.
The "any" means, that not only the standard alphabet should be possible, but also characters, like '§', '•', '–' (ASCII values from 0x20 to 0xFF) and at least some control characters (TAB, ESC, CR)...

Additionally I don't want that strange things happen, when the user pushes a key like "Ctrl" during the output...

So I gave my best and started with this code:

Code: Select all

Global Dim SendInputData.INPUT(0)

Procedure SendInput(Text.s)

	Protected i,j

	i=Len(Text)

	If i

		j=i
		ReDim SendInputData(j-1)

		While j
			j-1

			SendInputData(j)\type=#INPUT_KEYBOARD

			SendInputData(j)\ki\wVk=PeekB(@Text+j)
			SendInputData(j)\ki\dwExtraInfo = 0
			SendInputData(j)\ki\dwFlags = 0
			SendInputData(j)\ki\time = 1
			SendInputData(j)\ki\wScan = 0
		Wend
		
		SendInput_(i,SendInputData(0),SizeOf(INPUT))
		
	EndIf

EndProcedure

Delay(2000)
Delay(2000)
SendInput("H")
; SendInput("Hello, Purebasic is here ;) ·•o©") ; <--- Don't do that, it will start the calculator ?!
But the only thing, I'll get is a lousy "h" :shock:

Now I really need experts :roll:

Thanks,
Michael
Pupil
Enthusiast
Enthusiast
Posts: 715
Joined: Fri Apr 25, 2003 3:56 pm

Post by Pupil »

Note that you're sending the virtual keycodes with the method you're currently using, this means that you can only use capital letters with the conversion you use now. i.e:

Code: Select all

SendInput("HELLO")
However these capital letters will be sent as lower case letters to the thread receiving the events.

I would suggest that you use the #KEYEVENTF_UNICODE flag to send unicode characters instead (read up on MSDN). This way i think you could easily send those other ASCII-values from 0x20 to 0xff. This few lines is more or less what you need for this:

Code: Select all

#KEYEVENTF_UNICODE = 4

 ...
      Protected char.l
      While j
         j-1
         PokeS(@char, Chr(PeekB(@Text+j)), #PB_Unicode)
         SendInputData(j)\type=#INPUT_KEYBOARD
         SendInputData(j)\ki\wVk=0
         SendInputData(j)\ki\dwExtraInfo = 0
         SendInputData(j)\ki\dwFlags = #KEYEVENTF_UNICODE
         SendInputData(j)\ki\time = 0
         SendInputData(j)\ki\wScan = char
      Wend
...
Another thing i noticed was that if you try to send the same character again, one after the other, only the first gets trough. So it's possible that you need to send a key_up event for that character to be able to send two in a row.

Before you begin sending you should use GetAsyncKeyState() to determine if any of the shift/ctrl keys are being pressed and compensate for this in the input stream you send. Otherwise their state might interfere with the new input you provide.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Here's a basic working sample:

Code: Select all

; Yet another useless program from netmaestro 
; because there will never be enough useless programs 

Procedure SendInput(*txt) 

  *idata = AllocateMemory(SizeOf(INPUT)*2) 
  Protected *inputdata.INPUT, onekey.INPUT 
  
  *key.CHARACTER = *txt 
  
  While *key\c 
    
    ; Translate the character to its virtual key code
    ; and see if it requires SHIFT to be pressed
    
    key.w = VkKeyScan_(*key\c) 
    keyvalue = key & $FF 
    shift    = key >> 8 
    
    ; Press SHIFT if indicated
    
    If shift 
      RtlZeroMemory_(onekey,SizeOf(INPUT)) 
      With onekey 
        \type   = #INPUT_KEYBOARD 
        \ki\wVk = #VK_SHIFT 
      EndWith 
      SendInput_(1, onekey, SizeOf(INPUT)) 
    EndIf 
    
    ; Press the character key down
    
    *inputdata = *idata 
    RtlZeroMemory_(*idata,SizeOf(INPUT)*2) 
    
    With *inputdata 
      \type   = #INPUT_KEYBOARD 
      \ki\wVk = keyvalue 
    EndWith 
    
    ; Release the character key
    
    *inputdata + SizeOf(INPUT) 
    
    With *inputdata 
      \type       = #INPUT_KEYBOARD 
      \ki\wVk     = keyvalue 
      \ki\dwFlags = #KEYEVENTF_KEYUP 
    EndWith 
    
    SendInput_(2, *idata, SizeOf(INPUT)) 

    ; Release the SHIFT key if we pressed it
    
    If shift 
      RtlZeroMemory_(onekey,SizeOf(INPUT)) 
      With onekey 
        \type       = #INPUT_KEYBOARD 
        \ki\wVk     = #VK_SHIFT 
        \ki\dwFlags = #KEYEVENTF_KEYUP          
      EndWith 
      SendInput_(1, onekey, SizeOf(INPUT)) 
    EndIf 
    
    ; Process next character if there is one
    
    *key+1 
  Wend      
EndProcedure 

RunProgram("notepad.exe") 
Delay(500) 
SendInput(@"Hello, this is a message from PureBasic, simply the finest programming tool in the world! ;)") 
BERESHEIT
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

> this link does not exist anymore

Here -> http://www.purebasic.fr/english/viewtopic.php?t=3766
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Post by Michael Vogel »

Thanks for all replies, they are all good (, but... :P)

I'd like to be able to do the following:

Code: Select all

SendInput("Hello..."+#CRLF$+"1)"+#TAB$+"äöüÖÄÜß"+#CRLF$+"2)"+#TAB$+"§@€µ{}"+#CRLF$+"3)"+#TAB$+"·•©"+#CRLF$)
...which should show the following on the screen (please paste it to the noteoad to see all chars correctly):

Code: Select all

Hello.
1)	äöüÖÄÜß
2)	§@€µ{}
3)	·•©
Pupils code does the following (one "l" is missing, the Euro sign and the big dot :shock: ):

Code: Select all

Helo.
1)	äöüÖÄÜß
2)	§@€µ{}
3)	·•©
I have also tiny problems with Netmaestros code with special chars (and Purebasic needs to use an extra variable for doing the call):cry:

Code: Select all

Hello...
1)	äöüÖÄÜß
2)	§QEM/=
3)	
PBs link shows the use of keybd_event_ (this is they way I did my code before, but it will make problems when the user presses shift or control during the execution :x)

Hm, Pupils code shows most of the chars and would be already much better than my attempt, but the "Helo" instead of "Hello"... :?
Pupil
Enthusiast
Enthusiast
Posts: 715
Joined: Fri Apr 25, 2003 3:56 pm

Post by Pupil »

For the 'l' missing i said that a key up event should be sent in case of two same chars following. this should fix the missin 'l':

Code: Select all

#KEYEVENTF_UNICODE = 4

Global Dim SendInputData.INPUT(0)

Procedure SendInput(Text.s)

   Protected i,j, k.l, newchar.l, char.l

   k=Len(Text)
   
   If k
      ReDim SendInputData(k)
      While j < k
         PokeS(@newchar, Chr(PeekB(@Text+i)), #PB_Unicode)
         i+1
         If newchar = char
            SendInputData(j)\Type = #INPUT_KEYBOARD
            SendInputData(j)\ki\wVk = 0
            SendInputData(j)\ki\dwExtraInfo = 0
            SendInputData(j)\ki\dwFlags = #KEYEVENTF_UNICODE|#KEYEVENTF_KEYUP
            SendInputData(j)\ki\time = 0
            SendInputData(j)\ki\wScan = char
            k+1 : j+1
            ReDim SendInputData(k)
         EndIf
         char = newchar
         SendInputData(j)\type=#INPUT_KEYBOARD
         SendInputData(j)\ki\wVk=0
         SendInputData(j)\ki\dwExtraInfo = 0
         SendInputData(j)\ki\dwFlags = #KEYEVENTF_UNICODE
         SendInputData(j)\ki\time = 0
         SendInputData(j)\ki\wScan = char
         j+1
      Wend
      
      SendInput_(k, SendInputData(0), SizeOf(INPUT))
   EndIf

EndProcedure

Delay(4000)

SendInput("Hello..."+#CRLF$+"1)"+#TAB$+"äöüÖÄÜß"+#CRLF$+"2)"+#TAB$+"§@€µ{}"+#CRLF$+"3)"+#TAB$+"·•©"+#CRLF$)
I just added what was needed to the code so I suppose one could tidy it up a bit, but at least it works as expected here.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Post by Michael Vogel »

Pupil wrote:For the 'l' missing i said that a key up event should be sent in case of two same chars following. [...]
Sorry for that - usually I read the postings before I write an answer :oops:

Your code is great - just playing around with it to see why some chars (e.g. Euro 0x80 and Middot 0x95) will produce a "?" at the output...

Code: Select all

Global Dim SendInputData.INPUT(0)

Procedure SendInput(Text.s)

	#KEYEVENTF_UNICODE = 4

	Protected i.l;	Pointer to Character in the Text
	Protected j.l;	Counter for SendInput
	Protected k.l;	Total Size for SendInput

	Protected Char.l
	Protected NewChar.l
	Protected KeyDown.l

	k=Len(Text)

	If k
		ReDim SendInputData(k)

		While j < k
			PokeS(@newchar,Chr(PeekB(@Text+i)),#PB_Unicode)

			SendInputData(j)\Type=#INPUT_KEYBOARD
			SendInputData(j)\ki\wVk=0
			SendInputData(j)\ki\dwExtraInfo=0
			SendInputData(j)\ki\time=0
			
			If (newchar=char) And KeyDown
				KeyDown=#False
				SendInputData(j)\ki\dwFlags=#KEYEVENTF_UNICODE|#KEYEVENTF_KEYUP
				k+1
				ReDim SendInputData(k)
			Else
				char=newchar
				KeyDown=#True
				SendInputData(j)\ki\dwFlags=#KEYEVENTF_UNICODE
				i+1
			EndIf
			SendInputData(j)\ki\wScan=char

			j+1
		Wend

		; Just for testing...
		RunProgram("notepad.exe")
		Delay(500)

		SendInput_(k, SendInputData(0), SizeOf(INPUT))

	EndIf

EndProcedure

Define x.s=""
Define i.l

For i=32 To 255
	x+RSet(Hex(i),2,"0")+":"+Chr(i)
	If i&15=15
		x+#CR$
		Else
		x+" "
	EndIf
Next i
SendInput(x)
Anyhow there's (again) one more problem - SendInput works fine on most applications without doing some changes, but...
...there's Excel and this @%§&-software does not accept even Chr(13) as a return :x
Okay, got this also, but still fail to create an Alt+Enter simulation to get more than one text line into one cell:

Code: Select all

Global Dim SendInputData.INPUT(0)

Procedure SendPrepare(n,VK,Char,UpDown,*Resize.l=#Null)
	SendInputData(n)\Type=#INPUT_KEYBOARD
	SendInputData(n)\ki\wVk=VK
	SendInputData(n)\ki\time=0
	SendInputData(n)\ki\dwExtraInfo=0
	SendInputData(n)\ki\wScan=Char
	SendInputData(n)\ki\dwFlags=UpDown
	If *Resize
		n=PeekL(*Resize)+1
		PokeL(*Resize,n)
		ReDim SendInputData(n)
	EndIf
EndProcedure

Procedure SendInput(Text.s)

	#KEYEVENTF_UNICODE = 4

	Protected i.l;	Pointer to Character in the Text
	Protected j.l;	Counter for SendInput
	Protected k.l;	Total Size for SendInput

	Protected Char.l
	Protected NewChar.l
	Protected KeyDown.l

	k=Len(Text)

	If k
		ReDim SendInputData(k)

		While j < k
			PokeS(@NewChar,Chr(PeekB(@Text+i)),#PB_Unicode)

			If (newchar=char) And KeyDown
				KeyDown=#False
				SendPrepare(j,0,Char,#KEYEVENTF_UNICODE|#KEYEVENTF_KEYUP,@k)
			Else
				char=newchar
				KeyDown=#True
				If char=13
					SendPrepare(j,#VK_MENU,0,#KEYEVENTF_EXTENDEDKEY,@k)
					SendPrepare(j,#VK_RETURN,0,#KEYEVENTF_EXTENDEDKEY)
					SendPrepare(j,#VK_MENU,0,#KEYEVENTF_KEYUP,@k)
				Else
					SendPrepare(j,0,Char,#KEYEVENTF_UNICODE)
				EndIf
				i+1
			EndIf

			j+1
		Wend

		SendInput_(k,SendInputData(0),SizeOf(INPUT))

	EndIf

EndProcedure

; Start Excel now...
Delay(4000)
SendInput("Hello..."+#CR$+"Where is this?!")

So there are still (at least) two open points...
1) is there a way to get the code working also with character codes from 0x80 to 0x9F?
2) how to do a Alt+Enter for the Excel application?

Thanks for your tips,
Michael
Last edited by Michael Vogel on Sun Jun 21, 2009 1:59 pm, edited 1 time in total.
Pupil
Enthusiast
Enthusiast
Posts: 715
Joined: Fri Apr 25, 2003 3:56 pm

Post by Pupil »

It seems I have to apologize as well, because I didn't check the output thoroughly enough myself. I missed that the euro character was not correctly sent. So I've investigated a bit further and found that if you replace the following line:

Code: Select all

PokeS(@newchar, Chr(PeekB(@Text+i)), #PB_Unicode)
With this line:

Code: Select all

MultiByteToWideChar_(#CP_ACP, 0, @Text+i, 1, @newchar, 1)
youll get better result. With this line replaced i get all the characters in your test string transmitted correctly through the SendInput function.

Regarding your problem with excel, have you tried using #VK_LMENU instead of #VK_MENU ? I haven't got excel installed so i can't test it myself.
One possible error could also be that you seem to overwrite the same item in the array when you try to send the alt+return combination. Try to replace with something like this:

Code: Select all

 ...
      If char=13
         SendPrepare(j,#VK_MENU,0,#KEYEVENTF_EXTENDEDKEY,@k) : j +1
         SendPrepare(j,#VK_RETURN,0,#KEYEVENTF_EXTENDEDKEY) : j+1
         SendPrepare(j,#VK_MENU,0,#KEYEVENTF_KEYUP,@k)
       Else
         SendPrepare(j,0,Char,#KEYEVENTF_UNICODE)
       EndIf
 ...
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Post by Michael Vogel »

Pupil wrote:...youll get better result...

Code: Select all

MultiByteToWideChar_(#CP_ACP, 0, @Text+i, 1, @newchar, 1)
...problem with excel... #VK_LMENU
THANK YOU :!:

Everything works now perfectly, brilliant work :wink:
Here is the complete code inclusive the option for excel as well

Code: Select all

; Define...

	#ExcelMode=#True

	EnableExplicit

	Structure Pointer
		value.l
	EndStructure

	Global Dim SendInputData.INPUT(0)
; EndDefine

Procedure SendPrepare(*n.Pointer,VK,Char,UpDown,*Resize.Pointer=#Null)
	SendInputData(*n\Value)\Type=#INPUT_KEYBOARD
	SendInputData(*n\Value)\ki\wVk=VK
	SendInputData(*n\Value)\ki\time=0
	SendInputData(*n\Value)\ki\dwExtraInfo=0
	SendInputData(*n\Value)\ki\wScan=Char
	SendInputData(*n\Value)\ki\dwFlags=UpDown
	*n\Value+1
	If *Resize
		*Resize\Value+1
		ReDim SendInputData(*Resize\Value)
	EndIf
EndProcedure

Procedure SendInput(Text.s)

	#KEYEVENTF_UNICODE = 4

	Protected i.l;	Pointer to Character in the Text
	Protected j.l;	Counter for SendInput
	Protected k.l;	Total Size for SendInput

	Protected Char.l
	Protected NewChar.l
	Protected KeyDown.l

	k=Len(Text)

	If k
		ReDim SendInputData(k)

		While j < k
			PokeS(@NewChar,Chr(PeekB(@Text+i)),#PB_Unicode)

			If (NewChar=Char) And KeyDown
				KeyDown=#False
				SendPrepare(@j,0,Char,#KEYEVENTF_UNICODE|#KEYEVENTF_KEYUP,@k)
			Else
				Char=NewChar
				KeyDown=#True

				Select Char
				Case #ESC
					SendPrepare(@j,#VK_ESCAPE,0,#KEYEVENTF_EXTENDEDKEY)
				Case #CR
					If #ExcelMode
						SendPrepare(@j,#VK_LMENU,0,#KEYEVENTF_EXTENDEDKEY,@k)
					EndIf
					SendPrepare(@j,#VK_RETURN,0,#KEYEVENTF_EXTENDEDKEY)
					If #ExcelMode
						SendPrepare(@j,#VK_LMENU,0,#KEYEVENTF_EXTENDEDKEY|#KEYEVENTF_KEYUP,@k)
					EndIf
				Default
					SendPrepare(@j,0,Char,#KEYEVENTF_UNICODE)
				EndSelect
				i+1
			EndIf

		Wend

		SendInput_(k,SendInputData(0),SizeOf(INPUT))

	EndIf

EndProcedure

; Start Excel now...
Delay(4000)
SendInput("Hello..."+#CR$+"Where is this?!")
kinglestat
Enthusiast
Enthusiast
Posts: 746
Joined: Fri Jul 14, 2006 8:53 pm
Location: Malta
Contact:

Re: API - SendInput

Post by kinglestat »

This is an interesting post
But it didn't solve my issue

Can I use SendInput to keep a key pressed for a time?
And can I send keys like cursor keys or ALT...or Control ?
I may not help with your coding
Just ask about mental issues!

http://www.lulu.com/spotlight/kingwolf
http://www.sen3.net
infratec
Always Here
Always Here
Posts: 7577
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: API - SendInput

Post by infratec »

Yes,

if you don't send a keyup, it stays pressed.

https://docs.microsoft.com/en-us/window ... -sendinput

You have to split your 'keystrokes' in several calls to SendInput_()
Post Reply