Pregunta

The title kind of says it all:

I'm wondering if there's any _NSWasLaunchedFromFinder-type API or hook that an OS/X C program (of the int main(int argc, char* argv[]) variety) could use to determine if it was launched by a user clicking on the executable in the Finder vs. if it was run through a more traditional route (like being typed into the Terminal).

¿Fue útil?

Solución

If you are talking about plain command line utility - there is no way to determine whether it was launched from Finder or in terminal since Finder will launch terminal and then execute your program in it.

But there is a solution. I'd rather call it a workaround. You can wrap your executable with bundle, create simple script (lets call it finderLauncher) which will launch actual executable with some additional command line parameter (-launchedFromFinder for example). Don't forget to make it executable. Than in your Info.plist file set finderLauncher as CFBundleExecutable value.
And now in Finder user will see only your bundle and by clicking on it your actual executable will be launched via finderLauncher passing specified command line parameter. Same behaviour will be by using open command in terminal.
And by direct launch from terminal there would be no -launchedFromFinder parameter (off course if user will not pass it directly).

P.s. It would be much easier by specifying command line parameters directly in Info.plist, but I can't find such key in Information Property List Key Reference although there is such key for agents/daemons.

Otros consejos

Method 1 :: You can use NSGetExecutablePath

Here's the dev reference to it :: Mac Developer Library

_NSGetExecutablePath() copies the path of the main executable into the buffer buf. The bufsize parameter should initially be the size of the buffer. This function returns 0 if the path was successfully copied, and * bufsize is left unchanged. It returns -1 if the buffer is not large enough, and * bufsize is set to the size required. Note that _NSGetExecutablePath() will return "a path" to the exe- cutable not a "real path" to the executable. That is, the path may be a symbolic link and not the real file. With deep directories the total bufsize needed could be more than MAXPATHLEN.

Method 2 :: Use AppleScript

You can use AppleScript to find the current applications open with the following script ::

tell application "Finder"
set appPath to my getFrontAppPath()
set AppleScript's text item delimiters to {":"}
set currentApp to text item -2 of appPath
say currentApp
end tell

on getFrontAppPath()
    set frontAppPath to (path to frontmost application) as text
    set myPath to (path to me) as text

    if frontAppPath is myPath then
        try
            tell application "Finder" to set bundleID to id of file myPath
            tell application "System Events" to set visible of (first process whose bundle identifier is bundleID) to false

            -- we need to delay because it takes time for the process to hide
            -- I noticed this when running the code as an application from the applescript menu bar item
            set inTime to current date
            repeat
                set frontAppPath to (path to frontmost application) as text
                if frontAppPath is not myPath then exit repeat
                if (current date) - inTime is greater than 2 then exit repeat
            end repeat
        end try
    end if
    return frontAppPath
end getFrontAppPath

That should get you the application last opened, whether it was Terminal or Finder :)

For Finder you get a response :: "Macintosh HD:System:Library:CoreServices:Finder.app:"

For Terminal :: "Macintosh HD:Applications:Utilities:Terminal.app:"

Get the Parent Process ID. Then browse its Process Status to get its PPID, recursively up to Finder.app or init.

Once you have found the terminal ancestor that is child of Finder.app, you can look at its start time and its arguments (see the -o and -O options in man ps: your keywords should include args and start): if the terminal process started near your programm start time and the arguments include your program name, you know that it has been started by Finder.app.

Probably, you can ignore the times and just look for the terminal's arguments.

You can assume the reverse logic and use isatty.

if (isatty(1)) printf("Launched in a terminal\n");
else printf("Launched by clicking something\n");

This just determines if stdout is a tty. If you launch it from a program, icon, menu etc... it will be false. also if you want to tell if an X server is running and it was launched from a terminal emulator you can use getenv("DISPLAY") which is set when X starts (so it will be NULL if run from the console)

    // when the user doubleclicks your program it will be started with a -psn_ parameter
    if (argc >= 2 && (strncmp(argv[1], "-psn_", 5) == 0)) {
        InfoLogWithClient(L"Init", L"Program %d cannot be started with double-click!", getpid());
        return EX_USAGE;
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top