Python 调用dll Part2

2022-06-18  本文已影响0人  welder77

第二部分主要介绍将 Python 函数做为回调函数传给dll的方法。

实现回调注册的原理,主要是通过ctypes.CFUNCTYPE或ctypes.WINFUNCTYPE作为桥梁,将python的函数注册到C中。

CFUNCTYPE和WINFUNCTYPE主要的区别是,WINFUNCTYPE 为Windows下独有的,通过使用使用stdcall调用约定的函数,CFUNCTYPE 使用标准C调用约定的函数。
一般如果先前用WinDLL导入dll则推荐使用WINFUNCTYPE,如果通过CDLL导入,则用CFUNCTYPE

CFUNCTYPE或WINFUNCTYPE的参数定义如下:

CFUNCTYPE/WINFUNCTYPE(restype, *argtypes, **kw)
#restype 代表函数的返回值
#*argtypes, **kw 代表传入的实参

下面,我们来举一个例子,这里以TS的dll为例,dll中包含一个注册回调函数的功能,可以通过注册的形式,将指定的python函数,注册为收发CAN报文的回调函数使用,这有点类似于CANoe中的on message回调函数。

以下是.h部分和Python代码部分。

#.h
typedef void (__stdcall* TCANEvent)(const ps32 AObj, const PCAN ACAN);
#其中第一个参数代表handle句柄,第二个参数则为CAN报文结构体,用于回调时,传递报文数据。
TSAPI(s32) tsapp_register_event_can(const ps32 AObj, const TCANEvent AEvent);

#Python
#定义TLIBCAN结构体,其中包含普通CAN报文所包含的元素
class TLIBCAN(Structure):
    _pack_ = 1
    _fields_ = [("FIdxChn", c_uint8),
                ("FProperties", c_uint8),
                ("FDLC", c_uint8),
                ("FReserved", c_uint8),
                ("FIdentifier", c_int32),
                ("FTimeUs", c_int64),
                ("FData", c_uint8 * 8),
                ]

#定义Python中的回调函数
def On_CAN_EVENT(OBJ, ACAN):
        print("回调接收成功")
        for i in ACAN.contents.FData:
            print('%#x' % i, end=' ')

#定义结构体参数PCAN
PCAN = POINTER(TLIBCAN)
#定义回调函数的签名,其中第一个参数值函数的返回值(无返回或不关注返回),后两个为实参。
OnTx_RxFUNC_CAN = WINFUNCTYPE(None, POINTER(c_int32), PCAN)
#实例化
OnCANevent = OnTx_RxFUNC_CAN(On_CAN_EVENT)

#注册回调
obj = c_int32(0) #定义句柄,可以代表不同通道
if 0 == dll.tsapp_register_event_canfd(byref(obj), OnCANevent):
    print("回调事件注册成功")
else:
    print("回调事件注册失败")

经过注册后,On_CAN_EVENT函数,就可以打印出TS发出和接收到的CAN报文了。

p.s.
需要注意 OnTx_RxFUNC_CAN(On_CAN_EVENT)这个指针函数是有自己的生存空间的,如果生存空间已过,会被释放,C代码再回调的时候,就会使用一个过期指针。所以这里建议将OnTx_RxFUNC_CAN放在一个全局的位置,比如程序开头或main中。

Todo:
需要研究,回调函数中是否可以通过ctypes.memmove修改指针所指向的内容,
比如,是否可以在TS 提供的CAN预发送回调函数中修改报文添加CRC和Count到CAN报文中,实现周期性发送E2E报文的功能。

上一篇下一篇

猜你喜欢

热点阅读