How can I use ctypes to pass a byteArray into a C function that takes a char* as its argument?

14,109

Solution 1

Mark's answer is quite helpful in that it passes a character array to the C function, which is what the OP really wanted, but in case there's folks finding their way here who really want to pass a byte-array, an approach seems to be to build a ctypes.c_char backed by the memory of your byte-array, and pass that.

My example here disregards the argument declaration recommend by Mark, which indeed seems like a good idea.

import ctypes

# libFoo.c:
# (don't forget to use extern "C" if this is a .cpp file)
#
# void foo(unsigned char* buf, size_t bufSize) {
#   for (size_t n = 0; n < bufSize; ++n) {
#     buf[n] = n;
#   }
# }

fooLib = ctypes.cdll.LoadLibrary('./lib/libFoo.dylib')

ba = bytearray(10)

char_array = ctypes.c_char * len(ba)

fooLib.foo(char_array.from_buffer(ba), len(ba))

for b in ba:
  print b

# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9

Solution 2

The minimum you need (Python 2) is:

hello = "hello"
encryptPy.encrypt(5,hello)
encryptPy.decrypt(5,hello)

But it is good to declare the argument types and return values as well. Full program:

#!python2
import ctypes

encryptPy = ctypes.CDLL('/home/aradhak/Documents/libencrypt.so')

encryptPy.encrypt.argtypes = (ctypes.c_int,ctypes.c_char_p)
encryptPy.encrypt.restype = None
encryptPy.decrypt.argtypes = (ctypes.c_int,ctypes.c_char_p)
encryptPy.decrypt.restype = None

hello = "hello"
encryptPy.encrypt(len(hello),hello)
encryptPy.decrypt(len(hello),hello)

Note that when passing a python byte string, consider the buffer immutable. In this case you only are reading the buffer, but if you need to allow the C function to mutate the string use:

hello = ctypes.create_string_buffer('hello',5)

Below works as well, but will be length 6. A terminating null will be included.

hello = ctypes.create_string_buffer('hello')
Share:
14,109
Admin
Author by

Admin

Updated on July 13, 2022

Comments

  • Admin
    Admin almost 2 years

    I have created a function in C which takes an int size and a char *buffer as arguments. I would like to use ctypes to call this function from python and pass in a python byteArray. I know that first you must compile the C file into a shared library (.so file) and use ctypes to call that function. Here's the code I have so far.

    encrypt.c:

    #include <stdio.h>
    void encrypt(int size, unsigned char *buffer);
    void decrypt(int size, unsigned char *buffer);
    
    void encrypt(int size, unsigned char *buffer){
        for(int i=0; i<size; i++){
            unsigned char c = buffer[i];
            printf("%c",c);
        }
    }
    void decrypt(int size, unsigned char *buffer){
        for(int i=0; i<size; i++){
            unsigned char c = buffer[i];
            printf("%c",c);
        }
    }
    

    And here's the python file:

    import ctypes
    
    encryptPy = ctypes.CDLL('/home/aradhak/Documents/libencrypt.so')
    hello = "hello"
    byteHello = bytearray(hello)
    encryptPy.encrypt(5,byteHello)
    encryptPy.decrypt(5,byteHello)
    

    Basically I want to call the C method from python, pass through a python byte array, and have it iterate through the array and print each element

  • orion elenzil
    orion elenzil about 6 years
    this is very helpful, thank you. for the python-ignorant out there in the future, if your C function takes only a single argument, specify argtype as [ctypes.some_type] rather than as (ctypes.some_type). the latter is apparently interpreted as a single item rather than a list containing a single item.
  • Eugene Mayevski 'Callback
    Eugene Mayevski 'Callback over 4 years
    Unfortunately the link takes to a different topic. But thank you for the code anyway - it seems to work (though I am not sure that there's no memory allocated when char_array is assigned to initially.
  • osvein
    osvein almost 4 years
    @orionelenzil try (ctypes.some_type,) with a comma
  • Mark Tolonen
    Mark Tolonen almost 4 years
    @osvein Parentheses aren’t needed except for clarity for order of operations. .argtypes = c_int, works. The comma makes it a tuple.
  • Jay Sullivan
    Jay Sullivan almost 4 years
    TLDR: Convert your byte array to a char array with (ctypes.c_char*len(ba)).from_buffer(bytearray(ba)), where ba is your byte array.
  • Mark Tolonen
    Mark Tolonen about 3 years
    TLDR: In Python 3 just usebytes(ba) (for immutable buffer) or create_string_buffer(bytes(ba)) (for mutable).