Home > other >  python3 call fread of ctypes cannot get data
python3 call fread of ctypes cannot get data

Time:07-19

Python use ctypes API to call c code. Following code works in Python2, but does not work in Python3.8 . I found result of func libc.fread(that is bytes_read) is 0(but actually should be 282624). Anyone know the root cause?

real_size = 282624
buffer = (ctypes.c_ubyte * real_size)()
length = real_size

libc = ctypes.CDLL(ctypes.util.find_library("c"))

# open file
libc.fopen.restype = ctypes.c_void_p
libc.fopen.argtypes = (ctypes.c_void_p, ctypes.c_void_p)
fp = libc.fopen(file_path, 'rb')
if fp is None:
    raise OSError('Cannot open file {}'.format(file_path))

libc.fclose.argtypes = (ctypes.c_void_p,)

# seek offset
libc.fseek.restype = ctypes.c_int
libc.fseek.argtypes = (ctypes.c_void_p,ctypes.c_long, ctypes.c_int)
if libc.fseek(fp, 0, os.SEEK_SET) != 0:
    libc.fclose(fp)
    raise IOError("fseek to {} failed".format(file_offset))

libc.fread.argtypes = (ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t, ctypes.c_void_p)
libc.fread.restype = ctypes.c_size_t
bytes_read = libc.fread(ctypes.addressof(buffer), 1, length, fp) #why bytes_read is 0?

Or another question is how to use fread to read data from binary file?

CodePudding user response:

ctypes.util.find_library('c') doesn't work on Windows, but given a C runtime library (I hardcoded one), this works:

from __future__ import print_function  # so this code will work on Python 2 as well
import os
import ctypes as ct

# Opaque type so you can't pass any old void*.
# Better type-safety.
class FILE(ct.Structure):
    pass

# represents FILE* from C
PFILE = ct.POINTER(FILE)

libc = ct.CDLL('msvcrt')
libc.fopen.argtypes = ct.c_char_p, ct.c_char_p
libc.fopen.restype = PFILE
libc.fclose.argtypes = PFILE,
libc.fclose.restype = ct.c_int
libc.fseek.argtypes = PFILE, ct.c_long, ct.c_int
libc.fseek.restype = ct.c_int
libc.fread.argtypes = ct.c_void_p, ct.c_size_t, ct.c_size_t, PFILE
libc.fread.restype = ct.c_size_t

buffer = (ct.c_ubyte * real_size)()

fp = libc.fopen(b'test.bin', b'rb') # Use byte strings for char*
if fp is None:
    raise OSError('Cannot open file {}'.format(file_path))

# Not really needed since file starts at offset 0
if libc.fseek(fp, 0, os.SEEK_SET) != 0:
    libc.fclose(fp)
    raise IOError("fseek to {} failed".format(file_offset))

bytes_read = libc.fread(buffer, 1, length, fp)
print(bytes_read)

But then again, so does this without ctypes:

with open('test.bin','rb') as f:
    data = f.read()
print(len(data))
  • Related