python cffi在回调后崩溃

时间:2013-10-25 23:38:35

标签: python ctypes cpython python-cffi

我有一个专有的dll与usb设备接口,ctypes接口工作正常,但cffi one在调用回调后崩溃了。 函数SwitchOn(6)如果找到设备则返回指向struct的指针,如果没有找到则返回NULL,如果找不到设备错误,则调用errno = 10调用回调。

我使用的是python27,py33的行为相同(需要删除'import thread'才能运行)

我使用它吗?我该如何调试呢?

尝试根据我的需要调整doc的示例,建议 abarnert 。它仍然崩溃。我做得对吗?

>>> cffi.__version__
'0.7.2'

ctypes示例输出:

10288
(10288, 10L, 1L)
0

cffi示例输出:

4504
(4504, 10L, 1L)

并崩溃

cffi_crash.py

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)

ctypes_no_crash.py

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)

cffi_indirection.py

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)

1 个答案:

答案 0 :(得分:1)

根据文档say

  

Windows:你还不能指定回调的调用约定......使用间接...

你的崩溃(从你的函数返回后立即发生)看起来就像你传递cdecl函数并将其作为stdcall函数调用一样:调用者(大概是C库中的SwitchOn函数)期望被调用者(CFFI的error函数的包装器)清理堆栈;被调用者希望调用者清理堆栈...所以没有人清理堆栈,所以当SwitchOn尝试返回时,它会返回你的一个参数或一个局部变量或其他垃圾,而不是它的调用者。

紧接着,文档显示了如何“使用间接”,它们意味着编写一个ffi.verify的C包装器。 (他们正在展示如何通过varargs回调来实现它,但这是同样的想法。)