CListCtrl::SetItem, LVIF_GROUPID, and I_GROUPIDNONE
This was originally posted in another forum but no one could answer it. Now I know why.
http://social.msdn.microsoft.com/Forums/en-US/windowsuidevelopment/thread/367c4a11-f55d-4ed2-86b6-334133ee4ce2?lc=1033A ListView (CListCtrl) has some groups. Each item might be in one of the existing groups or in I_GROUPIDNONE.
Sometimes I call CListCtrl::SetItem to change the groupid of an item. If the new groupid is one of the existing groups then it works. If the new groupid is I_GROUPIDNONE then it worked until 2009-10-13 but stopped working on 2009-10-14. Now instead of getting a new groupid of I_GROUPIDNONE, it gets a new groupid that is unpredictable, not completely random, but not useable. I cannot find any way today to set it to I_GROUPIDNONE.
Microsoft had around 30 Windows updates today. Which one killed groupids?
More importantly, is there some way to get I_GROUPIDNONE working again?
Windows XP SP3 with all security updates and most optional updates, and Visual Studio 2005 SP1 with all security updates. (Internet Explorer is still 6 with all its security updates because IE 7 and 8 crash too often in Japanese Windows XP.)
Addendum: I did not do a Windows Update in a Windows 7 machine, and confirmed that I_GROUPIDNONE is still working there.
Addendum 2: In both Windows XP (with Windows Updates of 2009-10-14) and in Windows 7 (without any updates), there is more odd behaviour.
When I call CListCtrl::SetItem with the LVIF_GROUPID bit on, if the groupid is positive then the ListView control sends an LVN_ITEMCHANGED notification to my dialog box, but if the groupid is I_GROUPIDNONE then the control does not send an LVN_ITEMCHANGED notification. This is consistent in both release builds and debug builds.
In release builds sometimes the ListView displays correct contents even though it didn't send an LVN_ITEMCHANGED notification. I forgot to check if it really stored the I_GROUPIDNONE value or not. If the groupid is positive it really stores.
In debug builds also sometimes it displays correct contents but less often. In a debug build under Windows XP with the latest updates I think it's just by accident that sometimes the ListView displays correct contents. I did check again that it stored garbage instead of the I_GROUPIDNONE value. If the groupid is positive then it really stores. In a debug build under Windows 7 without any updates, I forgot to check if it might store the I_GROUPIDNONE value.
Does anyone know how to make this work?
Addendum 3: Variations in misbehaviour might also depend on whether the processor is single-core or dual-core.
Does anyone know how to make this work?
HERE IS THE REAL PROBLEM.
CListCtrl::SetItem "probably" worked because it's a very thin wrapper around a Windows API.
CListCtrl::SetCheck accidentally usually works in a Release build because, when the MFC library forgets to initialize an automatic variable, the memory accidentally usually starts out at zeroes. CListCtrl::SetCheck tramps all over the Win32 ListView control when running in a Debug build because, when the MFC library forgets to initialize an automatic variable, the C runtime library helpfully initializes MFC's variable to some non-zero values. Then CListCtrl::SetCheck calls a Windows API with garbage undefined values in most of its argument.
CListCtrl::SetItemState does the same. Or different. Same bug but maybe different details in the results.
Same with other functions in winctrl6.cpp and winctrl2.cpp.
Maybe recent Windows Updates had an effect just by coincidence because maybe the Win32 ListView control changed its undefined behaviour when MFC gives it garbage. I wonder why the number of CPU cores seemed to matter, but maybe that was just a set of accidental coincidences.
Does anyone know how to teach Microsoft's MFC developers how to call ZeroMemory?
All Replies
- Hello
Thanks for the report. I hear your frustration. I will do my best to assit you.
I'm attempting to reproduce the problem first. If you could share with me a simple sample that can reproduce the problem, it'll be very helpful. My email address is jialge at microsoft.com.
Are you using this prototype to set the group id?
BOOL CListCtrl::SetItem(const LVITEM* pItem)
I just checked the source code of this function in afxcmn.inl. As you said, it's a very simple wrapper of Windows API.
_AFXCMN_INLINE BOOL CListCtrl::SetItem(const LVITEM* pItem)
{ ASSERT(::IsWindow(m_hWnd)); return (BOOL) ::SendMessage(m_hWnd, LVM_SETITEM, 0, (LPARAM)pItem); }
Am I right that the symptom you see on your side is that, after the SendMessage call, the group id in LVITEM is not successfully set on the item, even though the function CListCtrl::SetItem return TRUE?
Regards,
Jialiang Ge
MSDN Subscriber Support in Forum
If you have any feedback of our support, please contact msdnmg@microsoft.com.
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us. Mr. Ge,
My program calls CListCtrl::SetItem, CListCtrl::SetCheck, and CListCtrl::SetItemState in sequence. For two weeks it seemed that CListCtrl::SetItem was failing in this way: if iGroupId is positive then it works, but if iGroupId is I_GROUPIDNONE then the group id gets set to an incorrect value which might be zero or might be some 32-bit value which is not completely random but I don't know where it comes from.
Yesterday I noticed that on some PCs it's possible to use the F11 key in Visual Studio to step into CListCtrl::SetCheck or CListCtrl::SetItemState. I don't know why F11 steps into them on some PCs but steps over them on other PCs. Those functions are defined in files winctrl6.cpp and winctrl2.cpp. That is where I saw MFC code that defines automatic variables named lvi and doesn't initialize them. So yesterday, when reposting to this forum, I thought this was MFC's deficiency. Maybe SetItem correctly set the group id but subsequent invalid Win32 calls caused random overwrites (well not completely random).
Later I noticed that MSDN says MFC doesn't have to initialize most of the fields in its variables named lvi. Well, the MSDN Liebrary already contains a lot of fictions about groups in ListView controls, so who knows what Win32 is really doing, and who knows if the MFC code is valid or not.
Also since the control sends me an LVN_ITEMCHANGED notification when the group id is positive but not when the group id is I_GROUPIDNONE, maybe the defect is really in Win32 in the first place.This will take more experimenting.
- I inserted a call to GetItem immediately after the call to SetItem. GetItem gets a structure in which iGroupId is positive instead of I_GROUPIDNONE. So when SetItem pretended to set iGroupId to I_GROUPIDNONE, it lied.
At this point it looks like the problem is in Win32 after all instead of MFC. But it might not be. MFC already called Win32 with uninitialized members in an lvi struct, so the ListCtrl might already be in an irrepairable state and it might be MFC's fault.
This will still take more experimenting. - My latest experiments have reliably reproduced this misbehaviour in both Debug and Release builds in Windows XP, while working correctly in Windows 7, even with recent Microsoft Updates applied in Windows 7. So it really no longer looks like an MFC bug, it looks like a bug in the Win32 ListView control itself in Windows XP.
When SetItem pretended to set iGroupId to I_GROUPIDNONE, it lied.
When SetItem obeyed a call to set iGroupId to a positive value, it also sent an LVN_ITEMCHANGED notification, which seems redundant in terms of programming, but useful in double checking the behaviour of this bug.
Tentatively I have stopped using 0 as a group id, and behaviour seems to be less bad. I wonder how long that will last. Now InsertGroup inserts at other than the specified index ... well actually MSDN is pretty messed up in describing indexes and items for InsertGroup but for LVM_INSERTGROUP at least it says that the index is a group index. Anyway behaviour has been less bad for the past hour when group id 0 is avoided. - On the PC where the VC++ 2005 debugger cannot step into functions CListCtrl::SetItem, CListCtrl::SetCheck, and CListCtrl::SetItemState (where it steps over them instead of into them), Skype is installed, though not running.
I vaguely recall reading that Skype refuses to install if WinDbg is installed. Does anyone know if Skype interferes with VC++ 2005's user mode debugger too?
However, Skype is not the main problem. Other PCs where Windows XP's ListView control misbehaves do not have Skype installed. - Thanks for you efforts!
Is it convenient for you to share with me the experimental sample that can reliably reproduce the prblem? It will be very useful for me to delve into the problem. My email address is jialge@microsoft.com.
Regards,
Jialiang Ge
MSDN Subscriber Support in Forum
If you have any feedback of our support, please contact msdnmg@microsoft.com.
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.


