当前位置: 首页 > news >正文

编程语言扩展与驱动交互 - C扩展

前面已讨论了对于编程语言来说,是如何实现扩展的,例如 lua语言,python语言。

下面举例说明如何编写一个简单的C扩展。

如何具体编写一个简单的C扩展,可能是针对Python或Lua。由于前面的回答已经涵盖了Python和Lua的扩展机制,并且提供了Python调用DeviceIoControl的例子,现在想进一步了解如何编写一个简单的C扩展。

现在需要给出一个简单但完整的示例,展示如何编写一个Python C扩展模块,并编译使用。同时,要解释关键步骤和代码结构。还要注意,回答应该详细且深入。

计划提供:

  1. 概述Python C扩展的基本概念。

  2. 准备开发环境(需要Python头文件、编译器,例如Visual Studio或MinGW)。

  3. 编写C代码,包含初始化函数、方法定义、函数实现。

  4. 编写setup.py来编译扩展。

  5. 编译并测试。

  6. 可能提到一些注意事项,如引用计数、错误处理。

可以编写一个简单的扩展,例如一个计算斐波那契数列的函数,或者一个简单的加法函数,以展示基本操作。为了避免过于复杂,提供一个简单的“hello”模块,包含一个返回字符串的函数和一个整数相加的函数。

此外,由于之前关注Windows系统,可以强调在Windows上的编译步骤(使用Visual Studio Build Tools)。

确保回答包含实际的代码和解释,并指导用户如何运行。

同时也给出了Lua的C扩展示例。

接下来,我们先以Python为例,手把手带你编写一个最简单的 C 扩展模块。这个模块将包含两个函数:一个返回“Hello, World!”字符串,另一个计算两个整数的和。我们会详细解释每一步的代码和作用,并说明如何在 Windows 下编译使用它。


1. Python C 扩展基础

Python 的 C 扩展本质上是一个动态链接库(Windows 下为.pyd文件),Python 解释器可以像导入普通 Python 模块一样导入它。你需要:

  • Python 的头文件(如Python.h),定义了 Python C API 的数据结构和函数。
  • 一个 C 编译器,例如 Windows 上的Microsoft Visual Studio(或 MinGW-w64)。
  • setuptools模块,用于编译和安装扩展。

2. 编写 C 扩展代码

创建一个 C 源文件,例如mymodule.c,内容如下:

#include<Python.h>// 必须包含此头文件// 函数 1: 返回一个字符串 "Hello from C extension!"staticPyObject*hello(PyObject*self,PyObject*args){// 解析参数(此函数不需要参数,但为了演示依然调用 PyArg_ParseTuple)if(!PyArg_ParseTuple(args,""))returnNULL;// 解析失败,Python 会自动设置异常// 创建一个 Python 字符串对象并返回returnPyUnicode_FromString("Hello from C extension!");}// 函数 2: 计算两个整数的和staticPyObject*add(PyObject*self,PyObject*args){inta,b;// 解析参数:两个整数if(!PyArg_ParseTuple(args,"ii",&a,&b)){returnNULL;// 参数类型错误,Python 会引发 TypeError}// 计算结果,并包装成 Python 整数对象返回returnPyLong_FromLong(a+b);}// 模块的方法表:列出所有可供 Python 调用的函数staticPyMethodDef MyModuleMethods[]={{"hello",hello,METH_VARARGS,"Return a greeting string."},{"add",add,METH_VARARGS,"Add two integers."},{NULL,NULL,0,NULL}// 哨兵,表示结束};// 模块定义结构体staticstructPyModuleDefmymodule={PyModuleDef_HEAD_INIT,"mymodule",// 模块名NULL,// 模块文档字符串(可选)-1,// 每个解释器独立保存状态(-1 表示全局)MyModuleMethods// 方法表};// 模块初始化函数,Python 导入模块时会调用此函数PyMODINIT_FUNCPyInit_mymodule(void){returnPyModule_Create(&mymodule);}

关键点解释:

  • 所有函数都用static修饰,避免符号冲突。
  • 每个可导出函数返回PyObject*,接受selfargs参数。self在普通函数中为NULLargs是包含所有参数的元组。
  • PyArg_ParseTuple负责解析参数,格式化字符串"ii"表示两个整数。
  • PyUnicode_FromStringPyLong_FromLong将 C 类型转换为 Python 对象。
  • 方法表MyModuleMethods定义了模块中哪些函数可被 Python 调用。
  • 模块定义结构体mymodule包含模块名、方法表等信息。
  • 初始化函数必须命名为PyInit_模块名,返回一个已创建的模块对象。

3. 准备编译环境(Windows)

在 Windows 上编译 C 扩展,通常需要:

  • 安装 Python(确保开发头文件也安装了,一般在安装时勾选“Download debugging symbols”或“Development”组件)。
  • 安装Visual Studio Build Tools(或完整 Visual Studio),并选择包含“C++ 桌面开发”的工作负载。
  • 如果你使用 MinGW-w64,也可以编译,但推荐使用 Visual Studio 以保持与官方 Python 二进制兼容。

打开“开发者命令提示符”(例如“x64 Native Tools Command Prompt for VS 2022”),确保 Python 和编译器可用。


4. 编写 setup.py 构建脚本

在与mymodule.c相同的目录下创建setup.py

fromsetuptoolsimportsetup,Extension module=Extension('mymodule',# 模块名sources=['mymodule.c'],# 源文件列表)setup(name='mymodule',version='1.0',description='A simple C extension example',ext_modules=[module],)

然后运行构建命令:

python setup.py build_ext--inplace
  • build_ext是构建扩展的命令。
  • --inplace表示将生成的.pyd文件放在当前目录,方便直接导入。

如果一切顺利,你会在当前目录看到mymodule.cpXXX-win_amd64.pyd(文件名可能因 Python 版本和平台而异)。


5. 测试你的扩展

在同一目录下打开 Python 解释器,尝试导入并使用:

>>>importmymodule>>>print(mymodule.hello())HellofromC extension!>>>mymodule.add(10,20)30>>>mymodule.add(5,'a')# 故意传错类型Traceback(most recent call last):File"<stdin>",line1,in<module>TypeError:an integerisrequired(gottypestr)

恭喜!你已经成功编写并使用了第一个 C 扩展。


6. 深入一点:错误处理与引用计数

上面的例子非常简单,但真实场景中需要注意:

  • 错误处理:如果函数出错,应返回NULL并设置适当的异常。例如PyErr_SetString(PyExc_ValueError, "invalid input")
  • 引用计数:当你创建 Python 对象(如PyLong_FromLong)时,返回的是新引用。如果你不再需要该对象,应该调用Py_DECREF或让函数返回它(Python 会管理其生命周期)。上面的例子中,我们直接返回了新建的对象,是正确的。
  • 内存管理:如果在 C 中分配了内存,要确保在合适的时机释放。

对于更复杂的操作(如操作列表、字典等),你需要熟悉 Python C API 的更多细节。


7. 扩展到 Lua

Lua 编写 C 扩展的方式略有不同。大致步骤:

  • 编写 C 代码,包含lua.h等头文件,定义遵循 Lua C API 的函数。
  • 函数原型一般为static int l_myfunc(lua_State *L),通过 Lua 栈传递参数和返回值。
  • 使用luaL_newlib注册函数表。
  • 编译成动态库(Windows 下为.dll),并重命名为与 Lua 要求一致的名称(例如mylib.dll)。
  • 在 Lua 中通过require "mylib"加载。

下面举例说明Lua 的 C 扩展,进一步给出示例代码。

简化:编写一个Lua C模块,调用一个简单的Windows API(如MessageBox或GetCurrentProcessId)来演示概念,然后指出可用类似方式调用DeviceIoControl。或者直接给出一个框架,调用DeviceIoControl的示例,但需要解释清楚。

目标:提供一个清晰、可运行的Lua C扩展示例,能在Windows下编译并与Lua交互。最好包含必要的代码和编译指令。

我们要注意Lua的C API与Python不同。需要包含lua.h等头文件,使用luaL_register或luaL_newlib。函数通过lua_State*操作栈。

可以编写一个简单的mylib.c,包含两个函数:hello(返回字符串)和add(两个数相加)。然后展示如何编译成DLL,并在Lua中require。

另外,也可以展示如何调用Windows API,比如调用MessageBox,以显示如何与底层交互。为了与驱动主题关联,可以注释说明可用DeviceIoControl替换。

我们将提供完整的步骤和代码。

注意:Lua的C扩展在Windows下编译需要链接Lua库(lua5x.lib)。通常可以使用MinGW或Visual Studio。需要提供编译命令。

最终输出应该包括:C代码、编译说明、Lua测试代码,以及关于调用驱动的说明。

好的,接下来我们以Lua为例,编写一个简单的 C 扩展模块,并演示如何在 Windows 下编译和使用它。同时,我们会说明如何通过该扩展调用 Windows API(例如MessageBoxDeviceIoControl),从而与底层驱动交互。


8. Lua C 扩展基础

Lua 的 C 扩展也是一个动态链接库(Windows 下为.dll),但需要导出特定的符号(通常是luaopen_模块名)供 Lua 的require加载。扩展代码需要包含 Lua 的头文件,并使用 Lua C API 来操作栈(传递参数和返回值)。


9. 编写 C 扩展代码

创建一个源文件,例如mylib.c,内容如下:

#include<lua.h>#include<lauxlib.h>#include<lualib.h>/* 函数1:返回一个字符串 "Hello from Lua C extension!" */staticintl_hello(lua_State*L){/* 将字符串压入栈顶 */lua_pushstring(L,"Hello from Lua C extension!");return1;/* 返回值的个数 */}/* 函数2:计算两个整数的和 */staticintl_add(lua_State*L){/* 从栈中获取两个参数(假设都是整数) */inta=luaL_checkinteger(L,1);/* 检查并返回第一个参数(索引1) */intb=luaL_checkinteger(L,2);/* 检查并返回第二个参数(索引2) *//* 将结果压入栈顶 */lua_pushinteger(L,a+b);return1;/* 返回一个值 */}/* 函数3:调用 Windows API MessageBox 演示与系统交互 */#ifdef_WIN32#include<windows.h>staticintl_messagebox(lua_State*L){constchar*text=luaL_checkstring(L,1);constchar*caption=luaL_optstring(L,2,"Message");intret=MessageBoxA(NULL,text,caption,MB_OK);lua_pushinteger(L,ret);/* 返回 MessageBox 的结果(例如 IDOK) */return1;}#endif/* 注册表:列出模块中的所有函数 */staticconstluaL_Reg mylib[]={{"hello",l_hello},{"add",l_add},#ifdef_WIN32{"messagebox",l_messagebox},#endif{NULL,NULL}/* 哨兵 */};/* 模块入口函数:模块被 require 时调用,必须命名为 luaopen_模块名 */intluaopen_mylib(lua_State*L){/* 创建一个新表,并将 mylib 中的函数注册到表中 */luaL_newlib(L,mylib);return1;/* 返回这个表 */}

关键点解释:

  • 头文件<lua.h>提供基础 API,<lauxlib.h>提供辅助库,<lualib.h>提供标准库(本例未使用)。
  • 所有导出的 C 函数都遵循int func(lua_State *L)原型,返回值是推送到栈上的结果个数。
  • 使用luaL_checkintegerluaL_checkstring来安全地获取参数并自动抛出类型错误。
  • 通过lua_pushstringlua_pushinteger将结果压栈。
  • luaL_Reg结构体数组定义了函数名与 C 函数的映射。
  • luaL_newlib是一个辅助宏,用于创建一张新表并将函数列表注册进去。
  • 模块入口函数名必须为luaopen_模块名,此处模块名为mylib,因此函数名为luaopen_mylib
  • 为了演示与 Windows API 交互,我们增加了一个messagebox函数,调用MessageBoxA弹窗。这展示了如何通过 C 扩展调用操作系统 API,类似地,你可以用DeviceIoControl来与驱动通信。

10. 编译 Lua C 扩展(Windows)

准备工作:
  • 安装 Lua(可以从 Lua.org 下载源码或使用包管理器,如vcpkg install lua)。
  • 确保有 C 编译器(如 Visual Studio 或 MinGW-w64)。
  • 知道 Lua 的安装路径,以便找到头文件(lua.h等)和导入库(lua5x.liblua54.lib)。
使用 Visual Studio 编译:

打开“开发者命令提示符”,切换到源文件目录,执行以下命令(假设 Lua 安装在C:\Lua,且库文件名为lua54.lib):

cl /LD /I"C:\Lua\include"mylib.c /link /LIBPATH:"C:\Lua\lib"lua54.lib /OUT:mylib.dll
  • /LD表示构建 DLL。
  • /I指定头文件路径。
  • /link后指定链接选项,/LIBPATH指定库路径,最后指定要链接的 Lua 导入库。
  • /OUT指定输出 DLL 名称,必须与模块名一致(mylib.dll)。

如果使用 MinGW-w64,命令类似:

gcc-shared-I"C:\Lua\include"mylib.c -L"C:\Lua\lib"-llua54-omylib.dll

编译成功后,你会得到mylib.dll。注意:Lua 的require在 Windows 下查找 DLL 时,会尝试模块名.dll模块名.so,所以直接命名为mylib.dll即可。


11. 在 Lua 中测试扩展

将生成的mylib.dll放在 Lua 脚本同一目录(或 Lua 的package.cpath包含的路径下)。编写测试脚本test.lua

localmylib=require"mylib"-- 加载扩展print(mylib.hello())-- 调用 hello 函数localsum=mylib.add(10,20)print("10 + 20 =",sum)-- 如果编译时包含了 messagebox 函数(Windows)ifmylib.messageboxthenmylib.messagebox("Hello from Lua!","Lua Extension Demo")end

运行脚本:

lua test.lua

你应该看到输出:

Hello from Lua C extension! 10 + 20 = 30

如果messagebox函数可用,还会弹出一个消息框。


12. 如何扩展到与驱动交互(DeviceIoControl)

上面的messagebox示例展示了如何在 C 扩展中调用 Windows API。要调用设备驱动,只需将函数体内的代码替换为与设备交互的逻辑。例如,编写一个函数read_device,内部使用CreateFile打开设备,然后调用DeviceIoControl,最后将结果返回给 Lua。

大致框架如下(省略错误检查):

#include<windows.h>staticintl_read_device(lua_State*L){constchar*device_path=luaL_checkstring(L,1);DWORD bytes_returned;charbuffer[256];HANDLE hDevice=CreateFileA(device_path,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if(hDevice==INVALID_HANDLE_VALUE){lua_pushnil(L);lua_pushstring(L,"Failed to open device");return2;/* 返回 nil 和错误信息 */}BOOL success=DeviceIoControl(hDevice,IOCTL_GET_SERIAL_NUMBER,/* 你的控制码 */NULL,0,buffer,sizeof(buffer),&bytes_returned,NULL);CloseHandle(hDevice);if(success){lua_pushlstring(L,buffer,bytes_returned);return1;}else{lua_pushnil(L);lua_pushstring(L,"DeviceIoControl failed");return2;}}

然后将此函数添加到mylib注册表中,Lua 脚本就可以调用它来获取设备数据了。


13. 与 Python 扩展的对比

  • Python的扩展通过PyArg_ParseTuple解析参数,通过返回PyObject*返回值。
  • Lua的扩展通过栈传递参数和返回值,更加底层,但同样清晰。
  • 两者都需要编写包装代码,最终编译成动态库供脚本语言加载。

14. 总结

通过以上示例,你应该掌握了编写 Python C 扩展的基本流程。这不仅是调用底层 API 的基础,也是将性能关键代码用 C 重写、集成现有 C 库的常用方法。同时也应该能够理解 Lua C 扩展的基本结构,以及如何通过它调用 Windows API 甚至设备驱动。

http://www.jsqmd.com/news/502472/

相关文章:

  • 【JUC 一】线程 进程 synchronized Lock锁 生产者 消费者 8锁 线程安全集合类...
  • 企业上云如何避坑?2026年主流云主机深度对比与决策指南 - 资讯焦点
  • 深入解析PE内存注入技术:从文件到shellcode的转换机制
  • Python爬虫进阶:自动化采集语音训练数据实战
  • 国内云平台选购指南:主流服务对比与价格解析 - 资讯焦点
  • macOS鼠标滚动优化解决方案:提升效率的平滑滚动技术实现
  • MATLAB R2023b安装包下载及安装步骤说明
  • Phi-3-Mini-128K实战:利用卷积神经网络原理优化模型提示策略
  • 2026南京定制假发优质公司推荐榜 - 资讯焦点
  • 郑州叮叮智能荣登2026行业十大品牌,新晋实力派彰显领军风范 - 深度智识库
  • 2026年青岛留学中介哪家口碑好:五家优选深度解析 - 科技焦点
  • webpack的使用步骤及插件使用方法
  • 武汉医美哪家好?推荐几家靠谱的武汉医疗美容和机构 - 资讯焦点
  • HPatches数据集实战:从特征点检测到匹配精度的全链路评估
  • ROS+Prescan+Carsim联调实战:手把手搭建自动驾驶硬件在环测试平台(附避坑指南)
  • 选对名师少走弯路,药学主任药师考试高分名师推荐 - 医考机构品牌测评专家
  • EasyAnimateV5-7b-zh-InP与Typora结合:Markdown文档转视频教程
  • 别再跟风“代装”了!2026年OpenClaw“养虾”避坑实战指南
  • 2026年青岛留学机构推荐:五家优选深度解析 - 科技焦点
  • SeqGPT-560m轻量生成教程:从零训练专属领域微调版本完整流程
  • 存算一体C封装性能断崖式下降的真相:Cache Line对齐缺失、MMIO屏障遗漏、DMA描述符链错序(附GDB+Trace32联合调试清单)
  • 4.2.2 存储->POSIX 文件系统标准(IEEE,ISO IEC 采纳):Btrfs(B-tree File System)B 树文件系统
  • Gemma-3-12b-it图文问答效果展示:古籍扫描件识别+繁体转简体+释义
  • 深求·墨鉴(DeepSeek-OCR-2)效果展示:水墨留痕可视化识别过程
  • AI 生产与全民 Claw 时代:低门槛工具如何改变生产力格局
  • 能提升客户服务的 CRM 系统推荐 - SaaS软件-点评
  • SiamMask核心原理深度解析:孪生网络如何统一跟踪与分割
  • Emotion2Vec+快速入门:无需代码,用WebUI轻松识别语音情感
  • 高级职称外科护理讲师硬核测评|苏菲老师专属适配性全解析 - 医考机构品牌测评专家
  • AtlasOS性能优化终极方案:5个维度提升系统响应速度30%