我有以下编译成DLL的C源代码:
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);
}
我想要做的是让pfuncExtB“指向”Python函数,所以这就是我在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)
在此之后,我希望以下调用返回7
print funcB(3, 4)
但我明白了:
Traceback (most recent call last):
..................
print funcB(3, 4)
WindowsError: exception: access violation reading 0x00000001
那我在这里做错了什么?是否可以将Python函数“赋值”给ctypes指针到函数变量?
编辑:在看到Mark Tolonen的解决方法(用C编写的函数变量指针的set函数)之后,我发现为什么当我尝试它时它对我不起作用。
这不起作用:
set(CFUNCTYPE(c_int,c_int,c_int)(add))
print funcB(2, 3)
虽然这有效:
callback = CFUNCTYPE(c_int,c_int,c_int)(add)
set(callback)
print funcB(2, 3)
其中set是一个C函数,它将指针到函数的参数分配给全局,如Mark的答案。正如他指出的那样,答案就在文档中:
回调函数的重要说明: 确保只要从C代码中使用它们,就保持对CFUNCTYPE()对象的引用。 ctypes不会,如果你不这样做,它们可能会被垃圾收集,在回调时会崩溃你的程序。
答案 0 :(得分:1)
Python中全局变量的正确类型是CFUNCTYPE(c_int,c_int,c_int)
(没有POINTER()
),但是我没有看到一个方法来改变变量的值。如果你可以添加一个set函数,它可以工作:
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);
}
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
请注意,这不起作用:
pfuncExtB = POINTER(FUNC).in_dll(mutdll,'pfuncExtB')
pfuncExtB.contents(1,2) # exception!