2011-06-20 59 views
3

试图创建一个python回调,它需要在Windows环境中从dll调用C回调 时被调用。请查看下面的代码以了解问题。Python包装到C回调

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); 

OUTPUT:

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

目前的问题是,整个程序才能正确调用, 但蟒蛇回调不根本不需要打电话。该程序退出 gobiDLL.SetByteTotalsCallback(callback,c_byte(256))方法中的0状态,但在调用过程中不调用python中编写的 回调()方法。

请你指出什么可以帮助进入python回调? 另一个示例qsort()方法将指针非常精确地传递给python函数指针。 不知所措,在这里找到问题的根本原因。

TIA,

安东尼

+0

如果您可以将问题降至最低限度的可重复使用情况,那么帮助您会容易得多。谢谢! – rafalotufo

+0

我建议让这个例子更短(我怀疑你可以重复使用一半的代码),但是另外,考虑到这不是一种常见的情况,可能考虑提供一个视觉工作室解决方案,让某人可以自己编译和查看。 – Arafangion

回答

6

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

这是时候建立一个C扩展到python来包装该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函数你需要建立这个代码与MSVC(或替代工具非常快速的解释),用于构建您的Python发行版;使用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中。

从蟒蛇,用这个很简单,只要:

set_event_handler(func, some_tuple) 

凡FUNC已经像这样的匹配参数:

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

事情你可能想在读了起来:

+0

感谢您的回答9Fingers!我们将评估为Callback访问创建C扩展。希望这能解决我们的问题。 我希望改变函数指针调用的ctypes机制,类似于调用qsort(来自cdll.msvcrt dll)的python回调方式。 – Anthony

+0

有几个问题可以帮助理解理解 - 1.使用ctypes书写和映射的Python回调/包装器是否得到注册?我认为它可以工作,因为下面有一个支持callabcks的扩展。 2. qsort的例子有一个正确调用2个指针的函数,为什么来自外部dll的方法没有注册回调。由于处理回调不存在c扩展而导致失败? – Anthony