如何使用Ctypes模块从python调用的C函数访问数据

时间:2019-06-28 06:01:18

标签: python c python-3.x windows ctypes

我正在尝试从ctypes库返回到python的非返回C函数调用中获取数据。

C堆栈从func A()获取输入,调用func B()进行处理,并调用func C()通过硬件(例如UART)发送输出。

我正在使用ctypes库从python调用func A()。 func C当前是包装器代码,可将数据打印到控制台。这可以正常工作,但是我有一个GUI(PyQt5),我想在上面显示此数据(字符串)。

根据本文,我目前正在使用StdOut重定向。 https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/ 但是stdout重定向对我来说效果不佳,某些输出仍将输出到stdout并在某些输出后停止工作。

这是我目前正在使用的。

Cfile.c:

#include "stdio.h"
#include "stdint.h"
void test_consoleout()
{
    fprintf(stdout,"0xDE,0xAD,0xBE,0xEF\n");
    fflush(stdout);
}

Pythonfile.py

from ctypes import *
import ctypes.util
import time, sys, threading, os, io, tempfile
from contextlib import contextmanager
import platform

if platform.system() == "Linux":
    libc = ctypes.CDLL(None)
    c_stdout = ctypes.c_void_p.in_dll(libc, 'stdout')
if platform.system() == "Windows":
    class FILE(ctypes.Structure):
        _fields_ = [
            ("_ptr", c_char_p),
            ("_cnt", c_int),
            ("_base", c_char_p),
            ("_flag", c_int),
            ("_file", c_int),
            ("_charbuf", c_int),
            ("_bufsize", c_int),
            ("_tmpfname", c_char_p),
        ]

    ucrtbase = CDLL("ucrtbase")
    libc = ucrtbase
    iob_func = ucrtbase.__acrt_iob_func
    iob_func.restype = c_void_p
    iob_func.argtypes = [c_int]
    s_stdin = iob_func(0)
    c_stdout = iob_func(1)

@contextmanager
def stdout_redirector(stream):
    # The original fd stdout points to. Usually 1 on POSIX systems.
    original_stdout_fd = sys.stdout.fileno()

    def _redirect_stdout(to_fd):
        """Redirect stdout to the given file descriptor."""
        # Flush the C-level buffer stdout
        libc.fflush(c_stdout)
        # Flush and close sys.stdout - also closes the file descriptor (fd)
        sys.stdout.close()
        # Make original_stdout_fd point to the same file as to_fd
        os.dup2(to_fd, original_stdout_fd)
        # Create a new sys.stdout that points to the redirected fd
        sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd, 'wb'))

    # Save a copy of the original stdout fd in saved_stdout_fd
    saved_stdout_fd = os.dup(original_stdout_fd)
    try:
        # Create a temporary file and redirect stdout to it
        tfile = tempfile.TemporaryFile(mode='w+b')
        _redirect_stdout(tfile.fileno())
        # Yield to caller, then redirect stdout back to the saved fd
        yield
        _redirect_stdout(saved_stdout_fd)
        # Copy contents of temporary file to the given stream
        tfile.flush()
        tfile.seek(0, io.SEEK_SET)
        stream.write(tfile.read())
    finally:
        tfile.close()
        os.close(saved_stdout_fd)

C_STACK = CDLL(".\CStack.dll")

while(True):  
    f = io.BytesIO()    
    with stdout_redirector(f):
        C_STACK .test_consoleout()
    time.sleep(0.05)
    print('Got stdout: "{0}"'.format(f.getvalue()))

这是推荐的吗?有没有更好的方法(可靠且开销最小)?我正在使用Windows。

0 个答案:

没有答案