0

I'm calling a C dll from Python using ctypes and having some trouble passing the correct parameters to the IBSU_RegisterCallbacks function, defined below.

int WINAPI IBSU_RegisterCallbacks
    (const int         handle, 
     const IBSU_Events event, 
     void             *pCallbackFunction,
     void             *pContext);

It describes the final parameter as:

/* pContext Pointer to user context that will be passed to callback function.*/

A working C++ example calls the function like this:

RegisterCallbacks(0, 1, OnCallback, this);

So far in Python I have the below code (relevant section shown only), which crashes Python (no traceback) shortly after the event is triggered and callback run! is printed.

from ctypes import *

class IBSU(object):
    _dll = WinDLL('ibsu.dll')

    _C_CALLBACK = CFUNCTYPE(
        None,        
        c_int,                                         # device handle
        c_void_p)                                      # user context

    _register_callbacks = _dll.IBSU_RegisterCallbacks
    _register_callbacks.restype = c_int
    _register_callbacks.argtypes = (c_int,             # device handle
                                    c_int,             # event type
                                    c_void_p,          # callback function
                                    c_void_p)          # user context

    def _get_c_callback(self):
        def callback(handle,                           # device handle
                     p_context):                       # user context
            print 'callback run!'
        return self._C_CALLBACK(callback) 

    def register_callbacks(self):
        # keep ref to prevent garbage collection
        self._c_callback = self._get_c_callback()

        self._register_callbacks(0,                    # device handle
                                 1,                    # event type
                                 self._c_callback,     # callback function
                                 c_void_p())           # user context

What is the equivalent to this in my Python class that I can send instead of just c_void_p() in the last line of code? Could getting this parameter wrong cause the crash even after my Python callback function fires?

101
  • 8,514
  • 6
  • 43
  • 69
  • 1
    Generally a DLL will consistently using a calling convention. Are you sure the callback shouldn't use `WINFUNCTYPE`? – Eryk Sun Mar 30 '15 at 12:42
  • 1
    The context parameter is for lower-level languages such as C. It's pretty much useless for Python. You can pass a bound method or closure if you need context. – Eryk Sun Mar 30 '15 at 12:45
  • @eryksun thanks for pointing out `WINFUNCTYPE`, I'm in the habit of using the other. Are you implying that I don't _need_ to use context? That would make life easier for me, but not sure if the function requires it to be valid. Would `None` suffice then? If I did send a bound method how does one cast it to `c_void_p`? – 101 Mar 30 '15 at 21:21
  • 1
    `None` suffices; it gets converted to a `NULL` pointer. You can instantiate the `_C_CALLBACK` prototype using any callable Python object. This could be a function, which has a closure context in terms of its globals and nested scoping; a bound method that has the context of its `self` instance; or an object with a `__call__` method. – Eryk Sun Mar 30 '15 at 22:12
  • Thanks very much, good to know. With `None` and `WINFUNCTYPE` I'm still getting the crash though. I'm beginning to think this isn't to do with the user context parameter. – 101 Mar 31 '15 at 01:06
  • By crash, do you mean the error reporting dialog is displayed due to an unhandled Windows exception? Or is it a Python exception? – Eryk Sun Mar 31 '15 at 01:31
  • @eryksun error reporting dialog, so no Python traceback or anything like that. Not sure how to best debug that. – 101 Mar 31 '15 at 01:34

0 Answers0