【问题标题】:WIndows: subprocess making new console window, losing stdin/outWIndows:创建新控制台窗口的子进程,丢失标准输入/输出
【发布时间】:2011-11-13 03:55:10
【问题描述】:

我使用的是 Windows Vista 和 Python 2.7.2,但答案不必使用 Python。

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

  1. 获取进程制造控制台的标准输入/输出的句柄;或
  2. 让新的 shell 在旧的 shell 中运行(比如从 bash 中调用 bash)?

如果做不到这一点,我可以破解子进程的代码,如何在 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...

【问题讨论】:

  • @eryksun 是的,我正在运行vlc -I rc

标签: python windows stdin stdio filehandle


【解决方案1】:

我还没有让 rc 接口在 Windows 上使用管道标准输入/标准输出;我在所有尝试communicate 或直接写信给stdin 时都会收到IOError。有一个选项--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)

【讨论】:

  • 哇,感谢您提供的信息和套接字解决方案,它运行良好。该代码很有用。我没想到会有这么具体的答案,并认为我在死胡同。谢谢。
【解决方案2】:

关于监控出现在新的 Spawned Console Window 中的 stdOut。

这是解决问题的another question/answer

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

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

注意:至于rc接口的stdIn命令,--rc-host的解决方案由eryksun´s answer描述

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-11
    • 2013-03-01
    • 2012-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    相关资源
    最近更新 更多