0
votes

I have the following C source that I compile into a DLL:

int (*pfuncExtB)(int a, int b);

int funcB(int a, int b)
{
    return funcExtB(a, b);
}

int funcExtB(int a, int b)
{
    return pfuncExtB(a, b);
}

What I want to do is to make pfuncExtB "point" to a Python function, so this is what I do in Python:

from ctypes import *

def add(a, b):
    return a + b

mutdll = cdll.LoadLibrary("my.dll")

pfuncExtB = (POINTER(CFUNCTYPE(c_int, c_int, c_int))).in_dll(mutdll, 'pfuncExtB')

funcB = mutdll.funcB
funcB.argtypes = [c_int, c_int]
funcB.restype = c_int

pfuncExtB.contents = CFUNCTYPE(c_int, c_int, c_int)(add)

print funcB(3 , 4)

After this I expect that the following call to return 7

print funcB(3, 4)

But I get:

Traceback (most recent call last):
..................
print funcB(3, 4)
WindowsError: exception: access violation reading 0x00000001

So what am I doing wrong here? Is it possible to have a Python function "assigned" to a ctypes pointer-to-function variable?

Edit: After seeing Mark Tolonen's workaround (a set function for the pointer to function variable written in C), I found why it didn't work for me when I tried it.

This does not work:

set(CFUNCTYPE(c_int,c_int,c_int)(add))
print funcB(2, 3)

While this works:

callback = CFUNCTYPE(c_int,c_int,c_int)(add)
set(callback)
print funcB(2, 3)

Where set is a C function that assigns the pointer-to-function argument to the global like in Mark's answer. As he pointed out the answer is lays in the docs:

Important note for callback functions: Make sure you keep references to CFUNCTYPE() objects as long as they are used from C code. ctypes doesn’t, and if you don’t, they may be garbage collected, crashing your program when a callback is made.

1

1 Answers

1
votes

The correct type of the global variable in Python is CFUNCTYPE(c_int,c_int,c_int) (no POINTER()), but I don't see a method to change the value of the variable once you have it. If you can add a set function it can work:

C

typedef int (*FUNC)(int,int);

__declspec(dllexport) FUNC pfuncExtB;

__declspec(dllexport) void set(FUNC f)
{
    pfuncExtB = f;
}

int funcExtB(int a, int b)
{
    return pfuncExtB(a, b);
}

__declspec(dllexport) int funcB(int a, int b)
{
    return funcExtB(a, b);
}

Python

from ctypes import *

FUNC = CFUNCTYPE(c_int,c_int,c_int)

@FUNC
def add(a, b):
    return a + b

mutdll = cdll.LoadLibrary('x')

mutdll.set.argtypes = [FUNC]
mutdll.set.restype = None

mutdll.set(add) # set the global variable

pfuncExtB = FUNC.in_dll(mutdll,'pfuncExtB')
print(pfuncExtB(1,2)) # -> 3

funcB = mutdll.funcB
funcB.argtypes = [c_int, c_int]
funcB.restype = c_int

print(funcB(3 , 4)) # -> 7

Note that this does not work:

pfuncExtB = POINTER(FUNC).in_dll(mutdll,'pfuncExtB')
pfuncExtB.contents(1,2) # exception!