LVITEM for windows 64 bit

2019-02-19 18:13发布

For a long time I tried to use the LVM_GETITEMW message with LVIF_TEXT mask to get the text of a ListView. My program worked in 32 bit but not in 64 bit architecture.

I discovered that the problem was at the LVITEM struct. Shortly, my question is which struct is the appropriate one for 64 bit and why.

The struct I used as the LVITEMW struct had the following fields:

('mask', c_uint32),
('iItem', c_int32),
('iSubItem', c_int32),
('state', c_uint32),
('stateMask', c_uint32),
('pszText', c_uint32),
('cchTextMax', c_int32),
('iImage', c_int32),
('lParam', c_uint64),
('iIndent', c_int32),
('iGroupId', c_int32),
('cColumns', c_uint32),
('puColumns', c_uint32),
('piColFmt', c_int32),
('iGroup', c_int32)

(Written with python 2.7 ctypes, but this is just a form of writing - the language is really irrelevant).

These fields are just as documented.

After a lot of googling, I found this forum which had exactly what I needed - the 64 bit solution!

So in 64 bit the struct should have more "spaces", and should look something like this (the pointers are now 64 bit and also the stateMask is 64 bit. That's a little bit different from what the forum suggested but works too):

('mask', c_uint32),
('iItem', c_int32),
('iSubItem', c_int32),
('state', c_uint32),
('stateMask', c_uint64), <-- Now 64 bit
('pszText', c_uint64), <-- Now 64 bit which makes sense since this is a pointer
('cchTextMax', c_int32),
('iImage', c_int32),
('lParam', c_uint64),
('iIndent', c_int32),
('iGroupId', c_int32),
('cColumns', c_uint32),
('puColumns', c_uint64), <-- Now 64 bit which makes sense since this is a pointer
('piColFmt', c_int64), <-- Now 64 bit which makes sense since this is a pointer
('iGroup', c_int32)

The forum suggested having:

('mask', c_uint32),
('iItem', c_int32),
('iSubItem', c_int32),
('state', c_uint32),
('stateMask', c_uint64),
('pszText', c_uint64),
('cchTextMax', c_int32),
('iImage', c_int64), <-- Now 64 bit
('lParam', c_uint32),
('iIndent', c_int32),
('iGroupId', c_int32),
('cColumns', c_uint32),
('puColumns', c_uint32),
('piColFmt', c_int32),
('iGroup', c_int64), <-- Now 128 bit all together
('iGroup2', c_int64) <-- continuation

Which also works, at list for my need which is the text pointed by pszText.

And my questions are:

  1. Is this documented anywhere?
  2. Why should the stateMask be c_uint64 - shouldn't it always be the same size as the state?
  3. Which one is the true struct for 64 bit?

Thank you!

1条回答
看我几分像从前
2楼-- · 2019-02-19 19:08

Thanks to the comment by Raymond Chen I was able to figure out the answer!

It's about data alignment. Every pointer should be 8 bytes and should also be aligned to an address that can be divided by 8, so sometimes there should be a padding before the pointer. Also, the size of the struct should be dividable by min(max(sizeof(each field in the struct)), 8) which is 8 in our case since the size of a pointer is 8.

class LVITEMW_explicit(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('mask', c_uint32),         # 0
        ('iItem', c_int32),         # 4
        ('iSubItem', c_int32),      # 8
        ('state', c_uint32),        # 12
        ('stateMask', c_uint32),    # 16
        ('padding1', c_int),
        ('pszText', c_uint64),      # 20 --> 24 after padding (A pointer)
        ('cchTextMax', c_int32),    # 32
        ('iImage', c_int32),        # 36
        ('lParam', c_uint64),       # 40 (On 32 bit should be c_long which is 32 bits)
        ('iIndent', c_int32),       # 48
        ('iGroupId', c_int32),      # 52
        ('cColumns', c_uint32),     # 56
        ('padding2', c_int),
        ('puColumns', c_uint64),    # 60 --> 64 after padding (A pointer)
        ('piColFmt', c_int64),      # 72 (A pointer)
        ('iGroup', c_int32),        # 80
        ('padding3', c_int32),      # The total length was 84 before padding3 was added, which is not dividable by 8
    ]

Or as this should really be written - without the _pack_ = 1:

class LVITEMW(ctypes.Structure):
    _fields_ = [
        ('mask', c_uint32),
        ('iItem', c_int32),
        ('iSubItem', c_int32),
        ('state', c_uint32),
        ('stateMask', c_uint32),
        ('pszText', c_uint64),
        ('cchTextMax', c_int32),
        ('iImage', c_int32),
        ('lParam', c_uint64), # On 32 bit should be c_long
        ('iIndent', c_int32),
        ('iGroupId', c_int32),
        ('cColumns', c_uint32),
        ('puColumns', c_uint64),
        ('piColFmt', c_int64),
        ('iGroup', c_int32),
    ]

And indeed ctypes.sizeof(LVITEMW) returns 88, same as ctypes.sizeof(LVITEMW_explicit).

Thanks again for the helpful comments!

查看更多
登录 后发表回答