【问题标题】:How to call a __cdel decorated function with cffi?如何使用 cffi 调用 __cdel 修饰函数?
【发布时间】:2013-09-17 14:33:08
【问题描述】:

@eryksun 和 Python 专家您好:

我正在尝试使用 cffi 使用以下命令将 dll 文件导入 python:

from cffi import FFI
ffi=FFI()
lib=ffi.dlopen('ATC3DG.DLL')

从之前关于ctypes 的问题中,我知道DLL 都是cdecl(感谢@eryksun 的提示),并且我能够通过以下方式访问它的功能:

from ctypes import *
lib=windll.LoadLibrary('ATC3DG.DLL')
lib._InitializeBIRDSystem

但是,我不确定 cffi 中的等效操作是什么。 lib._InitializeBIRDSystem 在 ctypes 下工作,但不在 cffi 下。有什么建议吗?

感谢您对此进行调查,

埃里克

【问题讨论】:

    标签: python dll python-cffi


    【解决方案1】:

    atc3dg.dll 导出的符号使用前导下划线,这对于 cdecl 来说是不常见的。您必须在 ffi.cdef 中使用的定义中添加下划线,就像使用 ctypes 一样。

    import cffi
    
    ffi = cffi.FFI()
    ffi.cdef('''
    enum MESSAGE_TYPE
    {
        SIMPLE_MESSAGE,     // short string describing error code
        VERBOSE_MESSAGE,    // long string describing error code
    };
    
    int _InitializeBIRDSystem(void);
    
    int _GetErrorText(
        int                 errorCode,
        char                *pBuffer,
        int                 bufferSize,
        enum MESSAGE_TYPE   type
        );
    ''')
    
    lib = ffi.dlopen('atc3dg.dll')
    buf = ffi.new('char[100]')
    
    err = lib._InitializeBIRDSystem()
    lib._GetErrorText(err, buf, len(buf), lib.SIMPLE_MESSAGE)
    print(ffi.string(buf).decode('ascii'))
    
    # output:
    # System         : No BIRDs were found anywhere
    

    如果您配置了 C 编译器,则可以使用 ffi.verify()。我对 cffi 的经验不是很丰富(还没有,但看起来很有希望),所以请谨慎对待。

    我不得不对标题做一些小改动。在COMMUNICATIONS_MEDIA_PARAMETERS的定义中,第500行需要添加enum,如下:

    enum COMMUNICATIONS_MEDIA_TYPE mediaType;
    

    此外,作为仅供参考,编译器打印了一些警告,例如“从 'unsigned __int64' 转换为 'unsigned short',可能丢失数据”。我没有在扩展模块的生成源中对此进行跟进。

    import os
    import cffi
    import subprocess
    
    pth = os.path.abspath(os.path.dirname(__file__))
    os.environ['PATH'] += ';%s' % pth
    
    # preprocess: modify this for your compiler
    # Using Microsoft's cl.exe that comes with VC++.
    # cdef chokes on __declspec, so define DEF_FILE.
    cmd = 'cl.exe /DDEF_FILE /EP %s' % os.path.join(pth, 'atc3dg.h')
    hdr = subprocess.check_output(cmd, universal_newlines=True)
    
    ffi = cffi.FFI()
    ffi.cdef(hdr)
    
    # using __declspec(dllimport) links more efficiently,
    # but skipping it still works fine
    lib = ffi.verify(hdr, library_dirs=[pth], libraries=['atc3dg'])
    buf = ffi.new('char[100]')
    
    err = lib.InitializeBIRDSystem()
    lib.GetErrorText(err, buf, len(buf), lib.SIMPLE_MESSAGE)
    print(ffi.string(buf).decode('ascii'))
    # output:
    # System         : No BIRDs were found anywhere
    

    从好的方面来说,这种方法避免了前导下划线。这是编译扩展模块时由链接器处理的 ABI 细节。

    【讨论】:

    • 再次感谢这些非常有用的答案。在我的电脑(64 位 win7,运行 32 位 python 环境)上,第二个脚本出现错误消息:lib = ffi.verify(hdr, library_dirs=[pth], libraries=['atc3dg']) 我设法通过将行更改为:lib = ffi.verify(hdr, libraries=['atc3dg']) Don't知道为什么。我是否需要设置任何其他路径参数才能使用 library_dirs 选项?
    • 我将第二个脚本与头文件、导入库和 dll 放在同一目录中。但是,它可以从任何工作目录运行,所以我将脚本的目录添加到 PATH 以加载 atc3dg.dll,还添加到 library_dirs 以便编译器找到 atc3dg.lib。您必须考虑编写正确的 setup.py。
    猜你喜欢
    • 2018-12-12
    • 1970-01-01
    • 1970-01-01
    • 2018-01-11
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    • 2016-10-22
    • 1970-01-01
    相关资源
    最近更新 更多