Reputation: 2332
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?
Upvotes: 2
Views: 1143
Reputation: 178369
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:
WINFUNCTYPE
with __stdcall
. CFUNCTYPE
is for __cdecl
.POINTER
or byref
to make it work.@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
Upvotes: 1