2013-07-30 10 views
8

Sto provando a reindirizzare l'output delle funzioni di printf su un file su Windows. Sto usando i ctypes con python3 per invocare le funzioni. Il mio codice è:reindirizzamento stdout con i tipi di carattere

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

Ma quando ho eseguito il codice da Eclipse ottengo il seguente sullo schermo:

begin 
end 
Printf function 1 
Printf function 2 
Printf function 3 

... e quanto segue nella TEST.TXT

python print 

Quando eseguo questo da cmd, questo è ciò che è sullo schermo:

begin 
end 

..e questo è nel TEST.TXT:

python print 

Quando io commento la seconda dup2() dichiarazione per esempio

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

Da Eclipse, sullo schermo:

begin 

... e nel file Test.txt:

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

Da cmd, sullo schermo:

begin 

... e nel file TEST.txt:

python print 
end 

Sono completamente confuso ora. Ho letto tutti i thread di reindirizzamento qui su StackOverflow e non riesco a capire cosa sta succedendo. Comunque, quello che ho raccolto è che le funzioni C accedono allo stdout che è collegato direttamente al descrittore del file, mentre python usa un oggetto speciale per quell'oggetto file stdout. Quindi la elementare sys.stdout=*something* non funziona con i tipi. Ho anche provato os.fdopen(1) sull'output di dup2-ed e quindi chiamando flush() dopo ogni istruzione printf ma questo non funziona di nuovo. Ora sono completamente fuori di idee e apprezzerei se qualcuno avesse una soluzione per questo.

+0

Perché usi 'ctypes' di stampare qualcosa? – jfs

+0

correlate: [Come è possibile impedire una libreria C condivisa per stampare su stdout in Python?] (Http://stackoverflow.com/q/5081657/4279) (utilizzare il file invece di 'os.devnull') – jfs

+1

I' Ho letto questa discussione e non è utile. Il mio codice è quasi uguale a quello degli esempi. Ho bisogno di ctypes perché lo userò più tardi per testare un'unità in una libreria C che contiene printfs. –

risposta

4

Utilizzare lo stesso runtime C utilizzato da CPython 3.x (ad esempio msvcr100.dll per 3.3). Includere anche una chiamata a fflush(NULL) prima e dopo il reindirizzamento stdout. Per buona misura, reindirizzare l'handle di Windows StandardOutput, nel caso in cui un programma utilizzi direttamente l'API di Windows.

Questo può essere complicato se la DLL utilizza un runtime C diverso, che ha il proprio set di descrittori di file POSIX. Detto questo, dovrebbe essere OK se viene caricato dopo aver reindirizzato Windows StandardOutput.

Edit:

Ho modificato l'esempio per l'esecuzione in Python 3.5+. Il nuovo "CRT universale" di VC++ 14 rende molto più difficile l'utilizzo degli I/O standard C tramite i 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") 
+0

Grazie! Ci proverò più tardi! –

+0

Sì, sì, funziona! Mi hai salvato dopo 4 giorni di lettura e prova! :) –

+0

BTW, se si utilizza cdll.LoadLibrary per caricare altre DLL e si desidera reindirizzare questo dll stdout, è necessario chiamare il metodo di reindirizzamento prima che la DLL sia stata caricata, altrimenti potrebbe non funzionare quando eseguito in console. – lengxuehx

Problemi correlati