9

Is is possible to detect if a Python script was started from the command prompt or by a user "double clicking" a .py file in the file explorer on Windows?

pkit
  • 7,993
  • 6
  • 36
  • 36

5 Answers5

12

If running from the command line there is an extra environment variable 'PROMPT' defined.

This script will pause if clicked from the explorer and not pause if run from the command line:

import os

print 'Hello, world!'

if not 'PROMPT' in os.environ:
    raw_input()

Tested on Windows 7 with Python 2.7

Mads Bak
  • 121
  • 1
  • 3
  • Testing for two keywords `PROMPT` and `TCL_LIBRARY`. Starting up with 1) python shell, 2) double clicking on .py file 3) windows cmd: https://i.stack.imgur.com/CbFUT.jpg – Comrade Che May 04 '17 at 05:59
5

Here is an example of how to obtain the parent process id and name of the current running script. As suggested by Tomalak this can be used to detect if the script was started from the command prompt or by double clicking in explorer.

import win32pdh
import os

def getPIDInfo():
    """ 
    Return a dictionary with keys the PID of all running processes.
    The values are dictionaries with the following key-value pairs:
        - name: <Name of the process PID>
        - parent_id: <PID of this process parent>
    """

    # get the names and occurences of all running process names
    items, instances = win32pdh.EnumObjectItems(None, None, 'Process', win32pdh.PERF_DETAIL_WIZARD)
    instance_dict = {}
    for instance in instances:
        instance_dict[instance] = instance_dict.get(instance, 0) + 1

    # define the info to obtain 
    counter_items =  ['ID Process', 'Creating Process ID']

    # output dict
    pid_dict = {}

    # loop over each program (multiple instances might be running)
    for instance, max_instances in instance_dict.items():
        for inum in xrange(max_instances):
            # define the counters for the query 
            hq = win32pdh.OpenQuery()
            hcs = {}
            for item in counter_items:
                path = win32pdh.MakeCounterPath((None,'Process',instance, None,inum,item))
                hcs[item] = win32pdh.AddCounter(hq,path)
            win32pdh.CollectQueryData(hq)

            # store the values in a temporary dict
            hc_dict = {}
            for item, hc in hcs.items():
                type,val=win32pdh.GetFormattedCounterValue(hc,win32pdh.PDH_FMT_LONG)
                hc_dict[item] = val
                win32pdh.RemoveCounter(hc)
            win32pdh.CloseQuery(hq)

            # obtain the pid and ppid of the current instance
            # and store it in the output dict
            pid, ppid = (hc_dict[item] for item in counter_items) 
            pid_dict[pid] = {'name': instance, 'parent_id': ppid}

    return pid_dict

def getParentInfo(pid):
    """
    Returns a PID, Name tuple of the parent process for the argument pid process.
    """
    pid_info = getPIDInfo()
    ppid = pid_info[pid]['parent_id']
    return ppid, pid_info[ppid]['name']

if __name__ == "__main__":
    """
    Print the current PID and information of the parent process.
    """
    pid = os.getpid()
    ppid, ppname = getParentInfo(pid)

    print "This PID: %s. Parent PID: %s, Parent process name: %s" % (pid, ppid, ppname)
    dummy = raw_input()

When run from the command prompt this outputs:

This PID: 148. Parent PID: 4660, Parent process name: cmd

When started by double clicking in explorer this outputs:

This PID: 1896. Parent PID: 3788, Parent process name: explorer

Community
  • 1
  • 1
pkit
  • 7,993
  • 6
  • 36
  • 36
3

The command-prompt started script has a parent process named cmd.exe (or a non-existent process, in case the console has been closed in the mean time).

The doubleclick-started script should have a parent process named explorer.exe.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Clever. How would you do that? – grammar31 Feb 17 '09 at 21:36
  • From http://docs.python.org/library/os.html#os.getppid it seems I can get the process id, but not the name. – pkit Feb 17 '09 at 21:42
  • It depends. What tools do you have available? Do you want to check from within the python script or from outside? In any case, "How do I find out Win32 process information using Python" is good material for a separate question. – Tomalak Feb 17 '09 at 21:43
  • After a brief Googling it seems that the win32pdh module is just what you need. It looks quite promising. WMI should also be perfectly capable of answering such a question – Tomalak Feb 17 '09 at 21:51
  • 1
    Python 3.3+ by default installs a launcher (py.exe) that becomes a parent process for scripts started by double click. – monk-time Dec 19 '17 at 17:56
  • Good idea. Unfortunately, this doesn't work when using Powershell (PROMPT is not defined even in an interactive shell). – Raúl Salinas-Monteagudo Mar 18 '21 at 11:15
  • @Raúl I was not talking about a variable named "PROMPT". I was talking about the parent process ID. – Tomalak Mar 18 '21 at 12:11
  • @Tomalak: sorry, I think I answered in the wrong answer. :) – Raúl Salinas-Monteagudo Mar 23 '21 at 15:12
2

Good question. One thing you could do is create a shortcut to the script in Windows, and pass arguments (using the shortcut's Target property) that would denote the script was launched by double-clicking (in this case, a shortcut).

Jason Coon
  • 17,601
  • 10
  • 42
  • 50
0

I put this little function (pybyebye()) just before the return statement in some of my programs. I have tested it under Windows 10 on my desktop and laptop and it does what I want, i.e. it pauses awaiting user input only when the program has been started by double-clicking the program in File Explorer. This prevents the temporary command window from vanishing before the user says so. Under Linux, it does nothing. No harm anyway! Likewise on a Mac.

##  PYBYEBYE :
def pybyebye (eprompt="PROMPT",efps="FPS_BROWSER_"):
    "nice exit in Windows according to program launch from: IDLE, command, clix."

##  first examine environment (os & sys having been imported) :
ui = None
platform = sys.platform
##  print("os =",platform)
if not platform.lower().startswith("win"):
    return  ui  ##  only relevant in windows
fromidle = False
launched = "Launched from"
if sys.executable.endswith("pythonw.exe"):
    fromidle = True  ##  launched from within IDLE
envkeys = sorted(os.environ)
prompter = eprompt in envkeys
browser = False
for ek in envkeys:
    ##  print(ek)
    if ek.startswith(efps):
        browser = True
        break
##  next decide on launch context :
if fromidle and not prompter:  ##  surely IDLE
    ##  print(launched,"IDLE")
    pass  ##  screen won't disappear
elif browser and not prompter:  ##  run with double click
    ##  print(launched,"File Explorer")
    print("Press Enter to finish ....") ; ui=input()
elif prompter and not fromidle:  ##  run from preexisting command window
    ##  print(launched,"Command Window")
    pass  ##  screen won't disappear
else:  ##  something funny going on, Mac or Linux ??
    print("launch mode undetermined!")

return  ui