Check if a process is a windowed process in OSX - objective-c

Given the PID of a process, how can I check if it's a windowed process? I am looking for a solution on OSX.
Objective-C/ C solutions are wanted.
I am on OSX Mountain Lion.
I am defining a windowed process as a process that can be seen when the "Windowed Processes" tab is selected in activity monitor. http://cl.ly/FeXR

[NSWorkspace runningApplications]
This returns all the user applications in a NSArray as instances of NSRunningApplication which has a property processIdentifier.
Or you can use runningApplicationWithProcessIdentifier: directly to get the application with a specified PID.
Here is the sample code: http://developer.apple.com/library/Mac/#samplecode/AppList/Introduction/Intro.html
Through the activationPolicy property of the NSRunningApplication you could get the Windowed state. I modified the AppList code to make it show only the windowed applications as shown below. This matches what the activity monitor shows.
In particular, you are looking for the NSApplicationActivationPolicyRegular.
Note the restrictions however. This only works for user owned processes.
All other methods are deprecated and may not work in future. Methods like GetBSDProcessList and Carbon's GetProcessInformation are not recommended anymore.

Related

What exactly should I pass to -[NSApp activateIgnoringOtherApps:] to get my application to start "naturally" in comparison to most other OS X apps?

When I learned how to start NSApplications on my own, the code I used (based on here and here) did
[NSApp activateIgnoringOtherApps:YES];
which forces the app to the front at startup.
I'd like to know what most other apps do. I want to be able to run programs both directly from the binary and from an app bundle, and I'm not using Xcode to build this (raw building). So I'd rather this act naturally, so to speak.
The docs do say Finder issues NO, but... why Finder? Isn't this a method that's run from within the process, not outside? (I'm not in control of the choice.) And what about the Dock and other possible entry points?
I even went so far as to disassemble 10.8's NSApplicationMain() to see what it did, but as far as I can tell from the 32-bit version, unless this "light launch" thing issues this selector, this selector is never called.
Is there an answer to this question? Thanks... and sorry if this is confusing; I tried to word it as clearly as possible.
Apps normally do not call -activateIgnoringOtherApps: at all. And, generally speaking, shouldn't. Certainly, it wouldn't be in NSApplicationMain(), which is too early and fairly distantly related to actual app start-up.
Apps are normally launched by Launch Services (which is what is used by the Finder, the Dock, and /usr/bin/open, as well as any other app that might open yours or a document which yours handles). Roughly what happens is that Launch Services deactivates the app which called it to open something else and then, in the launched app, Cocoa's internals do something like (but not necessarily identical to) [NSApp activateIgnoringOtherApps:NO]. In this way, the launched app only activates if nothing else was activated in the interval between those two events. If that interval is long (because something was slow) and the user switched to something else in the meantime, you don't want to steal focus from whatever they switched to.
You should only call [NSApp activateIgnoringOtherApps:YES] in response to a user request to activate your app in a context which won't include the automatic deactivation of the current app by Launch Services. For example, if you have a command-line program which transforms itself into a GUI app (using -[NSApplication setActivationPolicy:] or the deprecated TransformProcessType()), then the user running that tool means they want it active. But Terminal is active and won't be deactivated spontaneously just by virtue of having run your program. So, the program has to steal focus.
If your program is a bundled app, then running it from the command line should be done with /usr/bin/open rather than directly executing the executable inside the bundle. Then, you don't need to call -activateIgnoringOtherApps: at all and the question of what value to pass is moot.

Which functions produce the "would like to access your contacts" window in MacOS 10.8?

I don't use the ABAddressBook framework at all. I have no clues why my application want to access the contacts? There are no exceptions, or useful log data...
There isn't any documentation I know of. I would try to set a breakpoint in the debugger on +addressBook and +sharedAddressBook of the ABAddressBook class and the relevant C functions such as ABGetMe and ABGetSharedAddressBook.
Furthermore, stop the app in the debugger as soon as the dialog appears and investigate all threads for suspicious function calls because OS X will be blocking the the call until the user has click one of the options.
In a related post in the Apple Developer Forums, I've read that the InstantMessage framework for iChat Theater support was the culprit in that case.

Uniquely identify active window on OS X

I’m trying to patch an application that resizes windows using the accessibility API.
I need to maintain a dictionary with the previous sizes of windows. The key needs to identify the currently active window. At the moment, this active window is retrieved via NSAccessibilityFocusedWindowAttribute upon the press of a hotkey.
However, every time this method is called, the returned AXUIElementRef which identifies the window is different! This of course means that I cannot use it as a dictionary key – the dictionary won’t find the corresponding entry.
The following code reproduces the problem:
-(IBAction)testWindowIdentification:(id)sender{
AXUIElementRef focusedApp;
AXUIElementRef focusedWindow;
AXUIElementCopyAttributeValue(_systemWideElement,
(CFStringRef) kAXFocusedApplicationAttribute,
(CFTypeRef*) &focusedApp);
AXUIElementCopyAttributeValue((AXUIElementRef) focusedApp,
(CFStringRef) NSAccessibilityFocusedWindowAttribute,
(CFTypeRef*) &focusedWindow);
CFShow(focusedWindow);
}
_systemWideElement has been initialised in the init method using a call to AXUIElementCreateSystemWide().
The CFShow statement clearly shows different IDs every time the method is called (even though the same window is active), which is useless for me:
<AXUIElement 0x47e850> {pid=42463}
<AXUIElement 0x47e890> {pid=42463}
<AXUIElement 0x47e2c0> {pid=42463}
…
The documentation on AXUIElement shows no method that retrieves a unique attribute for the UI element, and neither does that of the NSAccessibility protocol.
The unique PID is not enough for me, since a process can have multiple windows.
How can I retrieve some unique identifier of the active window in Cocoa?
(By the way, the real code is checking the return codes in the above calls; there is no error, the calls succeed.)
Rob Keniger has the right strategy with his answer here. The only thing missing from this answer (and indeed, the reason for the bounty placement) is a workable implementation that takes the current active window and translates it into a unique key suitable for indexing in the context of the current working application.
Rob's solution sketches this out through use of the CGWindowID given in the context of Quartz Window Services. It is, of course, strongly implied that this window reference is only useful for your current application.
Getting this window reference is tricky, because no strong guarantees exist between the Accessibility API and Quartz Window Services. However, you can work around this in the following ways:
Use extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);, as documented here. This isn't guaranteed to work, but it works as a ground-floor test to get things started if it works in your version of OSX.
Get the CGWindowID directly, using, for example, HIWindowGetCGWindowID(). More details about selecting the active window and extracting the ID can be found in the reference manual for the Carbon Window Manager (warning: large PDF).
Catalog your CGWindowID set using something like CGWindowListCreateDescriptionFromArray, exactly as Rob suggested. The goal here is then to find some scheme for bridging the Accessibility API and Quartz, but this is conceivable by utilizing, for example, a callback bound to the context of your current active window. I honestly don't know an optimal example of this that's properly future-proofed, however.
Of the options, I recommend going with 2. for your current needs, if you're unable to create some other decorator for your windows to uniquely identify them. It's currently defined in the legacy code base, but it will do what you desire.
Best of luck with your application.
I think you might be able to use the Quartz Window Services functions, specifically CGWindowListCreateDescriptionFromArray to enumerate the currently active windows in a particular app.
This call is lower-level than AppKit and isn't going to tell you which is the active window, but it will give you window IDs that are unique for the current user session. It's not a great solution, but you could compare the window bounds information with what you receive from the accessibility APIs to associate windows with their real IDs.

Checking whether spoken commands are enabled?

Is there a way to check whether the user has enabled speech recognition (spoken commands) in System Preferences? (Mac OS X). If the user has it enabled, I would like to support additional speech commands. Unfortunately there isn't any method in NSSpeechRecognizer to check this and I can't seem to find any Carbon functions to check it either.
One of the problem is that the round Speech Commands window (the one with a microphone on it) seems to appear intermittently whenever I instantiate NSSpeechRecognizer. Also it often freezes my app for about half a second or so while the object is created (probably it's starting up the speech recognition service).
In essence, if the speech recognizer isn't already running and being used, I don't want to start it up. But if the user actively uses the speech recognizer, I would like to provide additional support for it.
Thanks in advance.
I don't know the public API either; but the round mic window is controlled by SpeakableItems.app, at least on OS X 10.6. You can check the process list and/or the running applications list to see if it's there.
As inspired by #Yuji's answer, looks like the only way is to check whether the speech recognition server is running or not. Here is the code snippet, in case anyone also need it.
+(BOOL) speakableItemsEnabled {
NSString* speechServerBundleName = #"com.apple.speech.recognitionserver";
NSArray* apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:speechServerBundleName];
NSRunningApplication* speechServerApp = [apps lastObject];
return speechServerApp && !speechServerApp.terminated;
}
Hopefully this doesn't break in 10.7 "Lion".

Mac OS X: Getting detailed process information (specifically its launch arguments) for arbitrary running applications using its PID

I am trying to detect when particular applications are launched.
Currently I am using NSWorkspace, registering for the "did launch application" notification. I also use the runningApplications method to get apps that are currently running when my app starts.
For most apps, the name of the app bundle is enough. I have a plist of "known apps" that I cross check with the name of that passed in the notification.
This works fine until you come across an app that acts as a proxy for launching another application using command line arguments.
Example: The newly released Portal on the Mac doesn't have a dedicated app bundle. Steam can create a shortcut, which serves as nothing more than to launch the hl2_osx app with the -game argument and portal as it's parameter.
Since more Source based games are heading to the Mac, I imagine they'll use the same method to launch, effectively running the hl2_osx app with the -game argument.
Is there a nice way to get a list of the arguments (and their parameters) using a Cocoa API?
NSProcessInfo comes close, offering an `-arguments' method, but only provides information for its own process...
NSRunningApplication offers the ability to get information about arbitrary apps using a PID, but no command line args...
Is there anything that fills the gap between the two?
I'm trying not to go down the route of spawning an NSTask to run ps -p [pid] and parsing the output... I'd prefer something more high level.
You could use whatever ps uses, though it isn't cocoa based. According to Singh, ps is based on kvm and sysctl calls. Pouring over the source, the pertinant calls seem to be kvm_openfiles, kvm_getprocs and kvm_getargv. To get the command line arguments, first call kvm_openfiles to get access to the kernel memory space, then use kvm_getprocs to get kernel process info, then kvm_getargv.
The use of sysctl in ps seems less relevant to your goal; it's used to get other information, such as the group ID and parent proces ID. The particular sysctl name used is {CTL_KERN, KERN_PROC, KERN_PROC_which, flags}, where which specifies a process filter (e.g. ALL, PID) and flags are arguments for the filter (the details are in the sysctl man page).
OS X doesn't have support procfs, but Singh developed a FUSE based version, released under GPLv2. If you bundle it with your application, you'll have to release it under GPLv2 as well. Most of MacFUSE is released under a BSD-style license, so it can be distributed with your app without making it open source (fusefs/fuse_nodehash.c is released under Apple's open source license, but it also allows linking to closed source apps).
The question "Get other process' argv in OS X using C" should be of use, as it has sample code using kvm and sysctl. TN 2050 "Observing Process Lifetimes Without Polling" may also be of use to you.
Nope - running ps is your best bet. Standard process info interfaces aren't supported on OS X (noop versions were provided in OS X 10.4, but removed thereafter) and the private interfaces are likely to change between OS X revisions.
If you're willing to lock yourself into a single OS X version, all the source is available, for example for ps or libproc; you'll also need to run as root.