如何让C库修改通过ctypes传递的结构?

zbsbpyhn  于 2023-01-08  发布在  其他
关注(0)|答案(1)|浏览(151)

下面是我的C代码:

typedef struct {
    int a;
} A;

__declspec(dllexport) A new_a(int x) {
    A a = {x};
    return a;
}

__declspec(dllexport) void change(A a) { a.a++; }

__declspec(dllexport) int get_a(A a) { return a.a; }

我把它编译成了一个MODULE DLL,然后用Python做了如下操作:

import ctypes

lib = ctypes.windll.LoadLibrary('C:\\Users\\avishah\\CLionProjects\\Math\\lib\\libpythonclass2.dll')

a = lib.new_a(10)
print(lib.get_a(a), type(a))  # gives 10, says type of a is 'int'
lib.change(a)  # doesn't change value
print(lib.get_a(a))  # still shows 10

我确信我必须在C代码中使用指针,但它通常会给我这样的错误:

OSError: exception: access violation reading 0x000000000000000A
r3i60tvu

r3i60tvu1#

您的代码有两个问题。
第一个问题是没有为导入的FFI函数指定类型签名,需要为结构创建类型定义,并为导入的函数分配参数和返回类型。

import ctypes

lib = ctypes.windll.LoadLibrary(r'C:\Users\avishah\CLionProjects\Math\lib\libpythonclass2.dll')

class A(ctypes.Structure):
    _fields_ = (
        ('a', ctypes.c_int),
    )

new_a = lib.new_a
new_a.restype = A
new_a.argtypes = (ctypes.c_int,)

change = lib.change
change.restype = None
change.argtypes = (A,)

get_a = lib.get_a
get_a.restype = ctypes.c_int
get_a.argtypes = (A,)

第二个是change函数通过值来接收结构,换句话说,该函数对参数列表中接收到的结构的副本进行操作,这意味着调用者持有的副本永远不会被修改,要真正修改该结构,需要传递一个指针:
x一个一个一个一个x一个一个二个x
现在,您的代码应该如您所期望的那样运行:

a = new_a(10)
print(get_a(a), type(a))   # should print 10 <class '__main__.A'>
change(ctypes.byref(a))    # equivalent C: change(&a);
print(get_a(a))            # should print 11

相关问题