2013-07-30 22 views
8

Printf işlevlerinin çıktısını Windows'taki bir dosyaya yeniden yönlendirmeye çalışıyorum. Fonksiyonları çağırmak için python3 ile ctypes kullanıyorum.Ctypes ile stdout yönlendirmesi

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") 

Ama Eclipse kodunu çalıştırdığınızda Ekranda şu olsun: My kodudur

begin 
end 
Printf function 1 
Printf function 2 
Printf function 3 

... ve TEST.TXT

python print 
aşağıdaki

begin 
end 
: Ben cmd Bu çalıştırdığınızda

, bu ekranda budur

..ve bu test.txt içinde: Ben mesela ikinci dup2() deyiminde açýklama

python print 

Ekranda Eclipse itibaren

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") 

:

begin 

... ve Test.txt dosyada: cmd itibaren

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

, ekranda:

begin 

... ve TEST.txt dosyasında:

python print 
end 

Şimdi tamamen karıştı. Tüm yönlendirme konularını StackOverflow üzerinde okudum ve neler olduğunu anlayamıyorum. Her neyse, topladığım şey, C işlevlerinin doğrudan dosya tanıtıcısına bağlanan stdout'a erişmesidir, python bunun için özel bir nesne kullanır - stdout File Object. Dolayısıyla, temel sys.stdout=*something*, klişelerle çalışmaz. Dup2-ed çıkışında os.fdopen(1) denedim ve her printf ifadesinden sonra flush() numaralı telefonu aradım ancak bu işlem tekrar çalışmıyor. Şimdi tamamen fikirden çıktım ve bunun için bir çözümü varsa memnun olurum.

+0

neden bir şey yazdırmak için 'ctypes' kullanırım? – jfs

+0

ile ilgili: [C paylaşılan kitaplığını python'da stdout'a yazdırmayı nasıl engellerim?] (Http://stackoverflow.com/q/5081657/4279) (dosyanızı "os.devnull" yerine kullanın) – jfs

+1

Ben ' ve bu konuyu okuyun ve yardımcı olmuyor. Kodum örneklerinkiyle neredeyse aynı. Ctiplere ihtiyacım var çünkü daha sonra bunu printfs içeren bir C kütüphanesini test etmek için kullanacağım. –

cevap

4

CPython 3.x'in kullandığı aynı C çalışma zamanını kullanın (ör., 3.3 için msvcr100.dll). Ayrıca stdout yeniden yönlendirmeden önce ve sonra fflush(NULL) için bir çağrı ekleyin. Bir programın Windows API'yi doğrudan kullanması durumunda, iyi önlem için Windows StandardOutput tanıtıcısını yeniden yönlendirin. DLL POSIX dosya tanımlayıcıları kendi belirledi farklı bir C çalışma zamanını kullanıyorsa

Bu karmaşık olabiliyor. Bu, Windows StandardOutput yeniden yönlendirildikten sonra yüklenirse, Tamam olmalıdır dedi.

Düzenleme:

Ben 3.5+ Python çalışmasına örnek modifiye ettik. VC++ 14'ün yeni "Evrensel CRT'si", C standart I/O'yu ctypes ile kullanmakta çok daha zor hale geliyor.

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

Teşekkürler! Daha sonra deneyeceğim! –

+0

Evet, evet, işe yarıyor! Okuma ve denemeden 4 gün sonra beni kurtardın! :) –

+0

BTW, diğer dll'yi yüklemek için cdll.LoadLibrary kullanırsanız ve bu dll stdout'unu yeniden yönlendirmek istiyorsanız, dll yüklendikten önce yönlendirme yöntemini çağırmanız gerekir, aksi halde konsolda çalışırken çalışmayabilir. – lengxuehx