如何在 Windows 中的 Python 中重定向 C 级流? [英] How to redirect C-level streams in Python in Windows?

查看:54
本文介绍了如何在 Windows 中的 Python 中重定向 C 级流?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Eli Bendersky 详细解释了如何重定向Python 中的各种标准输出",特别是重定向 C 级流,例如共享库 (dll) 的标准输出.不过这个例子是在Linux下,在windows下不起作用,主要是以下几行:

Eli Bendersky has explained thoroughly how to "Redirecting all kinds of stdout in Python", and specifically Redirecting C-level streams, e.g. stdout of a shared library (dll). However, the example is in Linux and does not work in windows, mainly due to the following lines:

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

我们如何让它在 Windows 中运行?

How can we make it work in Windows?

推荐答案

我在 德雷金的代码.基于此,我对 Eli Bendersky 的例子:

I found the answer buried in Drekin's code. Based on that, I made a small change to Eli Bendersky's example:

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

Update: This code has been tested on Python 3.4 64-bit on Windows and Python 3.5 64-bit on Linux. For Python 3.5 on Windows, please see eryksun's comment.

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

这篇关于如何在 Windows 中的 Python 中重定向 C 级流?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆