python - 如何在 Windows 中重定向 Python 中的 C 级流?

标签 python windows redirect stdout

Eli Bendersky 已经彻底解释了如何“Redirecting all kinds of stdout in Python”,特别是重定向 C 级流,例如共享库 (dll) 的标准输出。但是这个例子是在linux下,在windows下不行,主要是以下几行:

libc = ctypes.CDLL(None)
c_stdout = ctypes.c_void_p.in_dll(libc = ctypes.CDLL(None), 'stdout')

我们如何让它在 Windows 中工作?

最佳答案

我在 Drekin's code 中找到了答案.基于此,我对 Eli Bendersky's example 做了一个小改动。 :

更新:此代码已在 Windows 上的 Python 3.4 64 位和 Linux 上的 Python 3.5 64 位上进行了测试。对于 Windows 上的 Python 3.5,请参阅 eryksun 的评论。

from contextlib import contextmanager
import ctypes
import io
import os
import sys
import tempfile

import ctypes.util
from ctypes import *
import platform

if platform.system() == "Linux":
    libc = ctypes.CDLL(None)
    c_stdout = ctypes.c_void_p.in_dll(libc, 'stdout')
if platform.system() == "Windows":
    class FILE(ctypes.Structure):
        _fields_ = [
            ("_ptr", c_char_p),
            ("_cnt", c_int),
            ("_base", c_char_p),
            ("_flag", c_int),
            ("_file", c_int),
            ("_charbuf", c_int),
            ("_bufsize", c_int),
            ("_tmpfname", c_char_p),
        ]

    # Gives you the name of the library that you should really use (and then load through ctypes.CDLL
    msvcrt = CDLL(ctypes.util.find_msvcrt())
    libc = msvcrt # libc was used in the original example in _redirect_stdout()
    iob_func = msvcrt.__iob_func
    iob_func.restype = POINTER(FILE)
    iob_func.argtypes = []

    array = iob_func()

    s_stdin = addressof(array[0])
    c_stdout = addressof(array[1])


@contextmanager
def stdout_redirector(stream):
    # The original fd stdout points to. Usually 1 on POSIX systems.
    original_stdout_fd = sys.stdout.fileno()

    def _redirect_stdout(to_fd):
        """Redirect stdout to the given file descriptor."""
        # Flush the C-level buffer stdout
        libc.fflush(c_stdout)
        # Flush and close sys.stdout - also closes the file descriptor (fd)
        sys.stdout.close()
        # Make original_stdout_fd point to the same file as to_fd
        os.dup2(to_fd, original_stdout_fd)
        # Create a new sys.stdout that points to the redirected fd
        sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd, 'wb'))

    # Save a copy of the original stdout fd in saved_stdout_fd
    saved_stdout_fd = os.dup(original_stdout_fd)
    try:
        # Create a temporary file and redirect stdout to it
        tfile = tempfile.TemporaryFile(mode='w+b')
        _redirect_stdout(tfile.fileno())
        # Yield to caller, then redirect stdout back to the saved fd
        yield
        _redirect_stdout(saved_stdout_fd)
        # Copy contents of temporary file to the given stream
        tfile.flush()
        tfile.seek(0, io.SEEK_SET)
        stream.write(tfile.read())
    finally:
        tfile.close()
        os.close(saved_stdout_fd)

if __name__ == '__main__':
    f = io.BytesIO()
    print('...')

    with stdout_redirector(f):
        print('foobar')
        print(12)
        libc.puts(b'this comes from C')
        os.system('echo and this is from echo')

    print('Got stdout:"\n{0}\n"'.format(f.getvalue().decode('utf-8')))

    print('Resuming normal operation...')

关于python - 如何在 Windows 中重定向 Python 中的 C 级流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35304920/

相关文章:

python - 如何在每行的开头添加字符串?

windows - 如何在不使用AWS SDK的情况下解密AWS Windows密码?

php - 使用 PHP 输出 CSS、javascript 和图像

apache - 使用 .htaccess 重定向站点但排除一个文件夹

python - 何时在 Python 中内联元类的定义?

python - 将 Python Pandas DataFrame 写入 Word 文档

python - 如何获得SVM精度?

c++ - 这是管理 COM 初始化的好方法吗?

c - 如何使用 TerminateProcess 终止进程

wordpress - 使用 .htaccess 将 HTTP 重定向到 HTTPs