Kri*_*hev 9 python windows ctypes
我正在尝试将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")
Run Code Online (Sandbox Code Playgroud)
但是当我从Eclipse运行代码时,我在屏幕上得到以下内容:
begin
end
Printf function 1
Printf function 2
Printf function 3
Run Code Online (Sandbox Code Playgroud)
...以及TEST.txt中的以下内容
python print
Run Code Online (Sandbox Code Playgroud)
当我从cmd运行它时,这就是屏幕上的内容:
begin
end
Run Code Online (Sandbox Code Playgroud)
..这是在TEST.txt中:
python print
Run Code Online (Sandbox Code Playgroud)
当我评论出第二个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")
Run Code Online (Sandbox Code Playgroud)
从Eclipse,在屏幕上:
begin
Run Code Online (Sandbox Code Playgroud)
...并在TEST.txt文件中:
python print
end
Printf function 1
Printf function 2
Printf function 3
Run Code Online (Sandbox Code Playgroud)
从cmd,在屏幕上:
begin
Run Code Online (Sandbox Code Playgroud)
...并在TEST.txt文件中:
python print
end
Run Code Online (Sandbox Code Playgroud)
我现在完全糊涂了.我在StackOverflow上阅读了所有重定向线程,我无法理解发生了什么.无论如何,我收集的是C函数访问直接绑定到文件描述符的stdout,而python使用特殊对象 - stdout文件对象.所以小学sys.stdout=*something*
不适用于ctypes.我甚至尝试os.fdopen(1)
过dup2-ed输出,然后flush()
在每个printf
语句后调用,但这不再起作用.我现在完全没有想法,如果有人有解决方案,我会很感激.
使用与 CPython 3.x 相同的 C 运行时(例如 3.3 的 msvcr100.dll)。还包括对fflush(NULL)
重定向前后的调用stdout
。为了更好的衡量,重定向 WindowsStandardOutput
句柄,以防程序直接使用 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
Run Code Online (Sandbox Code Playgroud)
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")
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2824 次 |
最近记录: |