WIndows: subprocess making new console window, losing stdin/out

六月ゝ 毕业季﹏ 提交于 2019-12-23 18:53:27

问题


I'm using Windows Vista and Python 2.7.2, but answers needn't be in Python.

So I can start and interact with a subprocesses stdin/stdout normally (using python), for command-line programs such as `dir'.
- however -
the program I now want to call likes to make a new console window for itself on Windows (not curses), with new handles, even when run from a pre-existing cmd.exe window. (Odd, as it's the "remote control" interface of VLC.) Is there any way of either:

  1. getting the handles for the process-made console's stdin/out; or
  2. getting the new shell to run within the old (like invoking bash from within bash)?

Failing that, so that I can hack the subprocesses' code, how would a new console be set up in Windows and in/output transferred?

Edit: I.e.

>>> p = Popen(args=['vlc','-I','rc'],stdin=PIPE,stdout=PIPE)
# [New console appears with text, asking for commands]
>>> p.stdin.write("quit\r\n")
Traceback:
    File "<stdin>", line 1, in <module>
IOError: [Errno 22] Invalid argument
>>> p.stdout.readline()
''
>>> p.stdout.readline()
''
# [...]

But the new console window that comes up doesn't accept keyboard input either.

Whereas normally:

>>> p = Popen(args=['cmd'],stdin=PIPE,stdout=PIPE)
>>> p.stdin.write("dir\r\n")
>>> p.stdin.flush()
>>> p.stdout.readline() #Don't just do this IRL, may block.
'Microsoft Windows [Version...

回答1:


I haven't gotten the rc interface to work with a piped stdin/stdout on Windows; I get IOError at all attempts to communicate or write directly to stdin. There's an option --rc-fake-tty that lets the rc interface be scripted on Linux, but it's not available in Windows -- at least not in my somewhat dated version of VLC (1.1.4). Using the socket interface, on the other hand, seems to work fine.

The structure assigned to the startupinfo option -- and used by the Win32 CreateProcess function -- can be configured to hide a process window. However, for the VLC rc console, I think it's simpler to use the existing --rc-quiet option. In general, here's how to configure startupinfo to hide a process window:

startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.Popen(cmd, startupinfo=startupinfo)

Just to be complete -- in case using pipes is failing on your system too -- here's a little demo I cooked up using the --rc-host option to communicate using a socket. It also uses --rc-quiet to hide the console. This just prints the help and quits. I haven't tested anything else. I checked that it works in Python versions 2.7.2 and 3.2.2. (I know you didn't ask for this, but maybe it will be useful to you nonetheless.)

import socket
import subprocess
from select import select

try:
    import winreg
except ImportError:
    import _winreg as winreg

def _get_vlc_path():
    views = [(winreg.HKEY_CURRENT_USER, 0),
             (winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_64KEY),
             (winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_32KEY)]
    subkey = r'Software\VideoLAN\VLC'
    access = winreg.KEY_QUERY_VALUE
    for hroot, flag in views:
        try:
            with winreg.OpenKey(hroot, subkey, 0, access | flag) as hkey:
                value, type_id = winreg.QueryValueEx(hkey, None)
                if type_id == winreg.REG_SZ:
                    return value
        except WindowsError:
            pass
    raise SystemExit("Error: VLC not found.")

g_vlc_path = _get_vlc_path()

def send_command(sock, cmd, get_result=False):
    try:
        cmd = (cmd + '\n').encode('ascii')
    except AttributeError:
        cmd += b'\n'
    sent = total = sock.send(cmd)
    while total < len(cmd):
        sent = sock.send(cmd[total:])
        if sent == 0:
            raise socket.error('Socket connection broken.')
        total += sent
    if get_result:
        return receive_result(sock)

def receive_result(sock):
    data = bytearray()
    sock.setblocking(0)
    while select([sock], [], [], 1.0)[0]:
        chunk = sock.recv(1024)
        if chunk == b'': 
            raise socket.error('Socket connection broken.')
        data.extend(chunk)
    sock.setblocking(1)
    return data.decode('utf-8')

def main(address, port):
    import time
    rc_host = '{0}:{1}'.format(address, port)
    vlc = subprocess.Popen([g_vlc_path, '-I', 'rc', '--rc-host', rc_host, 
                            '--rc-quiet'])
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect((address, port))
        help_msg = send_command(sock, 'help', True)
        print(help_msg)
        send_command(sock, 'quit')
    except socket.error as e:
        exit("Error: " + e.args[0])
    finally:
        sock.close()
        time.sleep(0.5)
        if vlc.poll() is None:
            vlc.terminate()

if __name__ == '__main__':
    main('localhost', 12345)



回答2:


With reference to monitoring the stdOut which appears in the new Spawned Console Window.

Here´s another question/answer that solves the problem.

In summary (as answered by Adam M-W ):

  • Suppress the new spawned console by launching vlc in quiet mode --intf=dummy --dummy-quiet or --intf=rc --rc-quiet.
  • Monitor stdErr of launched process

Note: As for stdIn commands for the rc interface, the --rc-host solution is described by eryksun´s answer



来源:https://stackoverflow.com/questions/8109488/windows-subprocess-making-new-console-window-losing-stdin-out

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!