zephyr zephyr - 1 year ago 134
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);
...
}SOME_STRUCT;


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
struct.function
was expecting a
LP_CFunctionType
instance but got a
CArgObject
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).

test.c

#include <stdio.h>

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

typedef struct some_struct {
    CALLBACK function;
    int other;
} SOME_STRUCT;

__declspec(dllexport) int func(SOME_STRUCT* pss)
{
    printf("%d\n",pss->other);
    return pss->function(1,2);
}

test.py

from ctypes import *

CALLBACK = WINFUNCTYPE(c_int,c_int,c_int)

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

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

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

struct = SOME_STRUCT(callback,7)
print(dll.func(byref(struct)))

Output

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