Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only use console mode if run on command line? #2117

Closed
rbalik opened this issue Jul 25, 2016 · 10 comments
Closed

Only use console mode if run on command line? #2117

rbalik opened this issue Jul 25, 2016 · 10 comments
Labels
area:bootloader Caused be or effecting the bootloader feature Feature request platform:Windows pull-request wanted Please submit a pull-request for this, maintainers will not actively work on this.

Comments

@rbalik
Copy link

rbalik commented Jul 25, 2016

This is half question and half feature request.

Let's say I build with the -w flag. Is there a way to allow the executable to still output to stdout if it's run on the command line, but not pop up a console window if the user double-clicks on the icon or starts it from a GUI?

@htgoebel htgoebel added feature Feature request area:bootloader Caused be or effecting the bootloader labels Jul 26, 2016
@htgoebel
Copy link
Member

I'm not a Windows guy, so I may be wrong. AFAIK this is not possible since on Windows even the entry points differ, see the source.

@codewarrior0
Copy link
Contributor

codewarrior0 commented Jul 26, 2016

It's possible, but it requires quite a bit more trickery than I'm willing to commit to. Luckily, all of the required C functions can be called via the ctypes and msvcrt modules, so you can do it all from your application's Python code without needing any changes to PyInstaller. See this article for details, and especially look at the AttachConsole solution and sample code at the end.

https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/

If you do get this approach to work, let us know. Working code for this would be welcome on the PyInstaller wiki as a new Recipe.

If not, there's always (myapp.exe; tail -f myapp.log)...

@rbalik
Copy link
Author

rbalik commented Jul 26, 2016

Hmm ok, I'll take a look at that. Thanks.

Our specific use case is that we have a windowed app, but you can specify some config parameters on the command line which means that being able to do --help would be useful but now the output gets lost.
We also have another app which you can run with --textmode and it'll do everything on stdin/stdout instead of creating the GUI.

@carlosperate
Copy link

carlosperate commented Jan 12, 2017

This is something I would be interested to do as well.
I found this comment, but I haven't had a chance to try it yet: #1339 (comment)
Would this be the only way to achieve this behaviour?

@htgoebel
Copy link
Member

htgoebel commented Dec 2, 2017

Now, is anybody taking the challenge on this`

@htgoebel htgoebel added the pull-request wanted Please submit a pull-request for this, maintainers will not actively work on this. label Dec 2, 2017
@mateo4778
Copy link

This issue recently came up for me as I was trying to build an application to run in either command line or GUI modes depending on where it was run from. Although it is not perfect, my solution was to have pyinstaller build the application with console=True, on startup detect how it was run, and then hide the console if we want GUI mode.

To hide the console I used the method suggested in this comment #1339 (comment) . The downside is that the console will flash on startup, but that didn't bother me much.

Then to detect the run mode on startup I did the following. The for loop is run 3 times to get the name of the grandparent of our process. If the grandparent is explorer.exe then we would run in GUI mode because it was double clicked. But if the grandparent is cmd or powershell then we would run in CLI mode. NOTE: the decision to traverse up 2 levels is because the first parent is the bootloader exe, because I am using onefile mode. I guess if you were using onedir mode you would only need to go up one level (but I didn't test that).

import wmi
cwmi = wmi.WMI()

def isCliCaller():
    '''Helper function to check the grandparent process and identify if we should use cli or window'''
    if getattr(sys, 'frozen', False):
        pid = os.getpid()
        name = ''
        for x in range(3):
            for process in cwmi.Win32_Process(ProcessId=pid):
                name = process.name
                pid = process.ParentProcessId
                if 'explorer' in name.lower():
                    return False
    return True

@Legorooj
Copy link
Member

This can't be done due to windows restrictions.

@sbytnar
Copy link

sbytnar commented Jan 20, 2021

An alternative that works for me: attach to a parent console if one is available, otherwise, don't show the console when the app is double clicked.

Set console=False in your pyinstaller spec.
Early in the execution of the code, walk the parent->grandparent processes (like the WMI example above or with psutil) until the first one of them successfully provides a non-zero return for ctypes.windll.kernel32.AttachConsole(parent_in_chain).
When the result is non-zero, attach sys.stdout = sys.stderr = open('CONOUT$', 'wt'); sys.stdin = open('CONIN$', 'rt'), and move along.

@Satheeshkumar-Duraisamy

@sbytnar I followed #2117 to find the parent pid but AttachConsole returns 0 and GetLastError returns 5

I want to redirect all stdout form this script to parent command line when the frozen exe got invoked from command line. Can you help me here...

def isCliCaller():
  '''Helper function to check the grandparent process and identify if we should use cli or window'''
  if getattr(sys, 'frozen', False):
    pid = os.getpid()
    for x in range(3):
      for process in cwmi.Win32_Process(ProcessId=pid):
          name = process.name
          pid = process.ParentProcessId
          print(x, name, pid)
          if 'cmd' in name.lower():
            res = ctypes.windll.kernel32.AttachConsole(pid)
            print(res)
            print("Attach failed with error " + str(ctypes.windll.kernel32.GetLastError()))
            # sys.stdout = sys.stderr = open('CONOUT$', 'wt')
            # sys.stdin = open('CONIN$', 'rt')
            return False
  return True

@earonesty
Copy link

earonesty commented Feb 10, 2022

we use the @sbytnar solution, but the problem is that windows command prompt and powershell will not block when running gui programs, and the results are "messy console output".

i think you need to mark the app as "console" but not actually allocate one to get the desired behavior in powershell. this would, likely, mean a custom bootloader. one that reports itself to windows as a console app, but uses a winmain entry point anyway

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 16, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area:bootloader Caused be or effecting the bootloader feature Feature request platform:Windows pull-request wanted Please submit a pull-request for this, maintainers will not actively work on this.
Projects
None yet
Development

No branches or pull requests

9 participants