我必须在Python中定义一个回调函数,一个将从DLL调用的函数。
BOOL setCallback (LONG nPort, void ( _stdcall *pFileRefDone) (DWORD nPort, DWORD nUser), DWORD nUser);
我尝试了这段代码,这似乎适用于Python 2.5,但是使用Python 2.7它会崩溃,我认为我做错了。
import ctypes, ctypes.wintypes
from ctypes.wintypes import DWORD
def cbFunc(port, user_data):
print "Hurrah!"
CB_Func = ctypes.WINFUNCTYPE(None, DWORD, DWORD)
mydll.setCallback(ctypes.c_long(0), CB_Func(cbFunc), DWORD(0))
错误在哪里?
注意:该平台仅在32位(Windows和Python)上运行。 DLL成功加载,内部的其他函数在从Python调用时也能正常工作。
https://github.com/ssbarnea/pyHikvision提供了一个可以重现此内容的完整示例代码 - 只需在py25和py27下运行Video.py。
答案 0 :(得分:3)
您的CB_Func(cbFunc)参数在setCallback函数之后立即被垃圾收集。只要可以调用回调(15.17.1.17. Callback functions,最后一段),该对象就必须持久化。将其分配给变量并保持不变。这是我的工作示例:
typedef unsigned int DWORD;
typedef long LONG;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
typedef void (__stdcall *CALLBACK)(DWORD,DWORD);
CALLBACK g_callback = 0;
DWORD g_port = 0;
DWORD g_user = 0;
BOOL __declspec(dllexport) setCallback (LONG nPort, CALLBACK callback, DWORD nUser)
{
g_callback = callback;
g_port = nPort;
g_user = nUser;
return TRUE;
}
void __declspec(dllexport) Fire()
{
if(g_callback)
g_callback(g_port,g_user);
}
from ctypes import *
def cb_func(port,user):
print port,user
x = CDLL('x')
CALLBACK = WINFUNCTYPE(None,c_uint,c_uint)
#cbfunc = CALLBACK(cb_func)
x.setCallback(1,CALLBACK(cb_func),2)
x.Fire()
from ctypes import *
def cb_func(port,user):
print port,user
x = CDLL('x')
CALLBACK = WINFUNCTYPE(None,c_uint,c_uint)
cbfunc = CALLBACK(cb_func)
x.setCallback(1,cbfunc,2)
x.Fire()
编辑:此外,由于CALLBACK
是一个返回函数的函数,它可以用作Python回调的装饰器,消除了回调超出范围的问题: / p>
from ctypes import *
CALLBACK = WINFUNCTYPE(None,c_uint,c_uint)
@CALLBACK
def cb_func(port,user):
print port,user
x = CDLL('x')
x.setCallback(1,cb_func,2)
x.Fire()
答案 1 :(得分:3)
在使用嵌套函数的Video class上下文中:
class Video(object):
def __init__(self, ...):
self.__cbFileRefDone = []
def open(self, filename):
@WINFUNCTYPE(None, DWORD, DWORD)
def cbFileRefDone(port, user_data):
print "File indexed.", filename
self.__cbFileRefDone.append(cbFileRefDone) # save reference
if not self.hsdk.PlayM4_SetFileRefCallBack(
c_long(self.port), cbFileRefDone, DWORD(0)):
logging.error("Unable to set callback for indexing")
return False
这有点难看,但更自然的变体在回调期间因TypeError而失败:
#XXX this won't work
@WINFUNCTYPE(None, DWORD, DWORD)
def cbFileRefDone(self, port, user_data):
print "File indexed."
要修复可以创建特殊功能描述符的问题:
def method(prototype):
class MethodDescriptor(object):
def __init__(self, func):
self.func = func
self.bound_funcs = {} # hold on to references to prevent gc
def __get__(self, obj, type=None):
assert obj is not None # always require an instance
try: return self.bound_funcs[obj,type]
except KeyError:
ret = self.bound_funcs[obj,type] = prototype(
self.func.__get__(obj, type))
return ret
return MethodDescriptor
用法:
class Video(object):
@method(WINFUNCTYPE(None, DWORD, DWORD))
def cbFileRefDone(self, port, user_data):
print "File indexed."
def open(self, filename):
# ...
self.hsdk.PlayM4_SetFileRefCallBack(
c_long(self.port), self.cbFileRefDone, DWORD(0))
此外,MethodDescriptor保存对C函数的引用,以防止它们被垃圾收集。