This single method will return the name of the front app. If the user renames an app this method still returns the original name. For example, I’ve renamed “Xojo” to “Xojo 14r1” but this will still return “Xojo”. I don’t know if the name will change in different languages though.
If the name changes by language a better way to identify an app may be the bundleIdentifier. That’s the thing that looks like “com.xojo.xojo” or “com.apple.finder”. Switch the commented declare and return lines to get the bundleID.
[code]Private Function frontMostApp() As String
const CocoaLib = “Cocoa.framework”
declare function NSClassFromString lib CocoaLib (aClassName as CFStringRef) as Ptr
declare function sharedWorkspace lib CocoaLib selector “sharedWorkspace” (class_id as Ptr) as Ptr
declare function frontmostApp lib CocoaLib selector “frontmostApplication” (inst_id As Ptr) As Ptr
declare function getLocName lib CocoaLib selector “localizedName” (inst_id As Ptr) As CFStringRef
//declare function bundleID lib CocoaLib selector “bundleIdentifier” (inst_id As Ptr) As CFStringRef
dim workspaceCls, workspace, frontRunningApp As Ptr
workspaceCls = NSClassFromString(“NSWorkspace”)
workspace = sharedWorkspace(workspaceCls)
frontRunningApp = frontmostApp(workspace)
return getLocName(frontRunningApp)
//return bundleID(frontRunningApp)
End Function[/code]
With just this method you can setup a Timer getting the name every second or so and watch for changes. A lighter weight (and more involved) way is to install a notification center observer for ‘did activate’ events. This module does that for a single event type and callback. First you call install with the method you want to be triggered when a new app is brought front. You should call unregister when done with.
How it works. install() first dynamically creates an NSObject subclass and adds a method that’s implemented by the Xojo method HandleActivateNotify. Then an instance of this class is made and added to notification center as an observer of the did activate event. When the OS sends those events handleActivateNotify gets triggered which simply calls the delegate. removeObserver() simply tells notification center as much and then nils its values.
[code]Module ActivateNotifier
Private Property refInstanceid As Ptr
Private Property refCenter As Ptr
Private Property notifyHandler As delNotifyHandler
Private Delegate delNotifyHandler(notifyEvent As Ptr)
Protected Sub install(handler As delNotifyHandler)
const CocoaLib = “Cocoa.framework”
declare function NSClassFromString lib CocoaLib (aClassName as CFStringRef) as Ptr
declare function NSSelectorFromString lib CocoaLib (aSelectorName as CFStringRef) as Ptr
declare function objc_allocateClassPair lib CocoaLib _
(superclass as Ptr, name as CString, extraBytes as Integer) as Ptr
declare sub objc_registerClassPair lib CocoaLib (cls as Ptr)
declare function class_addMethod lib CocoaLib _
(cls as Ptr, name as Ptr, imp as Ptr, types as CString) as Boolean
declare function alloc lib CocoaLib selector “alloc” (classRef as Ptr) as Ptr
declare function init lib CocoaLIb selector “init” (id as Ptr) as Ptr
declare function sharedWorkspace lib CocoaLib selector “sharedWorkspace” (class_id as Ptr) as Ptr
declare function notificationCenter lib CocoaLib selector “notificationCenter” (inst_id as Ptr) as Ptr
declare sub addObserver lib CocoaLib selector “addObserver:selector:name:object:” _
(obj_id as Ptr, notificationObserver as Ptr, notificationSelector as Ptr, _
notificationName as CFStringRef, notificationSender as Ptr)
notifyHandler = handler //store
dim callbackSelector As Ptr = NSSelectorFromString("HandleActivateNotify:")
//dynamically create class with Xojo implementing a method
dim classRef As Ptr = objc_allocateClassPair(NSClassFromString("NSObject"), "MyActivateNotifierxyz", 0)
objc_registerClassPair(classRef)
if not class_addMethod(classRef, callbackSelector, AddressOf handleActivateNotify, "v@:@") then break
//instantiate one
refInstanceid = init(alloc(classRef))
//link this instance to be notified of activated apps
refCenter = notificationCenter(sharedWorkspace(NSClassFromString("NSWorkspace")))
addObserver(refCenter, refInstanceid, callbackSelector, _
"NSWorkspaceDidActivateApplicationNotification", nil)
End Sub
Private Sub handleActivateNotify(id as Ptr, sel as Ptr, notification as Ptr)
#pragma unused id
#pragma unused sel
notifyHandler.Invoke(notification)
End Sub
Protected Sub unregister()
if refCenter = nil then return
const CocoaLib = "Cocoa.framework"
declare sub removeObserver lib CocoaLib selector "removeObserver:" _
(obj_id as Ptr, notificationObserver as Ptr)
declare sub release lib CocoaLib selector "release" (id as Ptr)
removeObserver(refCenter, refInstanceid)
release(refInstanceid)
refInstanceid = nil
refCenter = nil
End Sub
End Module[/code]
Here’s a demo. Add this code to a window along with the frontMostApp method and a TextArea. And add the ActivateNotifier Module. Run and switch apps.
[code]Sub Open()
ActivateNotifier.install(AddressOf anAppActivated)
End Sub
Private Sub anAppActivated(notifyEvent As Ptr)
#pragma unused notifyEvent
TextArea1.AppendText "just activated: " + frontMostApp + EndOfLine
End Sub
Sub Close()
ActivateNotifier.unregister
End Sub[/code]
notes: I think this is 10.7+ only. Very little error checking. Hard coded constants. When anAppActivated is triggered it gets an event object ptr containing the app in question. This code is ignoring that object and just getting the name through frontMostApp().