[Windows] SendInput_() and the [Alt] key

Just starting out? Need help? Post your questions and find answers here.
Little John
Addict
Addict
Posts: 4803
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

[Windows] SendInput_() and the [Alt] key

Post by Little John »

Hi,

I have a program that useses SendInput_() for sending strings to windows of 3rd party programs. That works fine so far.
However, I want to send different strings, depending on whether or not the user presses [Shift], [Ctrl] or [Alt] while clicking with the mouse at the respective button.

The following code demonstrates the situation. It works as expected, except with [Alt]: When the [Alt] key is pressed while klicking at the button, no text is sent to the editor gadget.
Microsoft wrote: This function does not reset the keyboard's current state. Any keys that are already pressed when the function is called might interfere with the events that this function generates. To avoid this problem, check the keyboard's state with the GetAsyncKeyState function and correct as necessary.
So how to “correct as necessary”? How can I make the code work also when [Alt] is pressed?

Code: Select all

; PB 6.12 LTS

EnableExplicit

#KEYEVENTF_UNICODE = 4

Procedure.i SendString (s$)
   ; -- sends a Unicode string to the foreground window
   ; in : s$: Unicode-String (may contain characters outside of the BMP)
   ; out: number of events that were successfully inserted into the input stream
   ;
   ; <https://stackoverflow.com/questions/50420514/sendinput-wont-send-basic-unicode-to-some-windows>
   Protected.Character *char, *char2
   Protected.i i=0, nEvents=2*Len(s$)
   
   If OSVersion() < #PB_OS_Windows_2000 Or nEvents = 0
      ProcedureReturn 0
   EndIf
   
   Protected Dim send.Input(nEvents-1)
   *char = @s$
   
   While *char\c <> 0
      If *char\c < $D800 Or *char\c > $DFFF
         send(i)\type = #INPUT_KEYBOARD
         send(i)\ki\wVk = 0
         send(i)\ki\wScan = *char\c
         send(i)\ki\dwFlags = #KEYEVENTF_UNICODE  ; Mit diesem Flag muss \wVk = 0 sein.
         i + 1
         
         send(i)\type = #INPUT_KEYBOARD
         send(i)\ki\wVk = 0
         send(i)\ki\wScan = *char\c
         send(i)\ki\dwFlags = #KEYEVENTF_UNICODE | #KEYEVENTF_KEYUP
         i + 1
         
      Else
         *char2 = *char + SizeOf(Character)
         
         send(i)\type = #INPUT_KEYBOARD
         send(i)\ki\wVk = 0
         send(i)\ki\wScan = *char\c
         send(i)\ki\dwFlags = #KEYEVENTF_UNICODE
         i + 1
         
         send(i)\type = #INPUT_KEYBOARD
         send(i)\ki\wVk = 0
         send(i)\ki\wScan = *char2\c
         send(i)\ki\dwFlags = #KEYEVENTF_UNICODE
         i + 1
         
         send(i)\type = #INPUT_KEYBOARD
         send(i)\ki\wVk = 0
         send(i)\ki\wScan = *char\c
         send(i)\ki\dwFlags = #KEYEVENTF_UNICODE | #KEYEVENTF_KEYUP
         i + 1
         
         send(i)\type = #INPUT_KEYBOARD
         send(i)\ki\wVk = 0
         send(i)\ki\wScan = *char2\c
         send(i)\ki\dwFlags = #KEYEVENTF_UNICODE | #KEYEVENTF_KEYUP
         i + 1
         
         *char = *char2
      EndIf
      
      *char + SizeOf(Character)
   Wend
   
   ProcedureReturn SendInput_(nEvents, @send(), SizeOf(Input))
EndProcedure


Macro IsAsyncKeyDown (_VK_)
   Bool((GetAsyncKeyState_(_VK_) & $8000) = $8000)
EndMacro


;-- Test
Enumeration
   #WinMain
EndEnumeration

Enumeration
   #edtGadget
   #btnGadget
EndEnumeration


Define event.i, out$

If OpenWindow(#WinMain, 100, 100, 200, 140, "Demo") = 0
   MessageRequester("Fatal error", "Can't open main window", #PB_MessageRequester_Error)
   End
EndIf

EditorGadget(#edtGadget, 20, 30, 160, 25)
ButtonGadget(#btnGadget, 60, 80,  80, 25, "Klick")

Repeat
   event = WaitWindowEvent()
   
   Select event
      Case #PB_Event_Gadget
         Select EventGadget()
            Case #btnGadget
               out$ = "Text"
               If IsAsyncKeyDown(#VK_SHIFT)
                  out$ + "+Shift"
               EndIf   
               If IsAsyncKeyDown(#VK_CONTROL)
                  out$ + "+Ctrl"
               EndIf   
               If IsAsyncKeyDown(#VK_MENU)
                  out$ + "+Alt"
               EndIf   
               
               ClearGadgetItems(#edtGadget)
               SetActiveGadget(#edtGadget)
               SendString(out$)
         EndSelect
   EndSelect
Until event = #PB_Event_CloseWindow
PeDe
Enthusiast
Enthusiast
Posts: 305
Joined: Sun Nov 26, 2017 3:13 pm

Re: [Windows] SendInput_() and the [Alt] key

Post by PeDe »

I assume that as long as the Alt key is pressed, the system waits for another keystroke to activate a corresponding menu. If you send a space, for example, the system menu of the test window opens briefly. It is sometimes only visible for a very short time. The keys sent are therefore used for a menu, if available, or swallowed. This is just a simple idea of mine.

Peter

Code: Select all

               If IsAsyncKeyDown(#VK_MENU)
                  ;out$ + "+Alt"
                  out$ = " "
               EndIf   
Little John
Addict
Addict
Posts: 4803
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: [Windows] SendInput_() and the [Alt] key

Post by Little John »

PeDe wrote: Fri Oct 04, 2024 7:59 pm I assume that as long as the Alt key is pressed, the system waits for another keystroke to activate a corresponding menu. If you send a space, for example, the system menu of the test window opens briefly. It is sometimes only visible for a very short time. The keys sent are therefore used for a menu, if available, or swallowed. This is just a simple idea of mine.

Peter

Code: Select all

               If IsAsyncKeyDown(#VK_MENU)
                  ;out$ + "+Alt"
                  out$ = " "
               EndIf   
Sorry, I can't see how this helps to solve the issue.
Mesa
Enthusiast
Enthusiast
Posts: 447
Joined: Fri Feb 24, 2012 10:19 am

Re: [Windows] SendInput_() and the [Alt] key

Post by Mesa »

ALT+CLIC

Code: Select all

Enumeration
  #Win
  #Btn
  #Txt
EndEnumeration


Procedure Win()
  
  If OpenWindow(#Win,0,0,150,150,"Win",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
    
    TextGadget(#Txt,  50, 20, 50, 25, "0", #PB_Text_Center|#PB_Text_Border)
    ButtonGadget(#Btn,  50, 70, 50, 50, "Btn")
    
  EndIf
  
EndProcedure

Win()

iVal = 1

Repeat
  iEvent = WaitWindowEvent()
  Select iEvent
      
    Case #PB_Event_Gadget
      
      If(EventGadget() = #Btn);ALT+CLIC
        If GetAsyncKeyState_(#VK_MENU)&32768;If GetAsyncKeyState_(#VK_CONTROL)&32768
          ival=100
        Else
          ival=1
        EndIf
        SetGadgetText(#Txt,Str(iVal))
      EndIf
      
  EndSelect
  
Until iEvent = #PB_Event_CloseWindow

End
Little John
Addict
Addict
Posts: 4803
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: [Windows] SendInput_() and the [Alt] key

Post by Little John »

@Mesa:
As written in the first post, I want to use SendInput_() together with the [Alt] key.
Your code does not use SendInput_() at all ...
Of course I know about SetGadgetText(), but that's not the point here.
infratec
Always Here
Always Here
Posts: 7662
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: [Windows] SendInput_() and the [Alt] key

Post by infratec »

You have to check if ALT (VK_MENU) is pressed,
Or generally release it in your case:

Code: Select all

Protected Dim send.Input(nEvents)
   *char = @s$
   
   send(i)\type = #INPUT_KEYBOARD
   send(i)\ki\wVk = #VK_MENU
   send(i)\ki\wScan = 0
   send(i)\ki\dwFlags = #KEYEVENTF_KEYUP
   i + 1
   
   
   While *char\c <> 0
But as written, you shiould check if it is pressed, then release it
ansd press it at the end again.

But your program works with this fix.
Little John
Addict
Addict
Posts: 4803
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: [Windows] SendInput_() and the [Alt] key

Post by Little John »

Thank you, infratec!
Post Reply