【问题标题】:Confused using ctype pointers with memmove in Python在 Python 中使用 ctype 指针和 memmove 感到困惑
【发布时间】:2016-07-13 21:42:10
【问题描述】:

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

我很困惑。我希望 preqcontents 的地址与 req 相同。至少我认为 cast 会这样做。

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

谁能帮助我了解 Python 和 ctypes 如何直接操作内存以及如何实现我想要的目标?

谢谢。

【问题讨论】:

    标签: python-2.7 ctypes


    【解决方案1】:

    好的,我想出了我自己的问题。

    首先我发现了一个提示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'
    

    请注意,要解压缩 Name 字段,您必须反转您在包中所做的操作。这可以放入一个 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())确定所有字节都是一个连续的块。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-25
      相关资源
      最近更新 更多