我正在尝试将printf函数的输出重定向到Windows上的文件。我正在使用python3的ctypes来调用函数。我的代码是:
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运行代码时,我在屏幕上看到以下内容:
begin
end
Printf function 1
Printf function 2
Printf function 3
...以及TEST.txt中的以下内容
python print
当我从cmd运行时,这就是屏幕上的内容:
begin
end
..这是在TEST.txt:
python print
当我注释掉第二个dup2()
语句时,例如
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,在屏幕上:
begin
...并在TEST.txt文件中:
python print
end
Printf function 1
Printf function 2
Printf function 3
从cmd,在屏幕上:
begin
...并在TEST.txt文件中:
python print
end
我现在完全糊涂了。我在StackOverflow上阅读了所有重定向线程,我无法理解发生了什么。
无论如何,我收集的是C函数访问直接绑定到文件描述符的stdout,而python使用特殊对象 - stdout文件对象。因此,基本sys.stdout=*something*
不适用于ctypes。
我甚至在dup2-ed输出上尝试os.fdopen(1)
,然后在每个flush()
语句后调用printf
,但这不再有效。
我现在完全没有想法,如果有人有解决方案,我会很感激。
答案 0 :(得分:4)
使用CPython 3.x使用的相同C运行时(例如,msvcr100.dll for 3.3)。还包括在重定向fflush(NULL)
之前和之后调用stdout
。为了更好地衡量,请重定向Windows StandardOutput
句柄,以防程序直接使用Windows API。
如果DLL使用不同的C运行时,它会变得复杂,它有自己的一组POSIX文件描述符。也就是说,如果在重定向Windows StandardOutput
之后加载它,应该没问题。
修改强>
我已修改该示例以在Python 3.5+中运行。 VC ++ 14的新“通用CRT”使得通过ctypes使用C标准I / O变得更加困难。
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")