开发者

Python Wrapper to a C Callback

Trying to create a python callback which needs to be invoked while calling the C callback from a dll in Windows environment. Please review the code below to understand the issue.

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

The DLL Documents the Prototype and the callback for the SetByteTotalsCallback() method as shown below.

Prototype :

ULONG QCWWANAPI2K SetSessionStateCallback( tFNSessionState pCallback );

Callback :

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

The current problem is that the whole program gets called properly, but the python callback does not get called at all. The program exits with 0 status from gobiDLL.SetByt开发者_如何转开发eTotalsCallback(callback, c_byte(256)) method, but the callback() method written in python does not called during the call.

Could you please point out what could help enter the python callback ? The other sample qsort() method passes the pointer to the python function pointer wonderfully. At a loss to get the root cause of the issue here.

TIA,

Anthony


You can't. C/C++ functions can't access Python functions directly - that function prototype is probably expecting a pointer to C. Python will be passing it a pointer to its internal data structure for that particular function.

This is the time to build a C extension to python to wrap that DLL and expose it to Python. What you'd do is essentially have the C callback call the Python callback, since that can be done. To be clearer, what you want to achieve is:

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

The following is a very quick explanation for building a calling a python function from C. You'll need to build this code with the MSVC (or alternative tool) that was used to build your Python distribution; use depends.exe to find out which msvcXX.dll it is linked against.

Global state is generally considered bad, but for simplicity that's what I used:

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

I then added a set handler function to make the process of setting the callback easier. However, you don't need to do that, you just need to

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

The next line is the important one - I allow passing of two python objects (the O arguments to PyArg_ParseTuple. One object contains the function, the other its parameters.

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

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

Sort out references. Python needs you to do this.

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

You can then call this elsewhere with:

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

Don't forget the DECREF, you need Python to gc the arglist.

From python, using this is as simple as:

set_event_handler(func, some_tuple)

Where func has matching parameters like so:

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

Things you probably want to read up on:

  • LoadLibrary (load DLL from C).
  • GetProcAddress (find a function to call).
  • Extending Python with C or C++ from the Python docs.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜