2016-07-13 170 views
0

考慮下面的Python困惑使用CTYPE指針在Python

>>> from ctypes import * 
>>> from ctypes.wintypes import * 
>>> class _Filename(Structure): 
...  _fields_ = [("NameLengthInBytes", USHORT), 
...     ("Name",    WCHAR * 1)] 
... 
>>> req = create_string_buffer(20) 
>>> preq = cast(req, POINTER(_Filename)) 
>>> req 
<ctypes.c_char_Array_20 object at 0x0000000002038DC8> 
>>> preq.contents 
<__main__._Filename object at 0x0000000002038EC8> 
>>> preq.contents.NameLengthInBytes = 10 
>>> memmove(preq.contents.Name, u"ABCDE", 10) 
31932464L 
>>> memoryview(req).tobytes() 
'\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 
>>> preq.contents.Name=u"Z" 
>>> memoryview(req).tobytes() 
'\n\x00Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 

我很困惑。我預計內容的地址preqreq相同。至少這是我認爲會做。

我想要做的是創建一個連續的內存塊,其中包含NameLengthInBytes後跟一個寬度爲一個任意長度的字符串。我想編寫代碼,以便它取決於字段名稱_Filename,以便如果_Filename的定義發生更改(例如通過在Name之前添加額外字段),代碼仍會複製到正確的緩衝區位置。

任何人都可以幫助我理解Python和ctypes如何直接操作內存以及如何實現我期望的目標?

謝謝。

回答

0

好吧,我已經想出了我自己的問題。

首先,我發現一個提示here

注意的ctypes沒有OOR(原始對象返回),它每次 構建了一個全新的等價的對象,您檢索 屬性:

>>> from ctypes import * 
>>> i = c_int(42) 
>>> pi = pointer(i) 
>>> pi.contents is i 
False 
>>> pi.contents is pi.contents 
False 
>>> 

這解釋了爲什麼req和preq.contents不同。

下一個問題是如何得到我需要的地址。我想出了這個功能:

def PackFilename(fn): 
    bytes = len(fn) * sizeof(WCHAR) 
    needed = sizeof(_Filename) + bytes 
    req = create_string_buffer(needed) 
    preq = cast(req, POINTER(_Filename)) 
    preq.contents.NameLengthInBytes = bytes 
    memmove(addressof(preq[0]) + _Filename.Name.offset, fn, bytes) 
    return preq.contents, needed 

請注意,你必須採取針對性的以_filename再加入適當的偏移來計算所需要的memmove地址。

用法是,像這樣:

>>> j,js=PackFilename(u"c:\\jae\\temp") 
>>> j 
<__main__._Filename object at 0x0000000001FF8DC8> 
>>> js 
26 
>>> j.NameLengthInBytes 
22 
>>> j.Name 
u'c' 
>>> wstring_at(addressof(j) + _Filename.Name.offset, j.NameLengthInBytes/sizeof(WCHAR)) 
u'c:\\jae\\temp' 

注意,解壓縮,你必須扭轉你的包做了什麼名稱字段。可以放入UnpackFilename功能:

def UnpackFilename(fn): 
    if type(fn) is _Filename: 
     size = fn.NameLengthInBytes/sizeof(WCHAR) 
     return wstring_at(addressof(fn) + _Filename.Name.offset, size) 
    else: 
     log.debug("Unexpected argument type: %s", type(fn)) 
     raise TypeError() 

我確定所有字節都通過檢查內容的連續塊中PackFilename函數(()使用memoryview(REQ).tobytes)。