提问人:肉蛋充肌 提问时间:7/5/2023 最后编辑:肉蛋充肌 更新时间:7/20/2023 访问量:54
在 Python 中定义 C++ 的回调函数
Defining callback functions for C++in Python
问:
我正在尝试编写对使用回调函数的 C++ 函数的绑定。回调函数如下所示。
typedef enum
{
CB_Event_TermRegister = 101,
...
}enSdkCbType;
typedef enum
{
TSDK_DEV_TYPE_UNDEF = 0,
...
}enSdkDevType;
typedef struct
{
enSdkDevType eTermType;
...
}TSdkEventTermRegister;
typedef int (CALLBACK* ON_CTS_SDK_CALL_BACK)(enSdkCbType eCbType, LPVOID pParam, DWORD dwSize, int usr_data);
其中,为定制型。 根据 而指向不同的点。
我在 python 中定义这个回调是这样的:enSdkCbType
enum
pParam
struct
eCbType
from ctypes import *
class enSdkCbType(c_int):
CB_Event_TermRegister = 101
...
class enSdkDevType(c_int):
TSDK_DEV_TYPE_UNDEF = 0
...
class TSdkEventTermRegister(Structure):
_fields_ = [
("eTermType", enSdkDevType),
...
]
def callback(eCbType, pParam, dwParamLen, usr_data):
if eCbType == enSdkCbType.CB_Asw_OpenTermAudVid:
assert dwParamLen == sizeof(TSdkEventTermRegister)
p = POINTER(TSdkEventTermRegister).from_address(pParam)
print(p.contents.eTermType)
return 0
dll = CDLL('my_dll')
CallbackType = CFUNCTYPE(c_int, enSdkCbType, c_void_p, c_ulong)
dll.func(CallbackType(callback))
但是不起作用,因为在这个回调中发送的参数是这样的:它们的类型是
显然,参数的格式与我定义它们时不同。此外,似乎又向回调发送了一个 parameter(),因为根据定义,参数应该是这样的。
我不知道我的 Python 代码哪里出错了,但我可以提供运行良好的 C++ 版本的回调函数。if eCbType == enSdkCbType.CB_Asw_OpenTermAudVid
<enSdkCbType object at 0x01F10F80>, 101, 159492072, 72
<class '__main__.enSdkCbType'> <class 'int'> <class 'int'> <class 'int'>
<enSdkCbType object at 0x01F10F80>
101(eCbType), 159492072(pParam), 72(dwSize), usr_data
int CALLBACK CSdkMp3Dlg::OnTzlSdkCallback(enSdkCbType eCbType, LPVOID pParam, DWORD dwSize, int usr_data)
{
switch(eCbType)
{
case CB_Event_TermRegister:
{
ASSERT(dwSize == sizeof(TSdkEventTermRegister));
TSdkEventTermRegister * pEventTermRegister = (TSdkEventTermRegister*)pParam;
...
}
...
}
return 0;
}
答:
1赞
Mark Tolonen
7/20/2023
#1
主要错误是回调定义不正确:
CallbackType = CFUNCTYPE(c_int, enSdkCbType, c_void_p, c_ulong)
应该是:
CALLBACK = ct.WINFUNCTYPE(c_int, enSdkCbType, c_void_p, c_ulong, c_int)
决赛不见了。也是在 Windows 中匹配的类型,定义为 。它实际上只对 32 位版本很重要,但应该纠正可移植性。c_int
WINFUNCTYPE
CALLBACK
__stdcall
过去之后,回调中出现了一些错误。以下是我测试的 C 代码和更正后的 Python 代码:
测试.c
#include <windows.h>
#include <stdio.h>
#define API __declspec(dllexport)
typedef enum {
CB_Event_TermRegister = 101,
} enSdkCbType;
typedef enum {
TSDK_DEV_TYPE_UNDEF = 0,
} enSdkDevType;
typedef struct {
enSdkDevType eTermType;
} TSdkEventTermRegister;
typedef int (CALLBACK* ON_CTS_SDK_CALL_BACK)(enSdkCbType eCbType, LPVOID pParam, DWORD dwSize, int usr_data);
API void func(ON_CTS_SDK_CALL_BACK cb) {
TSdkEventTermRegister t = {5};
cb(CB_Event_TermRegister, &t, sizeof t, 0);
}
test.py
import ctypes as ct
import ctypes.wintypes as w # Defines Windows types like LPVOID and DWORD
# for closer matching of types as documented.
class enSdkCbType(ct.c_int):
CB_Event_TermRegister = 101
class enSdkDevType(ct.c_int):
TSDK_DEV_TYPE_UNDEF = 0
class TSdkEventTermRegister(ct.Structure):
_fields_ = ("eTermType", enSdkDevType),
dll = ct.CDLL('./test')
CALLBACK = ct.WINFUNCTYPE(ct.c_int, enSdkCbType, w.LPVOID, w.DWORD, ct.c_int)
# Good practice to explicitly declare function arguments and return type.
dll.func.argtypes = CALLBACK,
dll.func.restype = None
# This decorator means "callback = CALLBACK(callback)" so
# the function can be directly passed without wrapping it
# each time. Also safer if the callback is stored by the
# C code for later use, as wrapping it when called means
# the wrapper is destroyed when the call returns.
# It's not needed in this case, but another good practice.
@CALLBACK
def callback(eCbType, pParam, dwParamLen, usr_data):
# Get the value as a Python integer otherwise it doesn't
# match the type being compared to.
if eCbType.value == enSdkCbType.CB_Event_TermRegister:
assert dwParamLen == ct.sizeof(TSdkEventTermRegister)
# The address is of the structure, not a pointer to structure.
p = TSdkEventTermRegister.from_address(pParam)
print(p.eTermType.value)
return 0
dll.func(callback)
输出:
5
评论
0赞
肉蛋充肌
7/20/2023
它有效!我可以在类中定义这个回调函数吗?这样我就可以在调用函数时将信息传递给我的类。我正在尝试做这样的事情,但该过程以代码 -1073741819 (0xC0000005) 退出。还是我应该将回调函数定义为一个类并覆盖它的函数?def callback(self, eCbType, pParam, dwParamLen, usr_data):
__call__
0赞
肉蛋充肌
7/20/2023
我有一个使用此 dll 的 java 演示。他们将此回调函数定义为一个类,并在其中实现目标业务逻辑,并且该类扩展callback.invoke
callback
win32.StdCallLibrary.StdCallCallback
评论