在Python中调用libsmapi.so库时遇_as_parameter_错误求助
解决ctypes调用libsmapi.so时的
_as_parameter_ KeyError问题 问题背景
在Linux环境下编写Python程序处理squishDB格式文件(存储1980-2000年Fidonet网络旧消息),此前用C/C++调用libsmapi.so库无问题,现在尝试结合cstruct和ctypes调用该库时,调用MsgOpenApi函数出现参数错误。
原代码
import ctypes import cstruct class minf(cstruct.MemCStruct): __def__ = """ struct _minf { short req_version; short def_zone; short haveshare; /* filled in by msgapi routines - no need to set this */ /* Version 2 Information */ short smapi_version; short smapi_subversion; }; """ def main(): smapi = ctypes.CDLL('libsmapi.so') m = minf() m.def_zone = 2 m.req_version = 0 if smapi.MsgOpenApi(m): print("Error opening library") exit(1) if __name__ == '__main__': main()
报错信息
Traceback (most recent call last): File "/home/neo/progetti/PycharmProjects/smapitest/main.py", line 33, in <module> main() File "/home/neo/progetti/PycharmProjects/smapitest/main.py", line 27, in main if smapi.MsgOpenApi(m): ^^^^^^^^^^^^^^^^^^^ ctypes.ArgumentError: argument 1: KeyError: '_as_parameter_' Process finished with exit code 1
解决方案
原因分析
cstruct.MemCStruct的实例默认没有实现_as_parameter_属性,而ctypes在传递参数时会尝试读取这个属性来获取C语言层面的参数内存地址,因此抛出KeyError。
方法1:改用ctypes原生结构体定义
直接用ctypes.Structure定义与C兼容的结构体,这样实例可以直接被ctypes识别:
import ctypes class minf(ctypes.Structure): _fields_ = [ ("req_version", ctypes.c_short), ("def_zone", ctypes.c_short), ("haveshare", ctypes.c_short), ("smapi_version", ctypes.c_short), ("smapi_subversion", ctypes.c_short) ] def main(): smapi = ctypes.CDLL('libsmapi.so') m = minf() m.def_zone = 2 m.req_version = 0 # 显式指定函数参数类型,提升兼容性 smapi.MsgOpenApi.argtypes = [ctypes.POINTER(minf)] if smapi.MsgOpenApi(ctypes.byref(m)): print("Error opening library") exit(1) if __name__ == '__main__': main()
注意:如果
MsgOpenApi需要结构体值传递,直接传m即可;如果需要指针,用ctypes.byref(m),根据C函数原型调整。
方法2:给MemCStruct类添加_as_parameter_属性
如果坚持使用cstruct,可以给minf类添加_as_parameter_属性,让ctypes能获取到结构体的内存地址:
import ctypes import cstruct class minf(cstruct.MemCStruct): __def__ = """ struct _minf { short req_version; short def_zone; short haveshare; /* filled in by msgapi routines - no need to set this */ /* Version 2 Information */ short smapi_version; short smapi_subversion; }; """ @property def _as_parameter_(self): # 获取结构体的内存缓冲区地址 return ctypes.c_void_p(self.get_buf()) def main(): smapi = ctypes.CDLL('libsmapi.so') m = minf() m.def_zone = 2 m.req_version = 0 # 指定函数参数类型为void* smapi.MsgOpenApi.argtypes = [ctypes.c_void_p] if smapi.MsgOpenApi(m): print("Error opening library") exit(1) if __name__ == '__main__': main()
验证说明
两种方法都能让MsgOpenApi正确接收结构体参数,解决_as_parameter_的KeyError问题。建议优先使用方法1,因为ctypes原生结构体与C函数的兼容性更好,也更符合ctypes的设计规范。
内容的提问来源于stack exchange,提问作者felice.murolo
相关产品推荐
相关产品推荐

