2011-06-20 51 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