2013-07-30 99 views
8

我試圖將printf函數的輸出重定向到Windows上的文件。我使用python3的ctypes來調用函數。我的代碼是:標準輸出重定向與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 File Object。所以小學sys.stdout=*something*不適用於ctypes。 我甚至在DUP2-ED輸出試圖os.fdopen(1),然後調用每一個printf後聲明flush()但這又壞了。 我現在完全沒有想法,並希望如果有人有這個解決方案。

+0

爲什麼要使用'ctypes'打印的東西嗎? – jfs

+0

相關:[我如何防止C共享庫打印在標準輸出蟒蛇?](http://stackoverflow.com/q/5081657/4279)(使用您的文件,而不是'os.devnull') – jfs

+1

我'已經閱讀過這個帖子,這沒有幫助。我的代碼和例子幾乎一樣。我需要ctypes,因爲稍後我會用它來測試一個C庫,它裏面有printfs。 –

回答

4

使用相同的C運行時的CPython 3.x的用途(例如用於msvcr100.dll 3.3)。還包括在重定向stdout之前和之後致電fflush(NULL)。如果程序使用Windows API,則可以重定向Windows StandardOutput句柄。

這可能很複雜,如果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") 
+0

謝謝!我會稍後再嘗試! –

+0

是的,是的,它的工作原理!你在閱讀和嘗試4天后救了我! :) –

+0

順便說一句,如果您使用cdll.LoadLibrary加載其他DLL並且您想要重定向此dll標準輸出,則必須在加載dll之前調用重定向方法,否則在控制檯中運行時可能無法工作。 – lengxuehx