使用 WINAPI 的 Cython DLL 扩展编译在链接期间失败并出现错误 LNK2001

Cython DLL extension compilation using WINAPI fails during linking with error LNK2001

我正在尝试为 (Photron) 相机编写 Cython 接口 driver DLL。该库提供了一个 SDK,其中包含 header 文件和 Windows 32 位和 64 位库。我已经在 VisualStudio 中使用这个库成功编译了一个 C++ 示例。另一方面,Cython 编译在链接时失败。

自定义 Cython 代码包含一个包含类型的 typedefs.pxd 文件,一个包含类型的 photron.pxd 文件来自 SDK header 的常量和函数声明以及 camera.pyx 文件中的包装器 cdef class .

在 setup.py 文件的扩展(从 setuptools 导入)部分中,我包含了以下部分:

include_dirs=[s.path.abspath(os.path.normpath('./include'))],
libraries=[os.path.join(os.path.abspath(os.curdir), 'Lib', '64bit(x64)', 'PDCLIB')],
library_dirs=[os.path.abspath(os.path.normpath('./Lib/64bit(x64)'))],

我也尝试了成功的C++编译的命令行标志。 为了让初始的 cythonization 步骤起作用(生成 cpp 文件),我还必须将其添加到 SDK 的 header 中:

    #include <windows.h>

以下是 pxd 文件的相关摘录:

from typedefs cimport ulong, uint

cdef extern from "PDCFUNC.h":
    ulong PDC_CloseDevice(ulong nDeviceNo, ulong *pErrorCode)

当我尝试编译代码时,我得到了 cpp 文件,但是当尝试链接时,对于我在 pxd 文件中声明的每个函数,我得到如下一系列行:

camera.obj : error LNK2001: unresolved external symbol "unsigned long __cdecl PDC_CloseDevice(unsigned long,unsigned long *)" (?PDC_CloseDevice@@YAKKPEAK@Z)

我想这是因为链接器试图在指定的 header 中找到一个 __cdecl 函数,尽管这些函数在 header 文件中是这样声明的:

unsigned long WINAPI PDC_CloseDevice(unsigned long nDeviceNo, unsigned long *pErrorCode);

由于 Cython 文档明确指出支持调用约定,因此我尝试:

  1. 将 WINAPI 调用约定添加到 pxd 文件,但我在文件的 cython 化过程中 C 变量声明中的语法错误
  2. 直接添加 __stdcall 调用约定,因为程序无论如何都应该以 64 位编译。但我仍然得到寻找 __cdecl 的 LNK2001 行,就好像 Cython 绕过了调用约定一样。实际上,这是生成的 cpp 文件中的行:
    __pyx_v_ret = PDC_CloseDevice(__pyx_v_self->device_nb, (&__pyx_v_error_code));

__cdecl 警告也可能是转移注意力,但我没有其他线索,而且我通常不会在 Windows 下编译,所以我有一个对工具的了解有限。

在尝试了所有其他方法(包括正确编译的 visual studio 项目中的所有选项)之后,我重新启动了一个最小示例,在 pxd 文件中仅包含以下内容

cdef extern from "PDCLIB.h"
    ulong __stdcall PDC_Init(ulong *pErrorCode)
    ulong __stdcall PDC_CloseDevice(ulong nDeviceNo, ulong *pErrorCode)

和一个更简单的 pyx 文件,除了扩展名外具有相同的名称。

from pyphotron_pdclib cimport *

from typedefs cimport ulong, uint

cpdef init_pdc_lib():
    cdef ulong error_code = 0
    success = PDC_Init(&error_code)
    assert success != 0
    
cpdef close_cam(ulong dev_num):
    cdef ulong error_code = 0
    success = PDC_CloseDevice(dev_num, &error_code)
    assert success != 0

我在编译中看到的主要区别是

  1. 它现在可以在 c 中编译,因为没有更多的扩展类型 (cdef class)
  2. pyx文件与pxd文件同名

设置文件中的编译和链接选项如下:

extra_compile_args=['/Gz', '/fp:precise', '/Zc:wchar_t', '/Zc:forScope', '/Zc:inline'],
extra_link_args=['/DEBUG', '/MACHINE:X64'],