Page 1 of 1

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

Posted: Fri Oct 04, 2024 2:35 pm
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

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

Posted: Fri Oct 04, 2024 7:59 pm
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   

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

Posted: Sat Oct 05, 2024 8:48 am
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.

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

Posted: Sat Oct 05, 2024 11:10 am
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

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

Posted: Sat Oct 05, 2024 11:22 am
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.

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

Posted: Sat Oct 05, 2024 12:52 pm
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.

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

Posted: Sat Oct 05, 2024 3:35 pm
by Little John
Thank you, infratec!