molecule
molecule

Reputation: 1121

Python ctypes throws TypeError: one character bytes, bytearray or integer expected for functions with pointer references

I am new to both Python and ctypes the module. Trying to call C++ a function by loading a shared library. Here is the prototype of the function I want to call.

void foo_func(const char *binary, size_t binsz, size_t memsz, void *params, size_t paramssz, void *settings);

And here is the code I have written to call this function.

import ctypes
import pathlib

class virt_buff(ctypes.Structure):
  _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]

if __name__ == "__main__":
  libname = pathlib.Path().absolute() / "build/libfoo.so"
  c_lib = ctypes.CDLL(libname)

  func_param = virt_buff(7, 0)

  with open("build/fib.bin", mode='rb') as file: # b is important -> binary
    binary = file.read()

  c_lib.foo_func(ctypes.c_char(binary), file.tell(), 0x9000 + (file.tell() & ~0xFFF), func_param, 4, NULL)

But when I run this code, it gives me the following output.

Traceback (most recent call last):
File "ct_test.py", line 32, in <module>
  c_lib.foo_func(ctypes.c_char(binary), file.tell(), 0x9000 + (file.tell() & ~0xFFF), virt_param, 4, NULL)
TypeError: one character bytes, bytearray or integer expected

I tried a lot of things and nothing seems to work. Can anyone help me find out what the actual problem is?

Upvotes: 1

Views: 1772

Answers (2)

Mark Tolonen
Mark Tolonen

Reputation: 177901

The first error message is due to trying to pass a single character (c_char) and initializing it with a multiple character data blob. You want c_char_p for C char* instead. But there are other issues after that. Declaring .argtypes and .restype for your function correctly will help ctypes catch errors.

Make the following additional commented fixes:

import ctypes as ct
import pathlib

class virt_buff(ct.Structure):
  _fields_ = [("x", ct.c_int), ("y", ct.c_int)]

if __name__ == "__main__":
  libname = pathlib.Path().absolute() / "build/libfoo.so"

  # Declare argument types and result type that match the C call for ctypes to error check
  # void foo_func(const char *binary, size_t binsz, size_t memsz, void *params, size_t paramssz, void *settings);
  c_lib = ct.CDLL(str(libname)) # on windows, CDLL(libname) failed to accept WindwowsPath.
  c_lib.foo_func.argtypes = ct.c_char_p, ct.c_size_t, ct.c_size_t, ct.c_void_p, ct.c_size_t, ct.c_void_p
  c_lib.foo_func.restype = None

  func_param = virt_buff(7, 0)

  with open("build/fib.bin", mode='rb') as file:
    binary = file.read()
    # Indent under with, so file.tell() is in scope,
    # Pass address of structure using ct.byref() and size with ct.sizeof()
    # Use Python None instead of C NULL.
    c_lib.foo_func(binary, file.tell(), 0x9000 + (file.tell() & ~0xFFF), ct.byref(func_param), ct.sizeof(func_param), None)

Upvotes: 1

Tim Roberts
Tim Roberts

Reputation: 54743

Your parameter is almost certainly supposed to a POINTER to char, and not just a single char:

c_lib.foo_func(ctypes.c_char_p(binary), ... 

Upvotes: 1

Related Questions