WIndows:子进程创建新的控制台窗口,丢失stdin / out

时间:2011-11-13 03:55:11

标签: python windows stdin stdio filehandle

我正在使用Windows Vista和Python 2.7.2,但答案不一定是Python。

因此,对于命令行程序,例如`dir',我可以正常启动并与子进程stdin / stdout(使用python)进行交互。
- 但是 -
我现在想调用的程序喜欢在Windows(而不是curses)上创建一个新的控制台窗口,使用新句柄,即使从预先存在的cmd.exe窗口运行也是如此。 (奇怪,因为它是VLC的“远程控制”界面。)有没有办法:

  1. 获取进程控制台的stdin / out的句柄;或
  2. 让新的shell在旧的内部运行(比如从bash中调用bash)?
  3. 如果失败了,那么我可以破解子进程的代码,如何在Windows中设置新的控制台并在/输出转移?

    编辑: 即。

    >>> 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()
    ''
    # [...]
    

    但是出现的新控制台窗口也不接受键盘输入。

    通常:

    >>> 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...
    

2 个答案:

答案 0 :(得分:1)

我还没有让rc接口在Windows上使用管道stdin / stdout;我在IOError的所有尝试中获得communicate或直接写入stdin。有一个选项--rc-fake-tty可以让rc接口在Linux上编写脚本,但它在Windows中不可用 - 至少在我有点过时版本的VLC(1.1.4)中没有。另一方面,使用套接字接口似乎工作正常。

分配给startupinfo选项的结构 - 由Win32 CreateProcess函数使用 - 可以配置为隐藏进程窗口。但是,对于VLC rc控制台,我认为使用现有的--rc-quiet选项更简单。通常,以下是配置startupinfo以隐藏进程窗口的方法:

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

只是要完成 - 如果使用管道也在系统上失败 - 这是我使用--rc-host选项使用套接字进行通信的一个小实验。它还使用--rc-quiet来隐藏控制台。这只是打印帮助和退出。我没有测试任何其他东西。我检查它在Python版本2.7.2和3.2.2中有效。 (我知道你没有要求这个,但也许它对你有用。)

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)

答案 1 :(得分:0)

参考监视出现在新的Spawned控制台窗口中的stdOut。

这是解决问题的another question/answer

总结(由Adam M-W回答):

  • 通过以安静模式--intf=dummy --dummy-quiet--intf=rc --rc-quiet启动vlc来抑制新生成的控制台。
  • 监控已启动流程的标准

注意:对于rc接口的stdIn命令,eryksun´s answer

描述了--rc-host解决方案