【问题标题】:Python Wrapper to a C CallbackC 回调的 Python 包装器
【发布时间】:2011-06-20 14:31:01
【问题描述】:

尝试创建需要在调用 C 回调时调用的 python 回调 来自 Windows 环境中的 dll。请查看下面的代码以了解问题。

from ctypes import *

#---------qsort Callback-------------#
IntArray5 = c_int * 5
ia = IntArray5(5,1,7,33,99)
libc = cdll.msvcrt
qsort = libc.qsort
qsort.restype = None

CMPFUNC = CFUNCTYPE(c_int,POINTER(c_int),POINTER(c_int) )
test = 0
def py_cmp_func(a,b):
    #print 'py_cmp_func:',a[0],b[0]
    global test
    test = 10000
    return a[0]-b[0]

cmp_func = CMPFUNC(py_cmp_func)
qsort(ia, len(ia), sizeof(c_int), cmp_func)
print "global test=",test
for item in ia : print item

#----------Load DLL & Connect ------------#
gobiDLL = WinDLL("C:\LMS\QCWWAN2k.dll")
print  'Output of connect : ',gobiDLL.QCWWANConnect()

#----------SetByteTotalsCallback----------#
tx = POINTER(c_ulonglong)
rx = POINTER(c_ulonglong)
proto_callback = WINFUNCTYPE(c_void_p,tx,rx)

gtx = grx = 0 # Used to copy the response in the py_callback
def py_callback(t,r):
    sleep(10)
    print 'python callback ...'
    print "tx=",t,"rx=",r
    global gtx,grx
    gtx = 5000 # gtx = t
    grx = 2000 # grx = r
    #return 0

callback = proto_callback(py_callback)
gobiDLL.SetByteTotalsCallback.restype = c_ulong
gobiDLL.SetByteTotalsCallback.argtypes = [proto_callback,c_byte]                    

print "SetByteTotalsCallback = ",gobiDLL.SetByteTotalsCallback(callback, c_byte(256))
print "gtx = ",gtx
print "grx = ",grx

DLL 记录了原型和 SetByteTotalsCallback() 方法的回调,如下所示。

原型:

ULONG QCWWANAPI2K SetSessionStateCallback( tFNSessionState pCallback );

回调:

void ByteTotalsCallback( ULONGLONG  txTotalBytes, ULONGLONG rxTotalBytes );

输出:

>>> 
global test= 10000
1
5
7
33
99
Output of connect :  0
SetByteTotalsCallback =  0
gtx =  0
grx =  0
>>>>

当前的问题是整个程序被正确调用, 但是python回调根本没有被调用。程序以 0 状态退出 gobiDLL.SetByteTotalsCallback(callback, c_byte(256)) 方法,但是写的是callback()方法 在python中调用过程中不调用。

您能否指出什么可以帮助输入 python 回调? 另一个示例 qsort() 方法将指针完美地传递给 python 函数指针。 在这里找不到问题的根本原因。

TIA,

安东尼

【问题讨论】:

  • 如果您可以将问题减少到最小的可重现场景,那么为您提供帮助会容易得多。谢谢!
  • 我建议使示例更短(我怀疑您可以再次将代码减半),但另外,鉴于这不是常见的情况,也许考虑提供一个视觉工作室解决方案,有人可以自己编译看看。

标签: python c windows ctypes


【解决方案1】:

你不能。 C/C++ 函数不能直接访问 Python 函数 - 该函数原型可能需要一个指向 C 的指针。Python 将向它传递一个指向该特定函数的内部数据结构的指针。

现在是构建 Python 的 C 扩展来包装该 DLL 并将其公开给 Python 的时候了。你要做的基本上是让 C 回调调用 Python 回调,因为 可以 完成。更清楚地说,您要实现的是:

                | This side is C land i.e. "real" addresses
                |
Python objects --> C extension -- register callback with --> DLL
                |                                             |
 in the python  |                                     Calls callback
                |                                             |
  interpreter <-------------- Callback in C extension  <-------
                |

以下是从 C 构建调用 python 函数的快速说明。您需要使用用于构建 Python 发行版的 MSVC(或替代工具)构建此代码;使用depends.exe 找出它与哪个msvcXX.dll 链接。

全局状态通常被认为是不好的,但为了简单起见,我使用了它:

static PyObject* pyfunc_event_handler = NULL;
static PyObject* pyfunc_event_args = NULL;

然后我添加了一个设置处理函数,以使设置回调的过程更容易。但是,你不需要这样做,你只需要

static PyObject* set_event_handler(PyObject *self, PyObject *args)
{
    PyObject *result = NULL;
    PyObject *temp;

下一行是重要的 - 我允许将两个 python 对象(O 参数传递给PyArg_ParseTuple。一个对象包含函数,另一个包含其参数。

    if (PyArg_ParseTuple(args, "OO", &temp, &pyfunc_event_args)) {

        if (!PyCallable_Check(temp)) {
            PyErr_SetString(PyExc_TypeError, "parameter must be a function");
            return NULL;
        }

整理参考资料。 Python 需要你这样做。

        Py_XINCREF(temp);                    /* Add a reference to new func */
        Py_XDECREF(pyfunc_event_handler);    /* Dispose of previous callback */
        pyfunc_event_handler = temp;         /* Remember new callback */

        /* Boilerplate to return "None" */
        Py_INCREF(Py_None);
        result = Py_None;
    }
    return result;
}

然后你可以在别处调用它:

 PyObject* arglist = Py_BuildValue("(O)", pyfunc_event_args);
 pyobjresult = PyObject_CallObject(pyfunc_event_handler, arglist);
 Py_DECREF(arglist);

不要忘记DECREF,您需要 Python 来 gc arglist。

从python中,使用这个很简单:

set_event_handler(func, some_tuple)

其中 func 具有匹配的参数,如下所示:

def func(obj):
    /* handle obj */

您可能想阅读的内容:

【讨论】:

  • 感谢您的回答 9Fingers !我们将评估为回调访问创建 C 扩展。希望这能解决我们的问题。我希望改变函数指针调用的 ctypes 机制,类似于调用 qsort(来自 cdll.msvcrt dll)中的 python 回调的方式。
  • 一些有助于澄清理解的问题 - 1. 使用 ctypes 编写和映射的 Python Callback/Wrapper 是否未注册。我认为它可以工作,因为下面有一个支持 callabcks 的扩展。 2. qsort的例子有一个函数有2个指针都被正确调用,为什么外部dll中的方法没有注册回调。是否由于不存在用于处理回调的 c 扩展而失败?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-17
  • 2011-11-05
相关资源
最近更新 更多