The NSWorkspace class provides a set of methods and properties which allow open and manipulate files, applications and others useful tasks. One of these properties can be used to list the running applications. The property is runningApplications , this will return an array of NSRunningApplication elements representing the running applications. Unfortunately the definition of this interface (NSRunningApplication) in the Macapi.AppKit is incomplete.
NSRunningApplication = interface(NSObject) ['{96F4D9CA-0732-4557-BA1F-177958903B8F}'] function activateWithOptions(options: NSApplicationActivationOptions): Boolean; cdecl; function activationPolicy: NSApplicationActivationPolicy; cdecl; function executableArchitecture: NSInteger; cdecl; function forceTerminate: Boolean; cdecl; function hide: Boolean; cdecl; function isActive: Boolean; cdecl; function isFinishedLaunching: Boolean; cdecl; function isHidden: Boolean; cdecl; function isTerminated: Boolean; cdecl; function processIdentifier: Integer; cdecl; function terminate: Boolean; cdecl; function unhide: Boolean; cdecl; end;
As you can see in the above definition there is not a property to retrieve the application name or path. So the first task in order to retrieve the list of the running applications is add the missing properties like so.
NSRunningApplicationEx = interface(NSObject) ['{96F4D9CA-0732-4557-BA1F-177958903B8F}'] function activateWithOptions(options: NSApplicationActivationOptions): Boolean; cdecl; function activationPolicy: NSApplicationActivationPolicy; cdecl; function executableArchitecture: NSInteger; cdecl; function forceTerminate: Boolean; cdecl; function hide: Boolean; cdecl; function isActive: Boolean; cdecl; function isFinishedLaunching: Boolean; cdecl; function isHidden: Boolean; cdecl; function isTerminated: Boolean; cdecl; function processIdentifier: Integer; cdecl; function terminate: Boolean; cdecl; function unhide: Boolean; cdecl; //Added functions(properties) //Indicates the URL to the application's executable. function executableURL : NSURL; cdecl;//@property (readonly) NSURL *executableURL; //Indicates the name of the application. This is dependent on the current localization of the referenced app, and is suitable for presentation to the user. function localizedName : NSString; cdecl;//@property (readonly) NSString *localizedName; //Indicates the URL to the application's bundle, or nil if the application does not have a bundle. function bundleURL : NSURL; cdecl;//@property (readonly) NSURL *bundleURL; //Indicates the CFBundleIdentifier of the application, or nil if the application does not have an Info.plist. function bundleIdentifier : NSString; cdecl;//@property (readonly) NSString *bundleIdentifier; //Indicates the date when the application was launched. This property is not available for all applications. Specifically, it is not available for applications that were launched without going through LaunchServices. */ function launchDate : NSDate;cdecl;//@property (readonly) NSDate *launchDate; //Returns the icon of the application. function icon : NSImage;cdecl;//@property (readonly) NSImage *icon; end; TNSRunningApplicationEx = class(TOCGenericImport<NSRunningApplicationClass, NSRunningApplicationEx>) end;
Now using a TStringGrid we can list all the user applications running
var LWorkSpace : NSWorkspace; LApp : NSRunningApplicationEx; LFormatter : NSDateFormatter; i : integer; LArray : NSArray; begin LWorkSpace:=TNSWorkspace.create;//or TNsWorkspace.Wrap(TNsWorkSpace.OCClass.sharedWorkspace); LArray:=LWorkSpace.runningApplications; //NSDateFormatter Class Reference //https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/Reference/Reference.html TNSDateFormatter.OCClass.setDefaultFormatterBehavior(NSDateFormatterBehavior10_4); LFormatter:=TNSDateFormatter.Create; LFormatter.setDateFormat(NSSTR('HH:mm:ss YYYY/MM/dd')); if LArray<>nil then begin StringGrid1.RowCount:=LArray.count; for i := 0 to LArray.count-1 do begin LApp:= TNSRunningApplicationEx.Wrap(LArray.objectAtIndex(i)); StringGrid1.Cells[0,i]:=LApp.processIdentifier.ToString(); if LApp.launchDate<>nil then StringGrid1.Cells[1,i]:=string(LFormatter.stringFromDate(LApp.launchDate).UTF8String); StringGrid1.Cells[2,i]:=string(LApp.localizedName.UTF8String); StringGrid1.Cells[3,i]:=string(LApp.executableURL.path.UTF8String); if LApp.bundleIdentifier<>nil then StringGrid1.Cells[4,i]:=string(LApp.bundleIdentifier.UTF8String); if LApp.bundleURL<>nil then StringGrid1.Cells[5,i]:=string(LApp.bundleURL.path.UTF8String); case LApp.executableArchitecture of NSBundleExecutableArchitectureI386 : StringGrid1.Cells[6,i]:='I386'; NSBundleExecutableArchitecturePPC : StringGrid1.Cells[6,i]:='PPC'; NSBundleExecutableArchitecturePPC64 : StringGrid1.Cells[6,i]:='PPC64'; NSBundleExecutableArchitectureX86_64: StringGrid1.Cells[6,i]:='X86_64'; end; end; end; end;
And this is the final result.
Note : The runningApplications property only list the user applications and does not provide information about every process on the system. In order to access to all the process you can use the sysctl function with the CTL_KERN, KERN_PROC, KERN_PROC_ALL values.
Download the sample Firemonkey project from Github.
July 18, 2013 at 11:07 pm
nice post,,, like this…
July 19, 2013 at 4:10 am
thanx, very useful post.
July 19, 2013 at 11:42 am
Thanks for your post. Keep up your good works. I need more experience in OSX developer.
July 20, 2013 at 10:41 am
Nice example. Is there a reason you don’t declare the properties as returning a NSURL, NSString or NSImage though…?
July 20, 2013 at 11:21 pm
Thanks Chris, apparently was published an early version of the code. now the code was updated.
March 3, 2014 at 4:19 pm
Very interesting!
I try to use getFileSystemInfoForPath:isRemovable:isWritable:isUnmountable:description:type in NSWorkspace. This function doesn’t exist in the Delphi part so i try to declare a NSWorkspaceEx with the original Nsworkspace plus
function getFileSystemInfoForPath(fullPath: NSString; out isRemovable, isWritable, isUnmountable :Boolean; description, Atype: PPointer): Boolean; cdecl;
All works iin execution until the call of this function where i had an “unrecognized selector sent to instance” error.
Any idea?
January 19, 2016 at 11:20 am
Nice!
The sample project isn’t available anymore in the DropBox link, can you upload it somewhere else please?
Thanks.
January 19, 2016 at 11:56 pm
Hi, the source code is on Github (https://github.com/RRUZ/blog/tree/master/OSX/List%20OSX%20Processes)