2012-11-15 63 views
5

我想從Python調用一個DLL。該DLL是直寫C,沒有什麼非常複雜的。這是ctypes正是爲之而設計的。但是,我無法在從DLL函數調用返回的結構中獲取正確的值。傳遞的結構中的第一個值會正確返回,但其他值不會。Python ctypes不正確地返回通過引用傳遞的一些值

該代碼從內存中的緩衝區中讀取多條消息。它使用ReadFirst()/ ReadNext()編碼範例。有一個「當前消息」結構被傳入以跟蹤狀態。

這裏是C結構,其保持當前ReadFirst()/ ReadNext()狀態......

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下編譯的。我已經使用了這個DLL與各種C和C++ .NET程序沒有任何問題。

我已經使用ctypes和結構來返回其他代碼中的值,它似乎工作得很好。由於結構中的第一個值是確定的,但隨後的值是不正確的,所以「感覺」就像是數據對齊問題。我已經在ctypes結構中設置了 = 1,並在編譯的DLL代碼中設置了字節對齊。我已經完成每個結構的sizeof打印輸出,並驗證它們是預期的大小。

我已經在我的頭上撞過牆了一段時間了,現在已經沒有想法了。關於接下來要做什麼的任何想法?

+0

在Python代碼,您通過按地址(self.CurrMsg),但你打印從Decode1553.CurrMsg領域。你不應該從self.CurrMsg打印字段嗎? –

回答

2

我注意到C結構的第一個元素是unsigned int,但是你的python Structure是c_uint32。你確定它們在你的架構上的大小相同?

如果沒有任何幫助,我會做以下追查問題的根源:

首先,我從Python聲明中刪除_pack_和C源代碼的任何#pragma pack或其他對齊操作。從一個已知的地方開始,本地對齊。

然後......

我會寫一個C程序來迭代結構並用offsetof(struct, field)打印每個成員的偏移量。另外,這個程序應該調用First()和一些對Next()的調用,並將結構以二進制方式寫入文件。

接下來,使用struct模塊編寫python腳本,並遍歷從C程序中寫出的結構,並掌握字段大小和對齊的知識,調用struct.unpack(...)查找正確定義結構的格式字符串。

有了這些知識,你應該能夠正確地編寫你的ctypes.Structure

相關問題