Friday, January 28, 2022

#1 2021-11-13 12:11:12 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2208

List ALL applications associated with file?

Hello everyone.

I'm trying to develop a script here that would list all the applications recommended for opening a file. As the Finder does when opening the Open With context menu.

On another site, I came across an interesting solution to this problem in Python. Here it is:

import sys
from AppKit import NSURL
from LaunchServices import LSCopyApplicationURLsForURL, kLSRolesAll
     
url = NSURL.fileURLWithPath_(sys.argv[1])
for url in LSCopyApplicationURLsForURL(url, kLSRolesAll):
    print url.path()

As you can see, it uses function LSCopyApplicationURLsForURL from Core Services framework. I tried to translate (to bridge) it to AsObjC, but can't. Please help me to solve this interesting problem. If this bridging is possible, of course.

I also quote here my failed attempt for clarity:

Applescript:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "CoreServices"
use scripting additions

set posixPath to POSIX path of (choose file)

set theNSURL to current application's NSURL's fileURLWithPath:posixPath
set kLSRolesAll to a reference to current application's kLSRolesAll
set recommendedApplications to LSCopyApplicationURLsForURL(theNSURL, kLSRolesAll)

Last edited by KniazidisR (2021-11-13 12:23:43 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#2 2021-11-13 02:51:02 pm

Mark FX
Member
From:: UK
Registered: 2011-08-12
Posts: 159

Re: List ALL applications associated with file?

The use of the Launch Services in the CoreServices framework requires you to handle C data types.
And that means being able to work with 'CF' Core Foundation data type references and pointers.
Which you can't do directly with AppleScript or AppleScriptObjC.

So you would have to write a command line tool with Python or Swift or any other language.
In order to access the 'LSCopyApplicationURLsForURL' function, as in the python code snippet.
And then call the command line tool from AppleScript with a "do shell script" command.

The other option open to you is to use the NSWorkspace class's URLForApplicationToOpenURL: function, to retrieve the file type's default application's URL, something like this.

Applescript:


use framework "Foundation"
use scripting additions

property myApp : a reference to current application

set workspace to myApp's NSWorkspace's sharedWorkspace()

try
   set filePath to POSIX path of (choose file with prompt "Select a file type to open")
on error
   return -- User canceled selecting a file type to open
end try

set fileURL to myApp's NSURL's fileURLWithPath:filePath

set appURL to workspace's URLForApplicationToOpenURL:fileURL

if appURL ≠ missing value then
   set appName to appURL's lastPathComponent() as text
   -- Optional opening of the file in the default application
   set fileName to fileURL's lastPathComponent() as text
   set openFile to button returned of (display alert "Do you want to open the " & fileName & " file in the default application?" buttons {"NO", "YES"})
   if openFile = "YES" then
       set fileOpened to workspace's openFile:filePath withApplication:appName
       if fileOpened then
           return "SUCCESS Opening the file"
       else
           return "FAILURE Opening the file"
       end if
   else if openFile = "NO" then
       return -- User chose not to open the file in the default application
       -- You could use the Standard Additions "choose application" as alternative
   end if
else
   return -- No default application was found for the selected file type
end if

The NSWorkspace class has many useful functions for opening files and other applications, and you could always use the "choose application" command from Standard Additions, to select an alternative application to open the file type.

If I can think of another way to use 'LSCopyApplicationURLsForURL' function after more thought, I will let you know.

Regards Mark

Last edited by Mark FX (2021-11-13 03:04:36 pm)

Offline

 

#3 2021-11-13 03:21:21 pm

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2208

Re: List ALL applications associated with file?

Mark FX wrote:

The use of the Launch Services in the CoreServices framework requires you to handle C data types.
And that means being able to work with 'CF' Core Foundation data type references and pointers.
Which you can't do directly with AppleScript or AppleScriptObjC.


Thank you very much for trying to help resolve the issue. Unfortunately, the script you provided is not what I need. I need to get not default application, but a list of all recommended applications. To understand me, select the file, then right-click and hover the mouse over the Open With menu item. The list I want appears in the pop-up window.

No, about working the AppleScript or AppleScriptObjC with 'CF' Core Foundation data type references and pointers. I have heard before that this is impossible. But, one day, I myself was still able to make this "impossible" possible. Here is a concrete example of how I did it for other task:

Applescript:


-- Convert MacRoman text to IBM866 text (russian encoding)

use AppleScript version "2.4"
use framework "Foundation"
use framework "CoreFoundation"
use scripting additions
property NSMacRomanStringEncoding : a reference to 30

set str to "熂†´™† èÆ´‚†¢™† (à.䆢†´•‡®§ß•, 1936).srt"

-- get appropriate NSStringEncoding from known kCFStringEncoding
set theEncoding to current application's CFString's CFStringConvertEncodingToNSStringEncoding(current application's kCFStringEncodingDOSRussian)
--> 2.147484699E+9

set theData to ((current application's NSString)'s stringWithString:str)'s dataUsingEncoding:NSMacRomanStringEncoding

-- convert to IBM866 encoding
return (current application's NSString's alloc()'s initWithData:theData encoding:theEncoding) as text
--> "Наталка Полтавка (И.Кавалеридзе, 1936).srt"

So I thought there might be a way to do something like this with a new task. smile

Last edited by KniazidisR (2021-11-13 03:25:53 pm)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#4 2021-11-13 05:13:10 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6748

Re: List ALL applications associated with file?

KniazidisR wrote:

about working the AppleScript or AppleScriptObjC with 'CF' Core Foundation data type references and pointers. I have heard before that this is impossible. But, one day, I myself was still able to make this "impossible" possible. Here is a concrete example of how I did it for other task:



Your example doesn't use any CF types -- it just uses an integer (via a constant) as argument, and then returns another integer. It's only when CF types are used that it fails, and unfortunately that's most of the time.

However, Monterey has exposed several of the Launch Services functions as NSWorkspace methods. So you can get what you want like this:

Applescript:

use AppleScript version "2.8" -- Monterey (12.0) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set theFile to (choose file)
set theURLs to (current application's NSWorkspace's sharedWorkspace()'s URLsForApplicationsToOpenURL:theFile) as list

You can also use URLsForApplicationsWithBundleIdentifier:, and there are methods for setting the default application.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#5 2021-11-13 05:39:58 pm

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2208

Re: List ALL applications associated with file?

Thanks for the clarification. I'm on Catalina and I can't upgrade to Monterey. This means I have to use a Python solution.

There is (or not) another way that requires me to search more. If I succeed, I will post the solution here.

Last edited by KniazidisR (2021-11-13 05:45:34 pm)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#6 2021-11-14 07:42:31 am

Mark FX
Member
From:: UK
Registered: 2011-08-12
Posts: 159

Re: List ALL applications associated with file?

@KnaizidisR

Shanes solution is a cut down version of the one I posted using NSWorkspace's URLForApplicationToOpenURL: function, which is available on all MacOS's, and not just "Monterey".

I also read somewhere that the Launch Services 'LSCopyApplicationURLsForURL' function has also been deprecated on the latest MacOS's, I'm not sure what version of OS that it was deprecated on, but it may be worth checking if it's still available on your "Catalina" system anyway.

If it is still there on "Catalina" I can give you a basic Swift shell script code, which you could use to get what you want, or as you've already stated a python script would be better, as python is installed as standard on MacOS's, where as Swift would have to be installed to run a swift shell script.

Regards Mark

Last edited by Mark FX (2021-11-14 07:47:44 am)

Offline

 

#7 2021-11-14 09:45:19 am

Mark FX
Member
From:: UK
Registered: 2011-08-12
Posts: 159

Re: List ALL applications associated with file?

I have knocked up a quick dirty python script and swift script as a starting point for you if your interested.
And also that the forum moderators don't mind non AppleScript code.

Let me know.

Regards Mark

Last edited by Mark FX (2021-11-14 09:47:19 am)

Offline

 

#8 2021-11-14 03:56:02 pm

Fredrik71
Member
Registered: 2019-10-23
Posts: 929

Re: List ALL applications associated with file?

@KniazidisR

You could compile this code...
clang -g -fobjc-arc -fmodules launchHandler.m -o launchHandler

launchHandler.m is the name of the filename to compile. (Works on Mojave)

@import Foundation;
@import CoreServices;

// clang -g -fobjc-arc -fmodules launchHandler.m -o launchHandler

int main (int argc, const char *argv[]) {
   
    // Rudimentary argument checking.
    if (argc != 2) {
        printf ("usage: %s filename\n", argv[0]);
        return -1;
    }

    const char *filename = argv[1];

    // Get a string of the full path of the file, using realpath() as the workhorse
    char pathbuffer[MAXPATHLEN];
    char *fullpath = realpath (filename, pathbuffer);
    if (fullpath == NULL) {
        fprintf (stderr, "could not find %s\n", filename);
        return -1;
    }

    NSURL *url = [NSURL fileURLWithPath: @( fullpath )];

    // Ask launch services for the different apps that it thinks could edit this file.
    // This is usually a more useful list than what can view the file.
    LSRolesMask roles = kLSRolesEditor;
    CFArrayRef urls = LSCopyApplicationURLsForURL((__bridge CFURLRef)url, roles);
    NSArray *appUrls = CFBridgingRelease(urls);

    // Extract the app names and sort them for prettiness.
    NSMutableArray *appNames = [NSMutableArray arrayWithCapacity: appUrls.count];

    for (NSURL *url in appUrls) {
        [appNames addObject: url.lastPathComponent];
    }
    [appNames sortUsingSelector: @selector(compare:)];

    // Finally emit to the user.
    for (NSString *appName in appNames) {
        printf ("%s\n", appName.UTF8String);
    }

    return 0;

} // main


if you are the expert, who will you call if its not your imagination.

Offline

 

#9 2021-11-14 06:23:54 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6748

Re: List ALL applications associated with file?

Mark FX wrote:

I also read somewhere that the Launch Services 'LSCopyApplicationURLsForURL' function has also been deprecated on the latest MacOS's, I'm not sure what version of OS that it was deprecated on



It carries the deprecation notice in Monterey: "Use -[NSWorkspace URLsForApplicationsToOpenURL:] instead." Which is what I used above. The deprecation version is listed as API_TO_BE_DEPRECATED, which I guess means we're on notice that it will be hard deprecated in some future release.

It doesn't look like there's going to be much left in Launch Services.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#10 2021-11-14 11:54:26 pm

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2208

Re: List ALL applications associated with file?

@ Fredrik71, thanks a lot.

I created an executable utility launchHandler from your Swift code and saved it to the /usr/local/bin directory. Works great. I only had to patch kLSRolesEditor to kLSRolesAll to list all recommended applications, not just editors.

Thank you too, @Mark FX and @Shane Stanley. The problem was solved perfectly by the joint efforts of experienced users.

Example using created executable:

Applescript:


do shell script "/usr/local/bin/launchHandler " & quoted form of POSIX path of (choose file)

-- Result:

-- BBEdit.app
-- Google Chrome.app
-- LibreOffice.app
-- Microsoft Excel.app
-- Microsoft Word.app
-- Notes.app
-- Pages.app
-- Sublime Text.app
-- System Information.app
-- TextEdit.app

Last edited by KniazidisR (2021-11-15 01:23:26 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#11 2021-11-15 12:46:53 pm

Mark FX
Member
From:: UK
Registered: 2011-08-12
Posts: 159

Re: List ALL applications associated with file?

@ Fredrik71, thanks a lot.


Lets give credit to the author at least.

https://gist.github.com/markd2/ff362c20463cd4ecc5fa

Regards Mark

Last edited by Mark FX (2021-11-15 12:47:17 pm)

Offline

 

#12 2021-11-15 01:10:37 pm

Fredrik71
Member
Registered: 2019-10-23
Posts: 929

Re: List ALL applications associated with file?

@Mark FX
You are right, wink on other hand I never said it was mine.
I only did a few minutes search on Goggle as I do for many things in life. smile


if you are the expert, who will you call if its not your imagination.

Offline

 

#13 2021-11-15 01:37:08 pm

Mark FX
Member
From:: UK
Registered: 2011-08-12
Posts: 159

Re: List ALL applications associated with file?

@ Fredrik71

Hey! the solution was found for the OP, and that's the main thing.

But a lot of smart people on various developer sites give their time and expertise for free.
They do it to help others in an unselfish way, the least we can all do in return is to give them some appreciation and credit for their efforts.

I also found that code when researching the 'LSCopyApplicationURLsForURL' function, and decided the code was convoluted, and did not need two for loops, but it is still a good and valid solution, if not a bit old fashioned in style.

But for me it's simple, I would simply link to the web page, because that's the honourable thing to do.
The OP would have their solution, and the code author can receive the gratitude.
And you would have still been helping someone yourself, and so everyone would be a winner.

Last edited by Mark FX (2021-11-15 01:53:51 pm)

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)