WIndows:子进程创建新的控制台窗口,丢失标准输入/输出

发布于 2024-12-15 18:36:09 字数 1103 浏览 0 评论 0原文

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

因此,我可以正常启动子进程 stdin/stdout 并与之交互(使用 python),对于诸如“dir”之类的命令行程序。
- 然而 -
我现在想要调用的程序喜欢在 Windows 上为自己创建一个新的控制台窗口(不是curses),并带有新的句柄,即使是从预先存在的 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...

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

纵情客 2024-12-22 18:36:09

我还没有获得在 Windows 上使用管道 stdin/stdout 的 rc 接口;在所有尝试通信或直接写入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)

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)
℉絮湮 2024-12-22 18:36:09

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

这是另一个问题/答案 就解决了这个问题。

总之(如 Adam MW 的回答):

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

注意:对于rc接口的stdIn命令,--rc-host解决方案由eryksun的回答

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文