zephyr zephyr - 3 years ago 216
C Question

Have C library call python function with ctypes

I have a DLL in c and meant for windows platforms which has a structure similar to the following:

C Structure

typedef struct some_struct {
int (_stdcall *function)(int arg1, int arg2);

I've defined a python ctypes structure to mimic this with the following

Python Structure

class SOME_STRUCT(Structure):
_fields_ = [('function', POINTER(CFUNCTYPE(c_int, c_int, c_int))), ...]

The point of this structure in the C code is to register a callback function that gets executed upon certain triggers in its own thread. What I want to be able to do, if possible, is set that callback to be a Python function such that when the function in the C structure gets called from the C code, it is the python function that gets executed.

What I've got in python to try and accomplish this (which doesn't work) is the following:

def func(arg1,arg2):
print('I was called!')
return 0

struct = SOME_STRUCT()
prototype = CFUNCTYPE(c_int, c_int, c_int)
struct.function = byref(prototype(func))

The specific error I get (that may not be my only issue) is that it complains that
was expecting a
instance but got a
instance. How can I do what I'm trying to do?

Answer Source

Here's a working example and test DLL source. Oddly, I couldn't get it to work when the callback was the only member of the struct (crash). It seemed like a bug, because a callback without a struct wrapper or adding a second member to the struct made it work.

Things to note:

  • Use WINFUNCTYPE with __stdcall. CFUNCTYPE is for __cdecl.
  • You don't need POINTER or byref to make it work.
  • The @CALLBACK decorator is equivalent to func = CALLBACK(func).


#include <stdio.h>

typedef int (__stdcall *CALLBACK)(int arg1, int arg2);

typedef struct some_struct {
    CALLBACK function;
    int other;

__declspec(dllexport) int func(SOME_STRUCT* pss)
    return pss->function(1,2);


from ctypes import *

CALLBACK = WINFUNCTYPE(c_int,c_int,c_int)

class SOME_STRUCT(Structure):
    _fields_ = [('function', CALLBACK),
                ('other', c_int)]

def callback(arg1,arg2):
    return arg1 + arg2

dll = CDLL('test')
dll.argtypes = POINTER(SOME_STRUCT),
dll.restype = c_int

struct = SOME_STRUCT(callback,7)


Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download