5

我正在尝试从 Python 调用 DLL。DLL 是用纯 C 语言编写的,没有什么特别复杂的地方。这正是 ctypes 的用途。但是,我无法在从 DLL 函数调用返回的结构中获取正确的值。传递的结构中的第一个值被正确返回,但其他值不是。

该代码从内存中的缓冲区读取多条消息。它使用 ReadFirst() / ReadNext() 编码范例。有一个“当前消息”结构被传入以跟踪状态。

这是保存当前 ReadFirst() / ReadNext() 状态的 C 结构...

typedef struct
    {
    unsigned int            uMsgNum;
    uint32_t                ulCurrOffset;
    uint32_t                ulDataLen;
    Su1553F1_ChanSpec     * psuChanSpec;
    Su1553F1_Header       * psu1553Hdr;
    SuCmdWordU            * psuCmdWord1;
    SuCmdWordU            * psuCmdWord2;
    uint16_t              * puStatWord1;
    uint16_t              * puStatWord2;
    uint16_t                uWordCnt;
    uint16_t              * pauData;
    } Su1553F1_CurrMsg;

这是用 ctypes 编写的 ctypes 结构对应物...

class CurrMsg_1553F1(ctypes.Structure):
    ''' Data structure for the current 1553 message info structure '''
    _pack_   = 1
    _fields_ = [("MsgNum",      ctypes.c_uint32),
                ("CurrOffset",  ctypes.c_uint32),
                ("DataLen",     ctypes.c_uint32),
                ("pChanSpec",   ctypes.POINTER(ChanSpec_1553F1)),
                ("p1553Hdr",    ctypes.c_void_p),
                ("pCmdWord1",   ctypes.POINTER(CmdWord)),
                ("pCmdWord2",   ctypes.POINTER(CmdWord)),
                ("pStatWord1",  ctypes.c_void_p),
                ("pStatWord2",  ctypes.c_void_p),
                ("WordCnt",     ctypes.c_uint16),
                ("pData",       ctypes.c_void_p)]

我将在下面提供更多详细信息,但问题的症结在于调用 ReadFirst() / ReadNext() 正确返回 MsgNum 的值,但其他值是垃圾。这是一个示例输出...

MsgNum 0  CurrOffset 40912728  DataLen 40912732  Messages = 3664897
MsgNum 1  CurrOffset 40912728  DataLen 40912748  Messages = 60417
MsgNum 2  CurrOffset 40912728  DataLen 40912748  Messages = 60417
etc.

MsgNum 是正确的,并且 ReadFirst() / ReadNext() 迭代正确的次数。但是,CurrOffset 和 DataLen 的值是垃圾。(Messages 值已从指向其他内存的指针中取消引用。我认为我还没有这个权利,但我必须保存那个,直到我修复了其他内容。如果 CurrOffset 和 DataLen 错误,则指向 pChanSpec也可能是错误的。)

这是功能的接口...

EnI106Status enI106_Decode_First1553F1
    (SuI106Ch10Header * psuHeader,
     void             * pvBuff,
     Su1553F1_CurrMsg * psuMsg);

EnI106Status enI106_Decode_Next1553F1
    (Su1553F1_CurrMsg * psuMsg);

这些 DLL 函数被调用(连同调试打印语句)...

def __init__(self, PacketIO):
    self.CurrMsg  = CurrMsg_1553F1()

def Decode_First1553F1(self):
    Status = self.PacketIO._IrigDataDll.enI106_Decode_First1553F1(ctypes.byref(self.PacketIO.Header), ctypes.byref(self.PacketIO.Buffer),  ctypes.byref(self.CurrMsg))
    print "MsgNum %d  CurrOffset %d  DataLen %d  Messages = %d" % \
        (Decode1553.CurrMsg.MsgNum, Decode1553.CurrMsg.CurrOffset, Decode1553.CurrMsg.DataLen, Decode1553.CurrMsg.pChanSpec.contents.MsgCnt)
    return Status

def Decode_Next1553F1(self):
    Status = self.PacketIO._IrigDataDll.enI106_Decode_Next1553F1(ctypes.byref(self.CurrMsg))
    print "MsgNum %d  CurrOffset %d  DataLen %d  Messages = %d" % \
        (Decode1553.CurrMsg.MsgNum, Decode1553.CurrMsg.CurrOffset, Decode1553.CurrMsg.DataLen, Decode1553.CurrMsg.pChanSpec.contents.MsgCnt)
    return Status

C DLL 是在 Visual Studio 2005 下编译的。我在各种 C 和 C++ .NET 程序中多次使用这个 DLL,没有任何问题。

我使用 ctypes 和 structs 在其他代码中返回值,它似乎工作得很好。由于结构中的第一个值是可以的,但随后的值与这种“感觉”相去甚远,就像数据对齐问题一样。我在 ctypes 结构中设置了pack =1 并在编译的 DLL 代码中设置了字节对齐。我已经完成了每个结构的 sizeof 打印输出,并验证了它们是预期的大小。

我已经在这个问题上撞墙有一段时间了,我没有想法。关于接下来要尝试什么的任何想法?

4

1 回答 1

2

我注意到 C 结构的第一个元素是 unsigned int,但您的 python 结构是 c_uint32。你确定它们在你的架构上是一样的大小吗?

如果这没有任何帮助,我会执行以下操作来追踪问题的根源:

首先,我将从_pack_python 声明和#pragma packC 源代码中的任何或其他对齐操作中删除。从已知的地方开始,原生对齐。

然后...

我会编写一个 C 程序来迭代结构并使用offsetof(struct, field). 此外,该程序应该调用 First() 和少量调用 Next() 并将结构按原样以二进制形式写入文件。

接下来,使用 struct 模块编写一个 python 脚本,并遍历您从 C 程序中写出的结构,并掌握字段大小和对齐方式的知识,调用struct.unpack (...) 查找格式正确定义结构的字符串。

有了这些知识,您应该能够正确编码 ctypes.Structure

于 2012-11-16T13:45:43.637 回答