Cython调用外部C代码失败:UserScanInit等函数未定义求助
问题描述
从微软avscan示例中提取用户态代码,尝试将其主函数逻辑迁移到Cython内嵌C代码中实现,编译时出现链接错误,具体信息如下:
代码文件
cythonmod.pyx
cdef extern from *: r""" #include <windows.h> #include <stdio.h> #include <fltUser.h> #include "..\\avscan\\user\\utility.h" #include "..\\avscan\\inc\\avlib.h" #include "..\\avscan\\user\\userscan.h" HRESULT UserScanInit(PUSER_SCAN_CONTEXT pContext); int usermodeDef(){ printf("User mode connecting to kernel mode\n"); UCHAR c; HRESULT hr = S_OK; USER_SCAN_CONTEXT userScanCtx = {0}; hr = UserScanInit(&userScanCtx); if (FAILED(hr)) { fprintf(stderr, "Failed to initialize user scan data\n"); DisplayError( hr ); return 255; } // 读取用户输入直到按下'q'退出 for(;;) { printf("press 'q' to quit: "); c = (unsigned char) getchar(); if (c == 'q') { break; } } // 结束扫描线程上下文 hr = UserScanFinalize(&userScanCtx); if (FAILED(hr)) { fprintf(stderr, "Failed to finalize the user scan data.\n"); } return 0; } """ int usermodeDef()
setup.py
from distutils.core import setup, Extension from Cython.Build import cythonize ext_modules = [ Extension( "cythonmod", sources=["cythonmod.pyx"], include_dirs=["../avscan/inc", "../avscan/user"], extra_compile_args=["-include", "../avscan/user/userscan.h"], ), ] setup( name="avscanner", ext_modules=cythonize(ext_modules), compiler_directives={'language_level': 3, "unraisable_tracebacks": True} )
编译错误信息
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: build\temp.win-amd64-3.9\Release\commonmodules.o:commonmodules.c:(.text+0x75df5): undefined reference to `UserScanInit' c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: build\temp.win-amd64-3.9\Release\commonmodules.o:commonmodules.c:(.text+0x75e1d): undefined reference to `UserScanFinalize' c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: build\temp.win-amd64-3.9\Release\commonmodules.o:commonmodules.c:(.text+0x75e5b): undefined reference to `DisplayError' collect2.exe: error: ld returned 1 exit status error: command 'C:\\MinGW\\bin\\gcc.exe' failed with exit code 1
生成的GCC编译选项
gcc -mdll -O -Wall -I../avscan/inc -I../avscan/user -IC:\Users\username\AppData\Local\Programs\Python\Python39\include -IC:\Users\username\AppData\Local\Programs\Python\Python39\include -c cythonmod.c -o build\temp.win-amd64-3.9\Release\cythonmod.o -include ../avscan/user/userscan.h
已尝试直接用cdef extern from "../avscan/user/userscan.h"引入头文件,但问题未解决。请问如何在Cython内嵌C代码中正确导入外部C代码?
解决方案
你遇到的是链接错误:编译器能通过头文件找到函数声明,但找不到这些函数的实现代码。UserScanInit/UserScanFinalize的实现在userscan.c中,DisplayError的实现在utility.c中,当前setup.py仅编译了Cython的.pyx文件,未包含这些依赖的C源文件。
步骤1:添加依赖C源文件到setup.py
修改Extension的sources参数,把avscan用户态的C源文件路径加入,同时补充必要的链接库:
ext_modules = [ Extension( "cythonmod", sources=[ "cythonmod.pyx", "../avscan/user/userscan.c", "../avscan/user/utility.c" ], include_dirs=["../avscan/inc", "../avscan/user"], # MinGW编译需链接fltUser库 extra_link_args=["-lfltUser"], ), ]
步骤2:简化Cython的extern声明
无需手动声明函数,直接从头文件引入类型和函数,代码更规范:
# 从userscan.h引入结构体和函数声明 cdef extern from "../avscan/user/userscan.h": ctypedef struct USER_SCAN_CONTEXT: pass # Cython只需知晓结构体类型,无需具体成员 HRESULT UserScanInit(USER_SCAN_CONTEXT* pContext) HRESULT UserScanFinalize(USER_SCAN_CONTEXT* pContext) # 从utility.h引入DisplayError函数 cdef extern from "../avscan/user/utility.h": void DisplayError(HRESULT hr) # 内嵌主逻辑代码 cdef extern from *: r""" #include <windows.h> #include <stdio.h> #include <fltUser.h> int usermodeDef(){ printf("User mode connecting to kernel mode\n"); UCHAR c; HRESULT hr = S_OK; USER_SCAN_CONTEXT userScanCtx = {0}; hr = UserScanInit(&userScanCtx); if (FAILED(hr)) { fprintf(stderr, "Failed to initialize user scan data\n"); DisplayError( hr ); return 255; } for(;;) { printf("press 'q' to quit: "); c = (unsigned char) getchar(); if (c == 'q') { break; } } hr = UserScanFinalize(&userScanCtx); if (FAILED(hr)) { fprintf(stderr, "Failed to finalize the user scan data.\n"); } return 0; } """ int usermodeDef()
步骤3:注意编译环境兼容性
avscan示例是针对MSVC编写的,MinGW编译可能存在Windows API类型定义差异。如果上述步骤仍有问题,建议切换到MSVC编译:
- 安装Visual Studio并勾选「Desktop development with C++」组件
- 打开VS开发者命令提示符,运行
python setup.py build_ext --inplace
内容的提问来源于stack exchange,提问作者Abdul
相关产品推荐
相关产品推荐

