复杂Ctypes结构的问题

2024-10-01 00:15:30 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在使用ctypes将一些C代码[FANUC FOCAS库]移植到Python

在我必须移植的最复杂的结构之一中,我无法捕获所有变量的值,也无法找出原因

中的(摘自FANUC的fwlib32.h)

typedef struct speedelm {
    long    data;
    short   dec;
    short   unit;
    short   disp;
    char    name;
    char    suff;
} SPEEDELM ;

typedef struct odbspeed {
    SPEEDELM    actf;
    SPEEDELM    acts;
} ODBSPEED ;

FWLIBAPI short WINAPI cnc_rdspeed( unsigned short, short, ODBSPEED * );

然后,对于Python,我写道:

import ctypes

class SpeedElmT(ctypes.Structure):
    pass

SpeedElmT._fields_ = [
    ("data", ctypes.c_long),
    ("dec", ctypes.c_short),
    ("unit", ctypes.c_short),
    ("disp", ctypes.c_short),
    ("name", ctypes.c_char_p),
    ("suff", ctypes.c_char_p)
]

class ODBSpeed_T(ctypes.Structure):
    _fields_ = [
        ("actf", SpeedElmT),
        ("acts", SpeedElmT),
    ]

# import library
fwl = ctypes.cdll.LoadLibrary("/fwlib_path/")

fwl.cnc_rdspeed.argtypes = ctypes.c_ushort, ctypes.c_short, ctypes.POINTER(ODBSpeed_T)
fwl.cnc_rdspeed.restype = ctypes.c_short

在C中运行它的示例(可在inventcom.net上找到)

#include "fwlib32.h"

void example( void )
{
    ODBSPEED speed;
    short ret = cnc_rdspeed(h, -1, &speed);
    if(!ret) {
        printf("%c = %d\n", speed.actf.name, speed.actf.data);
        printf("%c = %d\n", speed.acts.name, speed.acts.data);
    }
}

而且,在Python中,我尝试了

speed = ctypes.pointer(ODBSpeed_T())

r = fwl.cnc_rdspeed(h, ctypes.c_short(-1), speed)

if r == 0:
    print(speed[0].actf.data)    # This one returns the correct value
    print(speed[0].acts.data)    # Zero when not Zero

我真的不明白为什么会起作用。数据不会返回预期值

有人能帮我吗?非常感谢


Tags: namedatactypesshortspeedcnccharacts
1条回答
网友
1楼 · 发布于 2024-10-01 00:15:30

错误是C代码有char而不是char*。Python中的字段必须是c_char而不是c_char_p,因此结构大小是错误的

以下是具有相同接口的测试DLL:

#include <windows.h>

#ifdef _WIN32
#   define FWLIBAPI __declspec(dllexport)
#else
#   define FWLIBAPI
#endif

typedef struct speedelm {
    long    data;
    short   dec;
    short   unit;
    short   disp;
    char    name;
    char    suff;
} SPEEDELM;

typedef struct odbspeed {
    SPEEDELM    actf;
    SPEEDELM    acts;
} ODBSPEED;

FWLIBAPI short WINAPI cnc_rdspeed(unsigned short a, short b, ODBSPEED* p) {
    p->actf.data = a + b;
    p->actf.dec = 1;
    p->actf.unit = 2;
    p->actf.disp = 3;
    p->actf.name = 'A';
    p->actf.suff = 'B';
    p->acts.data = a - b;
    p->acts.dec = 4;
    p->acts.unit = 5;
    p->acts.disp = 6;
    p->acts.name = 'C';
    p->acts.suff = 'D';
    return 7;
}

test.py:

import ctypes

class SpeedElmT(ctypes.Structure):
    _fields_ = (('data', ctypes.c_long),
                ('dec', ctypes.c_short),
                ('unit', ctypes.c_short),
                ('disp', ctypes.c_short),
                ('name', ctypes.c_char),  # C code had char not char*
                ('suff', ctypes.c_char))  # ditto
    def __repr__(self):
        return f'SpeedElemT({self.data},{self.dec},{self.unit},{self.disp},{self.name},{self.suff})'

class ODBSpeed_T(ctypes.Structure):
    _fields_ = (('actf', SpeedElmT),
                ('acts', SpeedElmT))
    def __repr__(self):
        return f'ODBSpeed_T({self.actf!r},{self.acts!r})'

fwl = ctypes.WinDLL('./test')  # WinDLL for WINAPI, which is __stdcall (matters on 32-bit Python)
fwl.cnc_rdspeed.argtypes = ctypes.c_ushort, ctypes.c_short, ctypes.POINTER(ODBSpeed_T)
fwl.cnc_rdspeed.restype = ctypes.c_short

speed = ODBSpeed_T()                               # Create an instance
r = fwl.cnc_rdspeed(333, 111, ctypes.byref(speed)) # pass instance by reference
print(r)
print(speed)

输出:

7
ODBSpeed_T(SpeedElemT(444,1,2,3,b'A',b'B'),SpeedElemT(222,4,5,6,b'C',b'D'))

相关问题 更多 >