Best way to send keystrokes to a 3rd party application?

Glad it worked for others too. That’s what these threads are all about, so we can figure this out together!

@Beatrix_Willius I’m curious if that snippet might work for you too. It removes all permissions, not just Accessibility, so it might help with use of the xojo debugger.

In theory the reason why it worked first time and not the subsequent times is because of the code signature. A lot of these “Permissions” requires the application to use the same and valid code signature each run.

The App Intents system with Catalina can even block your app from accessing files that it accessed before if the code signature changes.

I would also assume that simulating key presses is considered a “Requires Permissions” things as reading the list of HID connected devices triggers the same permissions request, which can be denied by the customer. All I wanted to do was to ensure Bluetooth isn’t disabled if the customer doesn’t have a wired input device connected, but instead it put some people off my app because of the fearmongering “App might be monitoring keystrokes warning”. pfffttt

1 Like

I’m familiar with tccutil reset. I use it for testing because I have screwed up permissions when making a final build before. Just for debugging I need a built app already on the hard disk.

1 Like

Here are the methods I ultimately came up with. They should work cross-platform on both Windows and Mac, thanks to MBS. Hope these help others. Apologies for all the edits, I originally copied this from the wrong file. :man_facepalming:t2:

Public Function AppActivate(Target As String) As Boolean
  //Brings a window to the front. Returns true if successful, false if app is not running.
  //On MacOS, Target is a bundle ID "com.apple.textedit"
  //On Windows it is an EXE name "notepad.exe"
  
  
  #If TargetMacOS Then
    var n() as NSRunningApplicationMBS = NSRunningApplicationMBS.runningApplicationsWithBundleIdentifier(target)
    if n.count = 0 then
      //App is not running
      Return false
    end if
    
    var u as integer, b as boolean = n(0).activateWithOptions(u)
    Return b
    
  #Else
    
    dim p as ProcessMBS
    p=new ProcessMBS
    p.GetfirstProcess ' get first
    var pid as Integer = -1
    do
      if p.Name = Target Then
        pid = p.ProcessID
        Exit
      end if
    loop until not p.GetNextProcess ' get next till no more
    
    if pid = -1 then Return False
    Return RemoteControlMBS.WinBringProcessToTop(pid)
    
  #EndIf
  
  
End Function

Public Function SendKeys(S as String) As Boolean
  //Sends keys to the foreground. This will only work for ASCII values <= 128, and does not
  //support function keys (Cmd/Ctrl/Alt) other than Shift on Windows. If you need this support you'll
  //have to add it yourself. You can, however, send the ENTER/RETURN key with Chr(13). 
  //
  //On MacOS you will need to have Accessibility Permissions from the user. The user will be prompted the first
  //time you call this, and the method will fail, returning false.
  
  
  #If TargetMacOS Then
//Set up a keycodes dictionary
    
var KeyCodes as New Dictionary
for i as integer = 0 to 127
KeyCodes.Value(RemoteControlMBS.MacTextForKeyCode(i, 0, 0)) = i
next


    // Initialize an empty array to store the integer values
    Dim intArray() As Integer, success as boolean
    
    // Iterate over each character in the string S
    For i As Integer = 0 To S.Length - 1
      
      var char as String = S.Middle(i, 1)
      if keyCodes.HasKey(Char) then success = RemoteControlMBS.MacPressKey(Asc(Char), keyCodes.Value(Char).IntegerValue)
      SleepMBS 0.01
    Next
    
    Return Success
    
    
  #Else
    //Windows
    
    Dim intArray() As Integer, success as Boolean
    
    
    // Iterate over each character in the string S
    For i As Integer = 0 To S.Length - 1
      
      var char as String = S.Middle(i, 1)
      var asciiValue as Integer = Asc(char)
      
      // Inline VirtualCode calculation
      Dim shift, control, alt As Boolean
      Dim vk As Integer
      If RemoteControlMBS.WinVirtualKeyForASCII(asciiValue, vk, shift, control, alt) Then
        // Now vk contains the virtual key code for the character
      Else
        // Handle error or unsupported character
        Continue
      End If
      
      // Inline ScanCode calculation
      Dim scanCode As Integer = RemoteControlMBS.WinVirtualKeyCodeToScanCode(vk)
      
      // Use vk and scanCode for the keystroke
      
      if shift then Call RemoteControlMBS.WinPressShiftKey True  //Handle shift key otherwise ! comes out as 1
      Call RemoteControlMBS.WinPressKey(vk, scanCode)
      if Shift then Call RemoteControlMBS.WinPressShiftKey False
      SleepMBS 0.01
    Next
    
    Return success
    
    
  #EndIf
End Function

3 Likes