2016-07-13 174 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)。