Recent

Author Topic: Detecting how an application was launched  (Read 4864 times)

geraldholdsworth

  • Full Member
  • ***
  • Posts: 214
Detecting how an application was launched
« on: June 16, 2024, 08:19:56 pm »
I was idly wondering if there is a way of determining, in Lazarus, if an application is launched from the Explorer/Filer or via Command Line/Terminal in Windows, macOS and Linux.

So, I begun looking to see if there was a way, and could only find a possible Windows route to determine this.

Is it possible in macOS or Linux?

MarkMLl

  • Hero Member
  • *****
  • Posts: 7086
Re: Detecting how an application was launched
« Reply #1 on: June 16, 2024, 08:46:45 pm »
Can't comment on Windows. In the case of the others get the PPID from /proc and see what it tells you, but be aware that Linux and a BSD derivative such as a Mac might have subtle differences.

I've looked at this to some extent on Linux and SunOS in the context of logging FPC build commands, see https://github.com/MarkMLl/fpclaz_build_scripts which might have something useful. I might also have looked at it more recently in the case of Linux, but can't remember what I was working on (i.e. where I put it) or precisely what level of detail I was looking for.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

TRon

  • Hero Member
  • *****
  • Posts: 2878
Re: Detecting how an application was launched
« Reply #2 on: June 17, 2024, 05:01:41 am »
The following is a (simple) example of MarkMLI's suggestion that works for Linux.
Code: Pascal  [Select][+][-]
  1. program parent;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   sysutils, baseunix;
  7.  
  8. function GetParentProcessName: string;
  9. begin
  10.   GetParentProcessName := fpReadLink('/proc/' + fpGetppid.ToString + '/exe');
  11. end;
  12.  
  13. begin
  14.   writeln('name of parent process = ', GetParentPRocessName);
  15. end.
  16.  
Do note that this still does not tell anything about how the program was actually launched as it might very well be done at the desktop that uses a menu-entry that invokes a shell. The heritage can be a very long path and you can't make any assumptions about when to stop following as in Linux there is no consistency regarding launchers whatsoever.
« Last Edit: June 17, 2024, 05:12:40 am by TRon »

Zvoni

  • Hero Member
  • *****
  • Posts: 2467
Re: Detecting how an application was launched
« Reply #3 on: June 17, 2024, 07:59:07 am »
Have you checked if there is anything in ParamStr?

EDIT: Just checked on Windows.
Nope, nothing in there, either
« Last Edit: June 17, 2024, 08:44:04 am by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

MarkMLl

  • Hero Member
  • *****
  • Posts: 7086
Re: Detecting how an application was launched
« Reply #4 on: June 17, 2024, 09:47:18 am »
Have you checked if there is anything in ParamStr?

EDIT: Just checked on Windows.
Nope, nothing in there, either

The sole possibility there would be if the... let's say "extended OS" i.e. kernel, GUI etc. that underlay the application program put the name of a .desktop file into ParamStr(0) if one had been used to start the binary. IME it never does: nothing to see, move along.

However one other thing would be to inspect shell/environment variables looking for e.g. XDG_something. In principle this tells one a lot and might allow other information to be mined via e.g. D-Bus, in practice it is at best erratic at least on Debian.

So the only way that I've found useful is to look at least one layer deep on the PPID sequence.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

finchley

  • Newbie
  • Posts: 2
Re: Detecting how an application was launched
« Reply #5 on: June 17, 2024, 10:33:23 am »
I've always used the following code for console programs. If you launched the program via a double click it will pause and wait for the enter key to be pressed before terminating. If run from the command line the program will just terminate.

Code: Pascal  [Select][+][-]
  1. program foo;
  2.  
  3. uses
  4.   windows;
  5.  
  6. var
  7.   ProcessIDs : array[0..255] of DWORD;
  8.  
  9. begin
  10.   { The last line of code... }
  11.   if (GetConsoleProcessList(ProcessIDs, high(ProcessIDs)) = 1) then ReadLn();
  12. end.
  13.  

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11651
  • FPC developer.
Re: Detecting how an application was launched
« Reply #6 on: June 17, 2024, 10:55:41 am »
Logically it would mean walking parent processes till you find a name you recognize.

But that assumes the process parent-child bonds are intact and unmodified (daemonization etc)

Fibonacci

  • Sr. Member
  • ****
  • Posts: 453
  • Internal Error Hunter
Re: Detecting how an application was launched
« Reply #7 on: June 17, 2024, 12:06:03 pm »
Another one for Windows

Code: Pascal  [Select][+][-]
  1. uses Windows;
  2.  
  3. function is_run_from_console: boolean;
  4. var
  5.   pid: dword;
  6. begin
  7.   GetWindowThreadProcessId(GetConsoleWindow, @pid);
  8.   result := GetCurrentProcessId <> pid;
  9. end;
  10.  
  11. begin
  12.   writeln('run from console = ', is_run_from_console);
  13.   readln;
  14. end.

Zvoni

  • Hero Member
  • *****
  • Posts: 2467
Re: Detecting how an application was launched
« Reply #8 on: June 17, 2024, 12:22:16 pm »
The sole possibility there would be if the... let's say "extended OS" i.e. kernel, GUI etc. that underlay the application program put the name of a .desktop file into ParamStr(0) if one had been used to start the binary. IME it never does: nothing to see, move along.
Would have to disagree, at least for Windows.
Just did a small test in Windows.
Created a Desktop-Link pointing to my exe, right-click on Desktop-Link, Properties, and then in Target, where the fully qualified path incl. exe-name is, i just appended a " -g" (arbitrary value, g=GUI)
It returns, that the ParamCount is >0, writeln-ing (ParamStr(1)) returns the "-g"

But a No-No for doubleclick-ing in Explorer
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

geraldholdsworth

  • Full Member
  • ***
  • Posts: 214
Re: Detecting how an application was launched
« Reply #9 on: June 17, 2024, 12:29:46 pm »
The following is a (simple) example of MarkMLI's suggestion that works for Linux.


Do note that this still does not tell anything about how the program was actually launched as it might very well be done at the desktop that uses a menu-entry that invokes a shell. The heritage can be a very long path and you can't make any assumptions about when to stop following as in Linux there is no consistency regarding launchers whatsoever.
It's a start, thank you.
Looks like there's plenty of Windows solutions though.

MarkMLl

  • Hero Member
  • *****
  • Posts: 7086
Re: Detecting how an application was launched
« Reply #10 on: June 17, 2024, 12:53:12 pm »
The sole possibility there would be if the... let's say "extended OS" i.e. kernel, GUI etc. that underlay the application program put the name of a .desktop file into ParamStr(0) if one had been used to start the binary. IME it never does: nothing to see, move along.
Would have to disagree, at least for Windows.

Windows does not TTBOMK use .desktop files.

https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Zvoni

  • Hero Member
  • *****
  • Posts: 2467
Re: Detecting how an application was launched
« Reply #11 on: June 17, 2024, 02:30:20 pm »
The sole possibility there would be if the... let's say "extended OS" i.e. kernel, GUI etc. that underlay the application program put the name of a .desktop file into ParamStr(0) if one had been used to start the binary. IME it never does: nothing to see, move along.
Would have to disagree, at least for Windows.

Windows does not TTBOMK use .desktop files.

https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html

MarkMLl
OC, i used the Windows-"equivalent" to ".desktop" --> .lnk

For linux .desktop files there is the EXEC Key-Word inside, which acc. to different Sites (Ubuntu) contains the executable name optionally followed by arguments

EDIT: Or the link you posted.
Quote
The Exec key must contain a command line. A command line consists of an executable program optionally followed by one or more arguments.
« Last Edit: June 17, 2024, 02:32:57 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

MarkMLl

  • Hero Member
  • *****
  • Posts: 7086
Re: Detecting how an application was launched
« Reply #12 on: June 17, 2024, 02:48:32 pm »
For linux .desktop files there is the EXEC Key-Word inside, which acc. to different Sites (Ubuntu) contains the executable name optionally followed by arguments

Yes, but the .desktop file does not appear in ParamStr(0), it's always resolved by the OS to the binary executable.

In practice my experience is that there are things in that file which are at best handled haphazardly, but these are not germane to this particular thread.

I suppose that if we're mentioning .desktop and .lnk files then we have to also mention the OS/2 WPS shadow files which- if memory serves correctly- were (in unix terms) hard filesystem links rather than being anything special.

We can probably say safely that ParamStr(0) refers to a binary, where there might be multiple hardlinks pointing to the same storage. If it was invoked via a symlink, the situation is implementation-specific. .desktop and .lnk files are resolved at an earlier stage of the GUI's handling of program startup.

None of which really helps OP: on unix he has to look at the PPID tree, and the structure of that will vary between Linux and BSD derivatives.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

BildatBoffin

  • New Member
  • *
  • Posts: 20
Re: Detecting how an application was launched
« Reply #13 on: June 17, 2024, 03:01:26 pm »
Since not already mentioned, on posix-compliant systems you can use

Code: Pascal  [Select][+][-]
  1. function isatty(fd: integer = 0): integer; cdecl;

The running program (if fd is 0, i.e the file des of the output stream, as set as default) can call this to determine if it's called from a term.

geraldholdsworth

  • Full Member
  • ***
  • Posts: 214
Re: Detecting how an application was launched
« Reply #14 on: June 17, 2024, 03:03:46 pm »
What I've got so far, collating the code from above, is:
Code: Pascal  [Select][+][-]
  1. uses
  2.  SysUtils
  3.  {$IFDEF Linux}
  4.  , BaseUnix
  5.  {$ENDIF}
  6.  {$IFDEF Windows}
  7.  , Windows
  8.  {$ENDIF}
  9.  ;
  10.  
  11. function IsRunFromConsole: Boolean;
  12. var                                      
  13. {$IFDEF Windows}
  14.  pid: dword;
  15. {$ENDIF}
  16. {$IFDEF Linux}
  17.  ParentProcess: String;
  18. {$ENDIF}
  19. {$IFDEF Darwin}
  20.  path: String;
  21. {$ENDIF}
  22. begin
  23. {$IFDEF Windows}
  24.  GetWindowThreadProcessId(GetConsoleWindow,@pid);
  25.  Result:=GetCurrentProcessId<>pid;
  26. {$ENDIF}
  27. {$IFDEF Linux}
  28.  ParentProcess:=fpReadLink('/proc/'+fpGetppid.ToString+'/exe');
  29.  {Should return nothing for launching from Filer;
  30.  '/usr/bin/lxterminal' when launched from Filer and "Execute in Terminal";
  31.  and '/usr/bin/bash' when launched from bash console.}
  32.  if ParentProcess<>'' then Result:=True else Result:=False;
  33. {$ENDIF}
  34. {$IFDEF Darwin}
  35.  path:=ParamStr(0);
  36.  {This is the command used to run the executable. From bash/Terminal you need
  37.  to do './project'. This dot will be missing when run from the Filer.}
  38.  if path[1]='.' then Result:=True else Result:=False;
  39. {$ENDIF}
  40. end;
Appears to work on my Raspberry Pi OS and my Mac. I haven't tried it on Windows yet - I need to fire that beast up and try it out later on.

 

TinyPortal © 2005-2018