2

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?

asked Sep 29, 2017 at 19:42
3
  • The example in the ctypes docs docs.python.org/3/library/ctypes.html#callback-functions doesn't use the call to byref as you are doing. Does it work if you remove the byref call and just do struct.function = prototype(func)? Commented Sep 29, 2017 at 20:06
  • @PaulCornelius I tried doing that but the function never seems to get called then. I never see an output from it. I was trying to follow the example you pointed out, but I believe I need byref or something similar since I'm trying to assign a pointer to a function rather than a function itself (which the example is doing). Commented Sep 29, 2017 at 20:09
  • In that case I have one more thought. If CFUNCTYPE returns an object that represents a pointer to a function, maybe you don't want the POINTER() function call in your structure definition. Commented Sep 29, 2017 at 21:29

1 Answer 1

1

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
answered Sep 30, 2017 at 16:02
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks so much. This has been very useful. One minor thing though, I had to do dll.func(byref(struct)) (last line of test.py) to make the call work.
@zephyr It worked without it for me but that is more technically correct. Did you have the final comma in argtypes? It must be a sequence and that makes it a tuple.
Yes, I did. In fact I copied and pasted your two programs and they worked just fine with the exception of the need for the byref. Without it, I get an OSError saying an access violation when writing to some random memory address.
@zephyr 32-bit or 64-bit Python? I'm just wondering why it worked for me and looking for the difference. I was using 64-bit and am guessing the marshaling of a struct is always by pointer in that ABI. It might also explain why I crashed when there was only one structure member. I'll have to go back and try that fix.
I'm using 32-bit.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.