0

I want to set a C char** pointer, called results, in Python. The variable is in a dll I have loaded. I want to set results so that it points to a string in Python. I want to get the string I created in Python (or at least a copy of it since ctypes does a lot of copying) to be pointed to by the C variable results. So I have in Python product_class = (ctypes.c_char_p)(b"321"). I want to set results to the value "321".

Here is the code I have written. It does not work. It does not even change the C-variable results.

# py_parse_pdl_func function is a callback which is called from a c dll which has been loaded into the python prorgram.
# Here is the declaration of the callback in c
# typedef int (*tsl_pdl_cb_t)(void *pz_prv, const char **results, const char* query);
# so am trying to set results to point to a string "321"


def py_parse_pdl_func(pz_prv, py_results, query):
    global product_class_void
    product_class = (ctypes.c_char_p)(b"321")
    product_class_void = ctypes.cast(product_class, ctypes.c_void_p)
    py_results.contents = ctypes.c_long(product_class_void.value)
  
    return 1
0

1 Answer 1

1

Here's a reproducible example. You may need to keep a reference to the string returned since Python could deallocate it at any time.

test.c

#include <stdio.h>

typedef int (*tsl_pdl_cb_t)(void *pz_prv, const char **results, const char* query);

__declspec(dllexport)
int function_using_callback(tsl_pdl_cb_t callback) {
    char* results = NULL;
    int retval = 0;

    if(callback)
        retval = callback(NULL, &results, "the query");
    printf("results = '%s'\n", results);
    return retval;
}

test.py

import ctypes as ct

CALLBACK = ct.CFUNCTYPE(ct.c_int, ct.c_void_p, ct.POINTER(ct.c_char_p), ct.c_char_p)

dll = ct.CDLL('./test')
dll.function_using_callback.argtypes = CALLBACK,
dll.function_using_callback.restype = ct.c_int

@CALLBACK
def py_parse_pdl_func(pz_prv, py_results, query):
    py_results[0] = b'321'
    return 1

retval = dll.function_using_callback(py_parse_pdl_func)
print('retval =', retval)

Output:

results = '321'
retval = 1
Sign up to request clarification or add additional context in comments.

5 Comments

Superb. That worked for me also. One question. My code was identical to yours except I used '(results.contents).contents = b"321"' ' instead of 'results[0] = b"321"'. Why do you think that did not work?. I have not seen that 'indexing syntax' in the ctypes documentation. I thought you had to use contents attribute to de-reference,
@drlolly .contents reads the pointer, but it returns a new object of the contents because altering it wouldn't change the original char* result passed in. Using indexing directly writes to the original object. I tracked down the problem by printing the pointers of &results and results before and after the callback in C, and comparing to the addresses received and changed in Python.
The documentation mentions this: "Note that ctypes does not have OOR (original object return), it constructs a new, equivalent object each time you retrieve an attribute:"
Your comment ‘Using indexing directly accesses the original object’, is the key to this problem. One point is I don’t understand where in the ctypes documentation that is stated. The documentation hints at this when it says ‘You can access or change arbitrary memory locations. ‘ But it does not explicitly say that the location is in the c module rather than the python module.
For others information: I have used double indexing [0][0] to access a ptr-to-prt-int in c. Doing this, the value of the ptr-to-int did not change, it pointed to the same integer. The value of the integer was changed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.