I have a proprietary dll which interfaces with usb device, ctypes interface for it is working fine, but cffi one is crashing after calling a callback. function SwitchOn(6) should return pointer to a struct if it finds a device and NULL if it doesnt, also if it does not find the device error callback gets called with errno=10.
I am using python27, py33 behaves the same(need to remove 'import thread' to run)
Am I using it right? How do I debug it?
Trying to adapt doc's example for my needs as abarnert suggested. It still crashes. Am I doing it right?
>>> cffi.__version__
'0.7.2'
ctypes example outputs:
10288
(10288, 10L, 1L)
0
cffi example outputs:
4504
(4504, 10L, 1L)
and crashes
import thread
def error(errno, critical):
print(thread.get_ident(), errno, critical)
from cffi import FFI
ffi = FFI()
ffi.cdef('''
void* SwitchOn(int FPort);
typedef void(*type_func_user_error)(unsigned int, unsigned int);
void SetErrorFunction(type_func_user_error);
''')
eeg_dll = ffi.dlopen("EEG4DLL.dll")
err_cb = ffi.callback('type_func_user_error', error)
eeg_dll.SetErrorFunction(err_cb)
print(thread.get_ident())
x = eeg_dll.SwitchOn(6)
print(x)
import thread
def error(errno, critical):
print(thread.get_ident(), errno, critical)
import ctypes
from ctypes import c_uint, WINFUNCTYPE
eeg_dll = ctypes.windll.EEG4DLL
func_user_error = WINFUNCTYPE(None, c_uint, c_uint)
SetErrorFunction = eeg_dll.SetErrorFunction
SetErrorFunction.argtypes = [func_user_error]
SetErrorFunction.restype = None
err_cb = func_user_error(error)
eeg_dll.SetErrorFunction(err_cb)
print(thread.get_ident())
x = eeg_dll.SwitchOn(6)
print(x)
def error(errno, critical):
print(errno, critical)
from cffi import FFI
ffi2 = FFI()
ffi2.cdef('''
void (*python_callback)(unsigned int, unsigned int);
void *const c_callback;
''')
wr = ffi2.verify('''
static void(*python_callback)(unsigned int x, unsigned int y);
static void c_callback(unsigned int x, unsigned int y) {
python_callback(x, y);
}
''')
err_cb = ffi2.callback('void(unsigned int, unsigned int)', error)
wr.python_callback = err_cb
ffi = FFI()
ffi.cdef('''
void* SwitchOn(int FPort);
typedef void(*type_func_user_error)(unsigned int, unsigned int);
void SetErrorFunction(type_func_user_error);
''')
eeg_dll = ffi.dlopen("EEG4DLL.dll")
eeg_dll.SetErrorFunction(wr.c_callback)
x = eeg_dll.SwitchOn(6)
print(x)
According to the docs say:
Windows: you can’t yet specify the calling convention of callbacks… Use an indirection…
And your crash (happening immediately after return from your function) looks like exactly what you'd get by passing a cdecl
function and having it called as a stdcall
function: The caller (presumably the SwitchOn
function in the C library) expects the callee (CFFI's wrapper around your error
function) to clean up the stack; the callee expects the caller to clean up the stack… so nobody cleans up the stack, so when SwitchOn
tries to return, it's returning to one of your arguments or a local variable or some other garbage instead of to its caller.
Immediately above that, the docs show how to "use an indirection", by which they mean writing a C wrapper that you ffi.verify
. (They're showing how to do it for passing a varargs callback, but it's the same idea.)