使用ctype指针和Python中的memmove混淆

时间:2016-07-13 21:42:11

标签: python-2.7 ctypes

考虑以下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'

我很困惑。我希望 preq 内容的地址与 req 相同。至少这是我认为演员会做的事情。

我想要做的是创建一个连续的内存块,其中包含 NameLengthInBytes ,后面紧跟一个任意长度的宽字符串。我想编写代码,以便它依赖于 _Filename 的字段名称,这样如果 _Filename 的定义发生变化(比如在Name之前添加一个额外的字段)代码仍然会复制到正确的缓冲区位置。

任何人都可以帮助我理解Python和ctypes如何直接操作内存以及如何实现我想要的目标?

谢谢。

1 个答案:

答案 0 :(得分: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())确定所有字节都是连续的块。