Finding other apps' paths - deterministically!
-
The methods for finding applications,
-[NSWorkspace fullPathForApplication:]
-[NSWorkspace absolutePathForAppBundleWithIdentifier:]
AppleScript's 'path to application'
all return only ONE result. If there is more than installation of the
application, which often happens out here in real life, the above
methods will often pick different installations, and even if you run
the same methods several minutes apart I sometimes get different
answers.
Is there any way to make these methods behave deterministically? If
not, what's a better way? Do I have to do a Spotlight search? I know
that unix 'find' would be way too slow.
Thanks,
Jerry Krinock -
On Sep 18, 2008, at 1:47 PM, Jerry Krinock wrote:
> The methods for finding applications,
>
> -[NSWorkspace fullPathForApplication:]
> -[NSWorkspace absolutePathForAppBundleWithIdentifier:]
> AppleScript's 'path to application'
>
> all return only ONE result. If there is more than installation of
> the application, which often happens out here in real life, the
> above methods will often pick different installations, and even if
> you run the same methods several minutes apart I sometimes get
> different answers.
>
> Is there any way to make these methods behave deterministically? If
> not, what's a better way? Do I have to do a Spotlight search? I
> know that unix 'find' would be way too slow.
The above methods all depend on Launch Services. I've seen the same
non-deterministic behavior that you have.
One solution, which is a complete hack, involves tagging the
application with a claim to handle a certain custom URL scheme. Then
you can use LSCopyApplicationURLsForURL with a fake URL of that
scheme. That's the only Launch Services API that I'm aware of that
actually returns a list of applications (rather than, for example, a
list of application bundle IDs which doesn't address your problem).
This, of course, only works if the application is one you're
developing rather than a third-party app.
Cheers,
Ken -
On Sep 18, 2008, at 8:47 PM, Jerry Krinock wrote:
> The methods for finding applications,
>
> -[NSWorkspace fullPathForApplication:]
> -[NSWorkspace absolutePathForAppBundleWithIdentifier:]
> AppleScript's 'path to application'
>
> all return only ONE result. If there is more than installation of
> the application, which often happens out here in real life, the
> above methods will often pick different installations, and even if
> you run the same methods several minutes apart I sometimes get
> different answers.
>
> Is there any way to make these methods behave deterministically? If
> not, what's a better way? Do I have to do a Spotlight search? I
> know that unix 'find' would be way too slow.
>
> Thanks,
>
> Jerry Krinock
Use NSTask with the command line find -x / -name '*.app'
Do this on a separate thread, and cache the result.
> ------
What is a woman that you forsake her, and the hearth fire and the home
acre,
to go with the old grey Widow Maker. --Kipling, harp song of the Dane
women
Tommy Nordgren
<tommy.nordgren...> -
On Sat, Sep 20, 2008 at 12:57 PM, Tommy Nordgren
<tommy.nordgren...> wrote:
> Use NSTask with the command line find -x / -name '*.app'
> Do this on a separate thread, and cache the result.
I highly recommend against this approach. One problem is that it will
fail badly if any of the returned paths contain the \n character,
which is a perfectly legal path character. For another it will fail if
the new process would exceed the user's process limit, a limit which
tends to be much lower than most other resource limits and thus much
easier to encounter in realistic situations.
For the original problem, I'd recommend using something like
LSCopyApplicationURLsForURL() if it's at all possible. Of course maybe
you have data that isn't good for that, but if you can use it then
that's the way to go.
If you must search the disk, use Spotlight if you can. It will be
vastly faster than anything else. The downside is that it won't work
if indexing is disabled or if the desired application is in an
excluded directory.
If you must search the whole disk without Spotlight, use
NSDirectoryEnumerator. Or if you're the sort who likes to use
unsupported private functions, you may be interested in the discussion
near the bottom of http://www.cocoadev.com/index.pl?AllApplications
Mike -
On Sep 21, 2008, at 6:36 AM, Michael Ash wrote:
> On Sat, Sep 20, 2008 at 12:57 PM, Tommy NordgrenSo this anomaly is easily detected when parsing find output
> <tommy.nordgren...> wrote:
>> Use NSTask with the command line find -x / -name '*.app'
>> Do this on a separate thread, and cache the result.
>
> I highly recommend against this approach. One problem is that it will
> fail badly if any of the returned paths contain the \n character,
Not true. With the given command line, find will return absolute paths.
>is very generous.
> which is a perfectly legal path character. For another it will fail if
> the new process would exceed the user's process limit, a limit which
> tends to be much lower than most other resource limits and thus much
> easier to encounter in realistic situations.
Firstly you can just increase the soft limit. Hard limits on Mac OS X
Secondly it would fail because maximum number of processes have been
reached,
you can simply retry later.
I have never had a process break because it has reached the maximum
allowed cpu-time,
except in situations where I've deliberately set it very low,
>open-source
>
> For the original problem, I'd recommend using something like
> LSCopyApplicationURLsForURL() if it's at all possible. Of course maybe
> you have data that isn't good for that, but if you can use it then
> that's the way to go.
>
> If you must search the disk, use Spotlight if you can. It will be
> vastly faster than anything else. The downside is that it won't work
> if indexing is disabled or if the desired application is in an
> excluded directory.
>
Using Spotlight to search for applications won't work, because some
packages install apps in directories that's not searched by spotlight,
For example - the default install path for Qt is in directory
/usr/local/Trolltech/Qt-<version-nr>
> If you must search the whole disk without Spotlight, use
> NSDirectoryEnumerator. Or if you're the sort who likes to use
> unsupported private functions, you may be interested in the discussion
> near the bottom of http://www.cocoadev.com/index.pl?AllApplications
>
> Mike
> -----------------------------------
See the amazing new SF reel: Invasion of the man eating cucumbers from
outer space.
On congratulations for a fantastic parody, the producer replies :
"What parody?"
Tommy Nordgren
<tommy.nordgren...> -
Tommy Nordgren wrote:
>
> On Sep 21, 2008, at 6:36 AM, Michael Ash wrote:
>
>> On Sat, Sep 20, 2008 at 12:57 PM, Tommy Nordgren
>> <tommy.nordgren...> wrote:
>>> Use NSTask with the command line find -x / -name '*.app'
>>> Do this on a separate thread, and cache the result.
>>
>> I highly recommend against this approach. One problem is that it will
>> fail badly if any of the returned paths contain the \n character,
> Not true. With the given command line, find will return absolute paths.
> So this anomaly is easily detected when parsing find output
>>
Don't do find /, please! It could end up looking through every directory
entry on a remote petabyte filesystem. Even if you restrict it to
"local" storage, you could be going to the network for iSCSI devices,
and if you avoid that, some people have multi-terabyte filesystems with
a lot of directory entries, they will be disappointed in any software
that does a find /.
Peter -
Peter O'Gorman wrote:
> Tommy Nordgren wrote:
>> On Sep 21, 2008, at 6:36 AM, Michael Ash wrote:
>>
>>> On Sat, Sep 20, 2008 at 12:57 PM, Tommy Nordgren
>>> <tommy.nordgren...> wrote:
>>>> Use NSTask with the command line find -x / -name '*.app'
Dunno how I missed the -x, does not matter though, find -x / on a large
filesystem with many files will take too long.
Peter
>>>> Do this on a separate thread, and cache the result.
>>> I highly recommend against this approach. One problem is that it will
>>> fail badly if any of the returned paths contain the \n character,
>> Not true. With the given command line, find will return absolute paths.
>> So this anomaly is easily detected when parsing find output
>
> Don't do find /, please! It could end up looking through every directory
> entry on a remote petabyte filesystem. Even if you restrict it to
> "local" storage, you could be going to the network for iSCSI devices,
> and if you avoid that, some people have multi-terabyte filesystems with
> a lot of directory entries, they will be disappointed in any software
> that does a find /.
-
On Sep 21, 2008, at 11:25 PM, Peter O'Gorman wrote:
> Peter O'Gorman wrote:
>> Tommy Nordgren wrote:
>>> On Sep 21, 2008, at 6:36 AM, Michael Ash wrote:
>>>
>>>> On Sat, Sep 20, 2008 at 12:57 PM, Tommy Nordgren
>>>> <tommy.nordgren...> wrote:
>>>>> Use NSTask with the command line find -x / -name '*.app'
>
> Dunno how I missed the -x, does not matter though, find -x / on a
> large
> filesystem with many files will take too long.
>
> Peter
Sure, but you don't need to do the scan every time. See the comment
on caching.
>
>
>>>>> Do this on a separate thread, and cache the result.
>>>> I highly recommend against this approach. One problem is that it
>>>> will
>>>> fail badly if any of the returned paths contain the \n character,
>>> Not true. With the given command line, find will return
>>> absolute paths.
>>> So this anomaly is easily detected when parsing find output
>>
>> Don't do find /, please! It could end up looking through every
>> directory
>> entry on a remote petabyte filesystem. Even if you restrict it to
>> "local" storage, you could be going to the network for iSCSI devices,
>> and if you avoid that, some people have multi-terabyte filesystems
>> with
>> a lot of directory entries, they will be disappointed in any software
>> that does a find /.
------
What is a woman that you forsake her, and the hearth fire and the home
acre,
to go with the old grey Widow Maker. --Kipling, harp song of the Dane
women
Tommy Nordgren
<tommy.nordgren...> -
On Sun, Sep 21, 2008 at 1:52 PM, Tommy Nordgren
<tommy.nordgren...> wrote:
>
> On Sep 21, 2008, at 6:36 AM, Michael Ash wrote:
>
>> On Sat, Sep 20, 2008 at 12:57 PM, Tommy Nordgren
>> <tommy.nordgren...> wrote:
>>>
>>> Use NSTask with the command line find -x / -name '*.app'
>>> Do this on a separate thread, and cache the result.
>>
>> I highly recommend against this approach. One problem is that it will
>> fail badly if any of the returned paths contain the \n character,
>
> Not true. With the given command line, find will return absolute
> paths.
> So this anomaly is easily detected when parsing find output
And what if it's the last character in a directory name?
>> which is a perfectly legal path character. For another it will fail if
>> the new process would exceed the user's process limit, a limit which
>> tends to be much lower than most other resource limits and thus much
>> easier to encounter in realistic situations.
>
> Firstly you can just increase the soft limit. Hard limits on Mac OS X
> is very generous.
> Secondly it would fail because maximum number of processes have been
> reached,
> you can simply retry later.
> I have never had a process break because it has reached the maximum
> allowed cpu-time,
> except in situations where I've deliberately set it very low,
You seem to have misunderstood the limit I'm talking about. I'm
talking about the limit on the number of processes, not anything else.
On my system, the number of processes per user is set at 266, and this
appears to be a hard limit. That limit is *extremely* easy to reach,
especially if you're running a bunch of poorly-written apps that spawn
subprocesses when they don't need to.
Retrying later is just silly. The amount of code to run 'find' with an
NSTask, read all its output, make some attempt at correcting \n
embedded in paths, and retry when the process can't spawn because
you've hit the process limit, is far more than the amount of code
you'd have to write to just directly iterate over the filesystem using
NSDirectoryEnumerator. What's the advantage supposed to be?
>> For the original problem, I'd recommend using something like
>> LSCopyApplicationURLsForURL() if it's at all possible. Of course maybe
>> you have data that isn't good for that, but if you can use it then
>> that's the way to go.
>>
>> If you must search the disk, use Spotlight if you can. It will be
>> vastly faster than anything else. The downside is that it won't work
>> if indexing is disabled or if the desired application is in an
>> excluded directory.
>>
> Using Spotlight to search for applications won't work, because some
> open-source
> packages install apps in directories that's not searched by spotlight,
> For example - the default install path for Qt is in directory
> /usr/local/Trolltech/Qt-<version-nr>
That's why I pointed this out when I suggested it.
Mike -
Incidentally, this is something Microsoft's Office 2008 update
installers do. If you're not careful you can walk away and come back
hours later with the search still running.
Jeffrey R. Kelley
<slauncha...>
ITCS - Campus Computing Sites
University of Michigan
On Sep 21, 2008, at 3:49 PM, Peter O'Gorman wrote:
> Don't do find /, please! It could end up looking through every
> directory
> entry on a remote petabyte filesystem.
-
Michael Ash wrote:
> For the original problem, I'd recommend using something like
> LSCopyApplicationURLsForURL() if it's at all possible.
Good idea but I'm looking for a helper app which is not registered to
open any URLs. Also, I don't see any indication that
LSCopyApplicationURLsForURL() would return multiple installations of
the same app.
> If you must search the disk, use Spotlight if you can. ... The
> downside is that it won't work if indexing is disabled or if the
> desired application is in an
> excluded directory.
Also, it seems to not find helper applications which are inside other
applications' packages.
On 2008 Sep, 21, at 15:08, Michael Ash wrote:
> On my system, the number of processes per user is set at 266, and this
> appears to be a hard limit. That limit is *extremely* easy to
> reach ...
Amen. My rule is: "Use NSTask, or spawn processes, as little as
possible".
EPILOG
The programmatic Spotlight search executes quickly and was fairly easy
to write, but then I found it didn't look inside packages. So I ended
up using Carbon's File Manager. It's not as fast as Spotlight, and
writing the code was not a cup of Cocoa, but it's done and has been
working in my tests for the last week or so.
I've posted the classes and demos for both solutions in case anyone
has similar needs. They're fairly well-documented with HeaderDoc.
http://www.sheepsystems.com/sourceCode/fileSysSearch.html -
On Tue, Sep 30, 2008 at 1:00 PM, Jerry Krinock <jerry...> wrote:
> Michael Ash wrote:
>
>> For the original problem, I'd recommend using something like
>> LSCopyApplicationURLsForURL() if it's at all possible.
>
> Good idea but I'm looking for a helper app which is not registered to open
> any URLs. Also, I don't see any indication that
> LSCopyApplicationURLsForURL() would return multiple installations of the
> same app.
Note that if you give it a file URL, it will look up the application
based on extension, UTI, HFS file type, etc. and not just the URL
scheme. So your helper doesn't need to be registered to open URLs,
just documents. Of course if it isn't registered to open documents
then this doesn't work so great.
As for multiple installations, I don't see any evidence that it
*wouldn't* return multiple installations, and it's a fair assumption
that it would based on the probability that the Finder uses this API
for things like the Open With... menu. It's a moot point if the helper
can't open documents, but if it does then it would bear testing.
>> If you must search the disk, use Spotlight if you can. ... The downside is
>> that it won't work if indexing is disabled or if the desired application is
>> in an
>> excluded directory.
>
> Also, it seems to not find helper applications which are inside other
> applications' packages.
Craptacular! Anyway, I'm glad you found an acceptable solution in the end.
Mike -
On Sep 30, 2008, at 12:00 PM, Jerry Krinock wrote:
> Also, it seems to not find helper applications which are inside
> other applications' packages.
Is there any reason why you couldn't just search for the main
application which contains the helper application inside its bundle
and then just go from there to find the helper?
Charles -
On 2008 Sep, 30, at 16:47, Charles Srstka wrote:
> On Sep 30, 2008, at 12:00 PM, Jerry Krinock wrote:
>
>> Also, it seems to not find helper applications which are inside
>> other applications' packages.
>
> Is there any reason why you couldn't just search for the main
> application which contains the helper application inside its bundle
> and then just go from there to find the helper?
OK, do a programmatic Spotlight search for the main application, get
the path and tack on /Contents/Resources/MyHelper.app. I hadn't
thought about that but, yes, it that would overcome the package
problem, Charles.
However it still wouldn't work for users who've turned off Spotlight
indexing or excluded stuff in their Spotlight prefs. So I'm happy
with my Carbon File Manager search, although admittedly not happy with
the several days I spent getting the stupid thing working. -
On 2008 Sep, 30, at 12:50, Michael Ash wrote:
> Of course if [your helper] isn't registered to open documents
> then this doesn't work so great.
Yes, that's the problem. It's a background kind of a guy. It doesn't
open documents of any kind.
> As for multiple installations, I don't see any evidence that
> [LSCopyApplicationURLsForURL()]
> *wouldn't* return multiple installations, and it's a fair assumption
> that it would based on the probability that the Finder uses this API
> for things like the Open With... menu.
Well, based on the fact that Finder only launches one app, I was
expecting the opposite. But since I started this thread, I did a
test, and to my surprise, Michael is correct!
I copied OmniOutliner.app to my desktop, gave
LSCopyApplicationURLsForURL the url of an OmniOutliner document, and
it found both copies of the app...
NSURL* url = [NSURL fileURLWithPath:@"/Users/jk/Desktop/
Firefox3Database.oo3"] ;
CFArrayRef finds = LSCopyApplicationURLsForURL (
(CFURLRef) url,
kLSRolesAll
) ;
NSLog([(NSArray*)finds description]) ;
*** Output:
(
file://localhost/Applications/OmniOutliner.app/,
file://localhost/Users/jk/Desktop/OmniOutliner.app/
)
So, this is very easy solution, for finding document-based apps. -
On Oct 1, 2008, at 2:47 PM, Jerry Krinock wrote:
>
> On 2008 Sep, 30, at 16:47, Charles Srstka wrote:
>
>> On Sep 30, 2008, at 12:00 PM, Jerry Krinock wrote:
>>
>>> Also, it seems to not find helper applications which are inside
>>> other applications' packages.
>>
>> Is there any reason why you couldn't just search for the main
>> application which contains the helper application inside its bundle
>> and then just go from there to find the helper?
>
> OK, do a programmatic Spotlight search for the main application, get
> the path and tack on /Contents/Resources/MyHelper.app. I hadn't
> thought about that but, yes, it that would overcome the package
> problem, Charles.
Actually, it'd be better to use +[NSBundle bundleWithPath:] and then
use that to find MyHelper.app rather than just tacking on the subpath.
You are correct that this would not work if users had excluded a
directory from the search, though.
Charles


