Reputation: 71
I'm trying to pass a string (as a pointer) from python to a C function using cTypes.
The c function needs to get a pointer to a string (array of chars) but I haven't successfully got this to work with multiple chars, but my only success(kind of successful, look at the output) is with 1 char and would love for some help!
I need to send a pointer of a string to char - (unsigned char * input)
My Python Code:
def printmeP(CHAR):
print("In Print function")
print(CHAR)
c_sends = pointer(c_char_p(CHAR.encode('utf-8')))
print("c_sends: ")
print(c_sends[0])
python_p_printme(c_sends)
print("DONE function - Python")
print(c_sends[0])
return
from ctypes import c_double, c_int,c_char, c_wchar,c_char_p, c_wchar_p, pointer, POINTER,create_string_buffer, byref, CDLL
import sys
lib_path = '/root/mbedtls/programs/test/mylib_linux.so' .format(sys.platform)
CHAR = "d"
try:
mylib_lib = CDLL(lib_path)
except:
print('STARTING...' )
python_p_printme = mylib_lib.printme
python_p_printme.restype = None
printmeP(CHAR)
My C Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void printme(char * param) {
printf("\nStart c Function!\n");
printf("%s\n Printing param\n ", param);
char add = '!';
strncat(param, &add, 1);
printf("%s\n Printing param\n ", param);
printf("Function Done - c\n");
}
My Output:
In Print function
d <--- this is what i am sending
c_sends:
b'd' <--- this is after encoding
��[� <-------------=|
Printing param |
��[� | This is the c code print
Printing param | **Question:** Why does it print '�' and no what's supposed to be printed
Function Done - c <--=|
DONE function - Python
b'd!' <--------------------- this is the last print that prints after the change.
Would love for some help, thanks to everyone that participated :)
sincerely, Roe
Upvotes: 6
Views: 7541
Reputation: 177481
There are a number of problems:
.argtypes
for your functions. It will catch errors passing incorrect parameters. Add the line below and note it is plural and is a tuple of argument types. The comma makes a 1-tuple:python_p_printme.argtypes = c_char_p,
Once you make that change, you'll get an error because this code:
c_sends = pointer(c_char_p(CHAR.encode('utf-8')
is actually sending a C char**
(a pointer to a c_char_p). Once you've set the argtypes properly, you can just call the function with a byte string and it will work. Your function becomes:
def printmeP(CHAR):
print("In Print function")
print(CHAR)
python_p_printme(CHAR.encode())
print("DONE function - Python")
create_unicode_buffer
(for c_wchar_p) or create_string_buffer
(for c_char_p); otherwise, the strcat
in your C code is going to corrupt the Python string.Here's a full example:
test.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// For Windows compatibility
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
// For C++ compiler compatibility
#ifdef __cplusplus
extern "C" {
#endif
API void alter_me(char* param, size_t length) {
// truncates if additional info exceeds length
strncat_s(param, length, " adding some info", length - 1);
}
#ifdef __cplusplus
}
#endif
test.py
from ctypes import *
lib = CDLL('./test')
alter_me = lib.alter_me
alter_me.argtypes = c_char_p,c_size_t
alter_me.restype = None
data = create_string_buffer(b'test',size=10)
alter_me(data,sizeof(data))
print(data.value)
data = create_string_buffer(b'test',size=50)
alter_me(data,sizeof(data))
print(data.value)
Output:
b'test addi'
b'test adding some info'
Note you do not need to use create_string_buffer
if the C function does not alter the buffer, such as if the C parameter is const char*
. Then you could just call printme(b'test string')
.
Upvotes: 8
Reputation: 27096
You could use create_string_buffer.
The documentation can be found here: https://docs.python.org/3/library/ctypes.html#ctypes.create_string_buffer
ctypes.create_string_buffer(init_or_size, size=None)
This function creates a mutable character buffer. The returned object is a ctypes array of c_char.
init_or_size must be an integer which specifies the size of the array, or a bytes object which will be used to initialize the array items.
With buf.value.decode("utf-8")
you can convert the buffer back to a UTF-8 python string.
A small example with your C code library might look like this:
from ctypes import *
mylib_lib = cdll.LoadLibrary("<your-lib>")
mylib_lib.printme.argtypes = c_char_p,
buf = create_string_buffer(128)
buf.value = b'Hello World'
mylib_lib.printme(buf)
print("print on python side:", buf.value.decode("utf-8"))
It would output:
Start c Function!
Hello World
...
print on python side: Hello World!
on the console.
Upvotes: 2