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.
How can a C program tell if it was launched from the Finder?
Question
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).
Solution
OTHER TIPS
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;
}