Python ctypes无法正确返回引用传递的某些值

时间:2012-11-15 18:11:52

标签: python ctypes

我正在尝试从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 struct对应物......

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的值是垃圾。 (消息值从指向其他内存的指针解除引用。我认为我还没有那个权利但是我必须保存那个,直到我修复了其他内容。如果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代码中设置了字节对齐。我已经完成了每个结构的打印输出大小,并验证了它们是预期的大小。

我已经在这一段时间里撞到墙上一段时间了,而且我已经没想法了。关于下一步该尝试的任何想法?

1 个答案:

答案 0 :(得分:2)

我注意到C struct的第一个元素是unsigned int,但你的python Structure是c_uint32。您确定它们的架构尺寸相同吗?

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

首先,我将从python声明和C源中的任何_pack_或其他对齐操作中删除#pragma pack。从一个已知的地方开始,原生对齐。

则...

我会写一个C程序来迭代结构并用offsetof(struct, field)打印每个成员的偏移量。此外,该程序应调用First()和一些对Next()的调用,并将结构(以二进制形式)写入文件。

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

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