具有ctypes的stdout重定向 [英] stdout redirection with ctypes

查看:107
本文介绍了具有ctypes的stdout重定向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将printf函数的输出重定向到Windows上的文件。我在python3上使用ctypes来调用函数。我的代码是:

I'm trying to redirect the output of printf functions to a file on Windows. I'm using ctypes with python3 to invoke the functions. My code is:

import os, sys
from ctypes import *

if __name__ == '__main__':

 print("begin")
 saved_stdout=os.dup(1)
 test_file=open("TEST.TXT", "w")
 os.dup2(test_file.fileno(), 1)
 test_file.close()
 print("python print")
 cdll.msvcrt.printf(b"Printf function 1\n")
 cdll.msvcrt.printf(b"Printf function 2\n")
 cdll.msvcrt.printf(b"Printf function 3\n")
 os.dup2(saved_stdout, 1)
 print("end")

但是当我从Eclipse运行代码,我在屏幕上看到以下内容:

But when I run the code from Eclipse I get the following on the screen:

begin
end
Printf function 1
Printf function 2
Printf function 3

...以及TEST.txt中的以下内容

...and the following in the TEST.txt

python print

当我从cmd运行此命令时,这是屏幕上显示的内容:

When I run this from cmd, this is what is on the screen:

begin
end

..,这在TEST.txt中:

..and this is in the TEST.txt:

python print

当我注释掉第二个 dup2()时语句,例如

When I comment out the second dup2() statement e.g.

import os, sys
from ctypes import *
if __name__ == '__main__':

    print("begin")
    saved_stdout=os.dup(1)
    test_file=open("TEST.TXT", "w")
    os.dup2(test_file.fileno(), 1)
    test_file.close()
    print("python print")
    cdll.msvcrt.printf(b"Printf function 1\n")
    cdll.msvcrt.printf(b"Printf function 2\n")
    cdll.msvcrt.printf(b"Printf function 3\n")
    #os.dup2(saved_stdout, 1)
    print("end")

从Eclipse在屏幕上:

From Eclipse, on the screen:

begin

...并在TEST.txt文件中:

...and in the TEST.txt file:

python print
end
Printf function 1
Printf function 2
Printf function 3

在屏幕上的cmd中:

begin

...并在TEST.txt文件中:

...and in the TEST.txt file:

python print
end

I'我现在完全感到困惑。我在StackOverflow上阅读了所有重定向线程,但不知道发生了什么。
无论如何,我收集到的是C函数访问直接绑定到文件描述符的stdout,而python为此使用了一个特殊对象-stdout File Object。因此,基本 sys.stdout = * something * 不适用于ctypes。
我什至在dup2输出上尝试过 os.fdopen(1),然后调用 flush() printf 语句之后的$ c>,但是此操作不再起作用。
我现在完全没主意了,如果有人对此有解决方案,将不胜感激。

I'm totally confused now. I read all the redirection threads here on StackOverflow and I can't understand what's going on. Anyway, what I've gathered is that C functions access the stdout that is bind directly to the file descriptor, while python uses a special object for that - stdout File Object. So the elementary sys.stdout=*something* doesn't work with ctypes. I've even tried os.fdopen(1) on the dup2-ed output and then calling flush() after every printf statement but this isn't working again. I'm totally out of ideas now and would appreciate if someone have a solution for this.

推荐答案

使用与CPython 3.x使用的相同的C运行时(例如,3.3的msvcr100.dll)。在重定向 stdout 之前和之后,还应包括对 fflush(NULL)的调用。如果程序直接使用Windows API,请采取措施重定向Windows StandardOutput 句柄。

Use the same C runtime that CPython 3.x uses (e.g. msvcr100.dll for 3.3). Also include a call to fflush(NULL) before and after redirecting stdout. For good measure, redirect the Windows StandardOutput handle, in case a program uses the Windows API directly.

如果DLL使用不同的C运行库(具有自己的POSIX文件描述符集),则此操作可能会变得复杂。也就是说,如果在重定向Windows StandardOutput 之后加载它,应该没问题。

This can get complicated if the DLL uses a different C runtime, which has its own set of POSIX file descriptors. That said, it should be OK if it gets loaded after you've redirected Windows StandardOutput.

编辑:

我修改了示例,使其可以在Python 3.5+中运行。 VC ++ 14的新通用CRT使通过ctypes使用C标准I / O变得更加困难。

I've modified the example to run in Python 3.5+. VC++ 14's new "Universal CRT" makes it much more difficult to use C standard I/O via ctypes.

import os
import sys
import ctypes, ctypes.util

kernel32 = ctypes.WinDLL('kernel32')

STD_OUTPUT_HANDLE = -11

if sys.version_info < (3, 5):
    libc = ctypes.CDLL(ctypes.util.find_library('c'))
else:
    if hasattr(sys, 'gettotalrefcount'): # debug build
        libc = ctypes.CDLL('ucrtbased')
    else:
        libc = ctypes.CDLL('api-ms-win-crt-stdio-l1-1-0')

    # VC 14.0 doesn't implement printf dynamically, just
    # __stdio_common_vfprintf. This take a va_array arglist,
    # which I won't implement, so I escape format specificiers.

    class _FILE(ctypes.Structure):
        """opaque C FILE type"""

    libc.__acrt_iob_func.restype = ctypes.POINTER(_FILE)    

    def _vprintf(format, arglist_ignored):
        options = ctypes.c_longlong(0) # no legacy behavior
        stdout = libc.__acrt_iob_func(1)
        format = format.replace(b'%%', b'\0')
        format = format.replace(b'%', b'%%')
        format = format.replace(b'\0', b'%%')
        arglist = locale = None        
        return libc.__stdio_common_vfprintf(
            options, stdout, format, locale, arglist)

    def _printf(format, *args):
        return _vprintf(format, args)

    libc.vprintf = _vprintf
    libc.printf = _printf



def do_print(label):
    print("%s: python print" % label)
    s = ("%s: libc _write\n" % label).encode('ascii')
    libc._write(1, s, len(s))
    s = ("%s: libc printf\n" % label).encode('ascii')
    libc.printf(s)
    libc.fflush(None) # flush all C streams

if __name__ == '__main__':
    # save POSIX stdout and Windows StandardOutput
    fd_stdout = os.dup(1)
    hStandardOutput = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)

    do_print("begin")

    # redirect POSIX and Windows
    with open("TEST.TXT", "w") as test:
        os.dup2(test.fileno(), 1)
        kernel32.SetStdHandle(STD_OUTPUT_HANDLE, libc._get_osfhandle(1))

    do_print("redirected")

    # restore POSIX and Windows
    os.dup2(fd_stdout, 1)
    kernel32.SetStdHandle(STD_OUTPUT_HANDLE, hStandardOutput)

    do_print("end")

这篇关于具有ctypes的stdout重定向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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