考虑以下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如何直接操作内存以及如何实现我想要的目标?
谢谢。
答案 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())确定所有字节都是连续的块。