【问题标题】:python cffi crashes after callback回调后python cffi崩溃
【发布时间】:2013-11-05 05:33:09
【问题描述】:

我有一个与 USB 设备接口的专有 dll,它的 ctypes 接口工作正常,但 cffi 调用回调后崩溃。 如果找到设备,函数 SwitchOn(6) 应该返回指向结构的指针,如果没有,则返回 NULL,如果它没有找到设备错误回调,则使用 errno=10 调用。

我使用的是 python27,py33 的行为相同(需要删除“导入线程”才能运行)

我用对了吗?如何调试?

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

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

【问题讨论】:

  • 从函数返回时崩溃看起来就像你调用 cdecl 函数,就好像它是 stdcall 函数一样,反之亦然。如果您可以运行调试器(如 MSVC、windbg 等;而不是 Python 调试器),您应该能够验证它是调用后的堆栈清理,可以轻松处理所有内容。

标签: python ctypes cpython python-cffi


【解决方案1】:

根据文档say

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

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

紧接着,文档展示了如何“使用间接”,他们的意思是编写一个您 ffi.verify 的 C 包装器。 (他们正在展示如何传递可变参数回调,但这是相同的想法。)

【讨论】:

  • 我没有 dll 的源代码。在这种情况下如何编写包装器?
  • @mindless:您不需要 DLL 的源代码。您是否单击了链接并查看了示例包装器?它不需要访问要使用的任何 C 库的源代码。为什么你认为你的会?
猜你喜欢
  • 2013-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-16
  • 1970-01-01
  • 2020-10-28
  • 2021-05-18
  • 1970-01-01
相关资源
最近更新 更多