You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:32:46