Can Xojo Detect Another Application Open

Is there a way that a Xojo app can detect that another application (Excel or Photoshop, for example) is currently open or not open on a Mac?

Public Function ListOfRunningApps(backgroundOnly as Boolean = true) as String()
  'osascript -e 'tell application "System Events" to get name of (processes where background only is false)'
  
  Dim res() As String
  
  #If TargetMacOS
    
    Dim s As New Shell
    If s<>Nil Then
      If backgroundOnly Then
        s.Execute("osascript -e 'tell application ""System Events"" to get name of (processes where background only is true)'")
      Else
        s.Execute("osascript -e 'tell application ""System Events"" to get name of (processes where background only is false)'")
      End If
      If s.ErrorCode=0 Then
        res = s.Result.ReplaceAll(" ","").ToListOfStrings
      End If
    End If
    
  #EndIf
  
  Return res
  
End Function

with

Public Function ToListOfStrings(extends aString as String, aSeparator as string=",") as string()
  
  dim i,j as Integer
  dim res() as string
  
  j = CountFields(aString,aSeparator)
  for i=1 to j
    res.Append NthField(aString,aSeparator,i)
  next
  
  Return res
  
End Function
4 Likes

Thanks, Jean, for jumping in. I will give this a try; I hope today. If it works for me, I will label it as the Solution.

Another possibility using Declares:

Public Function ApplicationIsRunning(bundleID as string) As Boolean
  Declare Function NSClassFromString Lib "Foundation" (name As cfstringref) As ptr
  Declare Function getCount Lib "Foundation" Selector "count" (obj As ptr) As Integer
  Declare Function runningApplicationsWithBundleIdentifier Lib "Foundation" Selector "runningApplicationsWithBundleIdentifier:" ( cls As ptr , bundleIdentifier As CFStringRef ) As Ptr
  
  Dim NSRunningApplication As ptr = NSClassFromString("NSRunningApplication")
  Dim runningApps As ptr = runningApplicationsWithBundleIdentifier(NSRunningApplication, bundleID)
  
  Dim c As Integer = getCount(runningApps)
  
  Return c > 0
End Function

Usage:

Dim isPhotoshopRunning As Boolean = ApplicationIsRunning("com.adobe.photoshop")

Dim isFinderRunning As Boolean = ApplicationIsRunning("com.apple.finder")

Dim isExcelRunning as Boolean = ApplicationIsRunning("com.microsoft.excel")
8 Likes

Jean’s code does what I need. If anyone is curious about slight modification, in my version below I replaced the ToListOfStrings code with the now available method in Xojo: String.Split which modernizes his code a bit. In this context, ToListOfStrings is no longer necessary.

Greg, I will try Declares in the future for my interest. I have not used Declares and am therefore not very familiar with them

Protected Function ListRunningApps(backgroundOnly as Boolean = True) As String()
  Var res() As String
  
  #If TargetMacOS
    
    Var s As New Shell
    If s <> Nil Then
      If backgroundOnly Then
        s.Execute("osascript -e 'tell application ""System Events"" to get name of (processes where background only is true)'")
      Else
        s.Execute("osascript -e 'tell application ""System Events"" to get name of (processes where background only is false)'")
      End If
      If s.ErrorCode = 0 Then
        res = s.Result.ReplaceAll(" ", "").Split(",")
      End If
    End If
    
  #EndIf
  
  Return res
End Function

Is the function for private or public code? In case of public code then your users will see the following message:

Why do you do a nil check after newing the shell? That’s superfluous.

would be nice if someone could provide the same for windows and/or linux ?

does the code from Greg with declares prevent from showing this warning dialog ?

paranoia … :wink:

Gregs code doesn’t use AppleScript so it won’t show the dialog.

Is it not true that that warning message will ONLY run the first time you use the app. After that, you are no longer bothered?

I am still living in the world of Mojave so I do not know the depths of security harassment the current OS imposes.

Yes, that’s true. But if your users don’t know what the message is about they will deny the permissions.

Well, I tried Greg’s suggestion and “surprise” it works. It is very fast and directly answers my original question although Jean’s proposal provides a lot of additional information that I can find useful in other situation. It is nice that the Greg approach does not popup the warning.

The real point for me is that I don’t have to understand Declares to use Greg’s code. I just copy the code. :slight_smile:

It is a slight nuisance to actually find the bundleID of whatever application you are interested in, but you can use Terminal to find it and the required command can be googled.

Example: osascript -e ‘id of app “Microsoft Word”’

Will return: com.microsoft.Word

One little warning: the “case” of the bundleID is important. com.microsoft.Excel and com.microsoft.excel are not the same. The former is correct and you have to supply the correct bundleID to get the right answer. So Greg’s example will not work properly as typed.

Sorry. Don’t have excel here to test with so I guessed. Every other bundle ID I’ve come across has been all lower case.

The docs is also case sensitive:

http://documentation.xojo.com/Desktopbutton
found nothing while

http://documentation.xojo.com/api/user_interface/desktop/desktopbutton.html
returns information about PushButton on Xojo 2021r3.1…

One potential problem with the Greg O solution is that it is not always easy in my experience to find out the bundleID. For the regular frontmost applications, this seems to be relatively straightforward. But relevant for me, in my use case, is trying to determine if KeyboardMaestroEngline is running.

Using the Jean YP technique, when KeyboardMaestroEngline in fact is running, I can see it listed in the background application list. From this, I can ultimately write the code to determine whether this particular app is running. But I cannot figure out what the bundleID of KeyboardMaestroEngline is and therefore cannot apply the Greg O technique to answer the question.

I would be interested if there were a way to get a list of the “bundleIDs” of the applications running rather than simply their names so I could figure this out for KeyboardMaestroEngline and perhaps other background applications should the need arise. Some of the background applications listed look like they are, in fact, the bundleIDs (com.apple.Safari.SandboxBroker) but KeyboardMaestroEngline – and I would assume others – are not actual bundleIDs.

The bundle identifier for Keyboard Maestro Engine.app is com.stairways.keyboardmaestro.engine

I would really recommend researching how declares work or investing in MBS plugins. In this modern day, I would suggest an answer employing Apple Script is the wrong answer (because of the usability and macOS security issues).

Hopefully this bundle identifier helps, if there’s anything else I can do to get you to flag Greg’s declares as the answer and not use Apple Script please let me know!

2 Likes

That is great and solves my immediate problem. But how did you figure this out?

I would suggest an answer employing Apple Script is the wrong answer (because of the usability and macOS security issues).

This must be a big topic. I hear whispers about the problems with AppleScript, and it is not pleasant to use but I find myself “having” to use it for this and that. And it does not seem to ever actually “go away.” If you know of some great resources to read about this whole topic and what it means and what is the future of AppleScript and why it is such a security problem and can that problem be solved etc. etc. I would read it. I sense that this all remains murky. Sadly in my current project, I do end up using AppleScript. Not a lot but at critical junctions.

Hopefully this bundle identifier helps, if there’s anything else I can do to get you to flag Greg’s declares as the answer and not use Apple Script please let me know!

I am sometimes conflicted as what to flag as the solution when two people provide different solutions. I might like one over the other but I have felt that I should be crediting the first viable answer. Is it even possible to change which answer you credit as the solution after the fact?

“how Declares work” seems almost to be like opening a Pandora box into another language altogether.

Thanks for your help (and concern :woozy_face:)

So the MacOS SDK offers a single method for d finding out if an application is running if you know the bundle identifier which I used above.

If you need to find by name, It would be fairly simple to get an array of all running apps and then look for a particular name, but there’s nothing that prevents a user from renaming an app, which could result in a false positive or negative. Heck, Adobe® itself could rename Photoshop to Photoshopper and your app would break, but the chance that they’d change the bundle ID is pretty slim.

1 Like

You open the app bundle and check the plist.

As soon as you update your computer to at least Crapolina you can experience all the joys of the stupid security yourself. The security feels like we are all trained as Pavlovian dogs to click and click and click. The computer doesn’t trust itself anymore.

The declares are for persons who know Objective-C. I don’t have time for that. Buying and using the MBS plugin is much easier.

1 Like