I_CHILDRENCALLBACK 64 位失败
I_CHILDRENCALLBACK 64bit failing
对于 TVN_ITEMEXPANDING 鼠标点击和键盘事件,以下内容适用于 32 位模式,但不适用于 64 位模式。
Object.h
afx_msg void OnTvnItemexpandingTreectrl(NMHDR *pNMHDR, LRESULT *pResult);
Object.cpp
BEGIN_MESSAGE_MAP(Object, CDialogEx)
ON_NOTIFY(TVN_ITEMEXPANDING, IDC_TREECTRL, OnTvnItemexpandingTreectrl)
END_MESSAGE_MAP()
void Object::LoadTree()
{
m_TreeCtrl1.DeleteAllItems();
HTREEITEM hParentItem = TVI_ROOT;
std::list<OBJ>::iterator itObj = m_Obj.begin()->m_Obj.begin();
for (size_t i = 0; i < m_Obj.begin()->m_Obj.size(); i++, ++itObj)
{
TVINSERTSTRUCT tvis;
TVITEM tvItem = { 0 };
tvItem.mask = LVIF_TEXT | LVIF_PARAM | TVIF_CHILDREN | TVIF_HANDLE | TVIF_STATE;
tvItem.cChildren = I_CHILDRENCALLBACK;
tvItem.pszText = itObj->m_TreeDesc.GetBuffer();
tvItem.cchTextMax = MAX_ITEMLEN;
tvItem.lParam = reinterpret_cast<LPARAM>(&*itObj);
tvis.item = tvItem;
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
hParentItem = m_TreeCtrl1.InsertItem(&tvis);
RecurseBuildTree(itObj->m_Obj, m_TreeCtrl1, hParentItem, TVI_LAST);
}
hParentItem = m_TreeCtrl1.GetFirstVisibleItem();
for (size_t i = 0; i < m_Obj.begin()->m_Obj.size(); i++)
{
HTREEITEM hNext = hParentItem;
OBJ *pObjNext = reinterpret_cast<OBJ*>(m_TreeCtrl1.GetItemData(hNext));
m_TreeCtrl1.SetCheck(hNext, pObjNext->m_bItemDisplayed);
RecurseTreeSetCheck(m_TreeCtrl1, hParentItem);
hParentItem = m_TreeCtrl1.GetNextItem(hNext, TVGN_NEXT);
}
}
void Object::OnTvnItemexpandingTreectrl(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
*pResult = 0;
}
void ExpandTreeItem(CTreeCtrl &tree, HTREEITEM hItem, UINT nCode)
{
HTREEITEM hChild;
if (tree.ItemHasChildren(hItem))
{
tree.Expand(hItem, nCode);
hChild = tree.GetChildItem(hItem);
while (hChild)
{
ExpandTreeItem(tree, hChild, nCode);
hChild = tree.GetNextItem(hChild, TVGN_NEXT);
}
}
}
void Object::ToggleItemState(HTREEITEM hti, CTreeCtrl &treectrl, const HTREEITEM hParentNode)
{
if (hti == hParentNode)
{
const int iImage = treectrl.GetItemState(hParentNode, TVIS_STATEIMAGEMASK) >> 12;
OBJ *pObj = reinterpret_cast<OBJ*>(treectrl.GetItemData(hti));
pObj->m_bItemDisplayed = (iImage == 1 ? 2 : 1);
}
else
{
const int iImage = treectrl.GetItemState(hParentNode, TVIS_STATEIMAGEMASK) >> 12;
treectrl.SetItemState(hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1), TVIS_STATEIMAGEMASK);
OBJ *pObj = reinterpret_cast<OBJ*>(treectrl.GetItemData(hti));
pObj->m_bItemDisplayed = (iImage == 1 ? 2 : 1);
}
if (treectrl.ItemHasChildren(hti)) //failing in release mode for root level node.
{
HTREEITEM htiChild = treectrl.GetChildItem(hti);
if (htiChild)
ToggleItemState(htiChild, treectrl, hParentNode);
else
return;
HTREEITEM htiSibling = treectrl.GetNextSiblingItem(htiChild);
while (htiSibling)
{
ToggleItemState(htiSibling, treectrl, hParentNode);
htiSibling = treectrl.GetNextSiblingItem(htiSibling);
}
}
}
观察:
以下在 64 位发布模式下失败但仅适用于根节点:
if (treectrl.ItemHasChildren(hti))
我有按钮可以手动调用 ExpandTreeItem() 和 tree.Expand() 调用 OnTvnItemexpandingTreectrl(),但在 64 位模式下,鼠标和键盘不会调用 TVN_ITEMEXPANDING,对于它起作用的其他事件美好的。
我怀疑这可能与
的初始化有关
TVINSERTSTRUCT tvis;
TVITEM tvItem = { 0 };
但我真的不知道我在找什么。
如果我需要 post 更多代码,请告诉我。
谢谢
解决方案仅仅是消息的设计吗?
此处引用文档TVM_EXPAND
When an item is first expanded by a TVM_EXPAND message, the action generates TVN_ITEMEXPANDING and TVN_ITEMEXPANDED notification codes and the item's TVIS_EXPANDEDONCE state flag is set. As long as this state flag remains set, subsequent TVM_EXPAND messages do not generate TVN_ITEMEXPANDING or TVN_ITEMEXPANDED notifications. To reset the TVIS_EXPANDEDONCE state flag, you must send a TVM_EXPAND message with the TVE_COLLAPSE and TVE_COLLAPSERESET flags set. Attempting to explicitly set TVIS_EXPANDEDONCE will result in unpredictable behavior.
我想我可能已经找到原因了:
tvItem.cChildren = I_CHILDRENCALLBACK; //<<failing in 64bit mode.
//Upto 0xFFFFFFFE works ok but not -1?
可以使用以下最少的代码来重现该问题:
//header
struct ItemData
{
CString Name;
int Value;
CString ToString() const
{
CString str;
str.Format(_T("%s = %d"), Name, Value);
return str;
}
};
CTreeCtrl m_tree;
std::vector<ItemData*> m_data;
//source
void CTreeSortDemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TREE, m_tree);
}
void CTreeSortDemoDlg::GenerateTreeContent()
{
ItemData* data1 = new ItemData;
data1->Value = 3;
data1->Name.Format(_T("%c%c"), 'P', data1->Value);
m_data.push_back(data1);
m_data.push_back(data1);
ItemData* data2 = new ItemData;
data2->Value = 3;
data2->Name.Format(_T("%c%c"), 'P', data2->Value);
m_data.push_back(data2);
m_data.push_back(data2);
HTREEITEM hParentItem = TVI_ROOT;
TVINSERTSTRUCT tvis;
TVITEM tvItem = { 0 };
tvItem.mask = LVIF_TEXT | LVIF_PARAM | TVIF_CHILDREN | TVIF_HANDLE | TVIF_STATE;
tvItem.cChildren = I_CHILDRENCALLBACK; //<<failing in 64bit mode. 1 works ok?
tvItem.pszText = L"Parent";
tvItem.cchTextMax = MAX_PATH;
tvItem.lParam = reinterpret_cast<LPARAM>(&m_data);
tvis.item = tvItem;
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
hParentItem = m_tree.InsertItem(&tvis);
//////
TVINSERTSTRUCT tvis2;
TVITEM tvItem2 = { 0 };
std::vector<ItemData*>::iterator itData = m_data.begin();
tvItem2.mask = LVIF_TEXT | LVIF_PARAM | TVIF_HANDLE | TVIF_STATE;
tvItem2.cChildren = 0;
tvItem2.pszText = L"child";
tvItem2.cchTextMax = MAX_PATH;
tvItem2.lParam = reinterpret_cast<LPARAM>(&itData);
tvis2.item = tvItem2;
tvis2.hParent = hParentItem;
tvis2.hInsertAfter = TVI_LAST;
HTREEITEM hItemThis = m_tree.InsertItem(&tvis2);
}
编辑:
如果要在树生成中使用I_CHILDRENCALLBACK,则还必须使用TVN_GETDISPINFO,即
ON_NOTIFY(TVN_GETDISPINFO, IDC_TREE, OnGetdispinfoTreectrl)
void CTreeSortDemoDlg::OnGetdispinfoTreectrl(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
TVITEM* pItem = &(pTVDispInfo)->item;
ItemData *pData = reinterpret_cast<ItemData*>(pItem->lParam);
if (pItem->mask & TVIF_CHILDREN)
{
if (pData != NULL)
{
pItem->cChildren = I_CHILDRENCALLBACK;
}
}
}
奇怪的是行为如何表现出来。
对于 TVN_ITEMEXPANDING 鼠标点击和键盘事件,以下内容适用于 32 位模式,但不适用于 64 位模式。
Object.h
afx_msg void OnTvnItemexpandingTreectrl(NMHDR *pNMHDR, LRESULT *pResult);
Object.cpp
BEGIN_MESSAGE_MAP(Object, CDialogEx)
ON_NOTIFY(TVN_ITEMEXPANDING, IDC_TREECTRL, OnTvnItemexpandingTreectrl)
END_MESSAGE_MAP()
void Object::LoadTree()
{
m_TreeCtrl1.DeleteAllItems();
HTREEITEM hParentItem = TVI_ROOT;
std::list<OBJ>::iterator itObj = m_Obj.begin()->m_Obj.begin();
for (size_t i = 0; i < m_Obj.begin()->m_Obj.size(); i++, ++itObj)
{
TVINSERTSTRUCT tvis;
TVITEM tvItem = { 0 };
tvItem.mask = LVIF_TEXT | LVIF_PARAM | TVIF_CHILDREN | TVIF_HANDLE | TVIF_STATE;
tvItem.cChildren = I_CHILDRENCALLBACK;
tvItem.pszText = itObj->m_TreeDesc.GetBuffer();
tvItem.cchTextMax = MAX_ITEMLEN;
tvItem.lParam = reinterpret_cast<LPARAM>(&*itObj);
tvis.item = tvItem;
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
hParentItem = m_TreeCtrl1.InsertItem(&tvis);
RecurseBuildTree(itObj->m_Obj, m_TreeCtrl1, hParentItem, TVI_LAST);
}
hParentItem = m_TreeCtrl1.GetFirstVisibleItem();
for (size_t i = 0; i < m_Obj.begin()->m_Obj.size(); i++)
{
HTREEITEM hNext = hParentItem;
OBJ *pObjNext = reinterpret_cast<OBJ*>(m_TreeCtrl1.GetItemData(hNext));
m_TreeCtrl1.SetCheck(hNext, pObjNext->m_bItemDisplayed);
RecurseTreeSetCheck(m_TreeCtrl1, hParentItem);
hParentItem = m_TreeCtrl1.GetNextItem(hNext, TVGN_NEXT);
}
}
void Object::OnTvnItemexpandingTreectrl(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
*pResult = 0;
}
void ExpandTreeItem(CTreeCtrl &tree, HTREEITEM hItem, UINT nCode)
{
HTREEITEM hChild;
if (tree.ItemHasChildren(hItem))
{
tree.Expand(hItem, nCode);
hChild = tree.GetChildItem(hItem);
while (hChild)
{
ExpandTreeItem(tree, hChild, nCode);
hChild = tree.GetNextItem(hChild, TVGN_NEXT);
}
}
}
void Object::ToggleItemState(HTREEITEM hti, CTreeCtrl &treectrl, const HTREEITEM hParentNode)
{
if (hti == hParentNode)
{
const int iImage = treectrl.GetItemState(hParentNode, TVIS_STATEIMAGEMASK) >> 12;
OBJ *pObj = reinterpret_cast<OBJ*>(treectrl.GetItemData(hti));
pObj->m_bItemDisplayed = (iImage == 1 ? 2 : 1);
}
else
{
const int iImage = treectrl.GetItemState(hParentNode, TVIS_STATEIMAGEMASK) >> 12;
treectrl.SetItemState(hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1), TVIS_STATEIMAGEMASK);
OBJ *pObj = reinterpret_cast<OBJ*>(treectrl.GetItemData(hti));
pObj->m_bItemDisplayed = (iImage == 1 ? 2 : 1);
}
if (treectrl.ItemHasChildren(hti)) //failing in release mode for root level node.
{
HTREEITEM htiChild = treectrl.GetChildItem(hti);
if (htiChild)
ToggleItemState(htiChild, treectrl, hParentNode);
else
return;
HTREEITEM htiSibling = treectrl.GetNextSiblingItem(htiChild);
while (htiSibling)
{
ToggleItemState(htiSibling, treectrl, hParentNode);
htiSibling = treectrl.GetNextSiblingItem(htiSibling);
}
}
}
观察:
以下在 64 位发布模式下失败但仅适用于根节点:
if (treectrl.ItemHasChildren(hti))
我有按钮可以手动调用 ExpandTreeItem() 和 tree.Expand() 调用 OnTvnItemexpandingTreectrl(),但在 64 位模式下,鼠标和键盘不会调用 TVN_ITEMEXPANDING,对于它起作用的其他事件美好的。
我怀疑这可能与
的初始化有关 TVINSERTSTRUCT tvis;
TVITEM tvItem = { 0 };
但我真的不知道我在找什么。
如果我需要 post 更多代码,请告诉我。
谢谢
解决方案仅仅是消息的设计吗?
此处引用文档TVM_EXPAND
When an item is first expanded by a TVM_EXPAND message, the action generates TVN_ITEMEXPANDING and TVN_ITEMEXPANDED notification codes and the item's TVIS_EXPANDEDONCE state flag is set. As long as this state flag remains set, subsequent TVM_EXPAND messages do not generate TVN_ITEMEXPANDING or TVN_ITEMEXPANDED notifications. To reset the TVIS_EXPANDEDONCE state flag, you must send a TVM_EXPAND message with the TVE_COLLAPSE and TVE_COLLAPSERESET flags set. Attempting to explicitly set TVIS_EXPANDEDONCE will result in unpredictable behavior.
我想我可能已经找到原因了:
tvItem.cChildren = I_CHILDRENCALLBACK; //<<failing in 64bit mode.
//Upto 0xFFFFFFFE works ok but not -1?
可以使用以下最少的代码来重现该问题:
//header
struct ItemData
{
CString Name;
int Value;
CString ToString() const
{
CString str;
str.Format(_T("%s = %d"), Name, Value);
return str;
}
};
CTreeCtrl m_tree;
std::vector<ItemData*> m_data;
//source
void CTreeSortDemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TREE, m_tree);
}
void CTreeSortDemoDlg::GenerateTreeContent()
{
ItemData* data1 = new ItemData;
data1->Value = 3;
data1->Name.Format(_T("%c%c"), 'P', data1->Value);
m_data.push_back(data1);
m_data.push_back(data1);
ItemData* data2 = new ItemData;
data2->Value = 3;
data2->Name.Format(_T("%c%c"), 'P', data2->Value);
m_data.push_back(data2);
m_data.push_back(data2);
HTREEITEM hParentItem = TVI_ROOT;
TVINSERTSTRUCT tvis;
TVITEM tvItem = { 0 };
tvItem.mask = LVIF_TEXT | LVIF_PARAM | TVIF_CHILDREN | TVIF_HANDLE | TVIF_STATE;
tvItem.cChildren = I_CHILDRENCALLBACK; //<<failing in 64bit mode. 1 works ok?
tvItem.pszText = L"Parent";
tvItem.cchTextMax = MAX_PATH;
tvItem.lParam = reinterpret_cast<LPARAM>(&m_data);
tvis.item = tvItem;
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
hParentItem = m_tree.InsertItem(&tvis);
//////
TVINSERTSTRUCT tvis2;
TVITEM tvItem2 = { 0 };
std::vector<ItemData*>::iterator itData = m_data.begin();
tvItem2.mask = LVIF_TEXT | LVIF_PARAM | TVIF_HANDLE | TVIF_STATE;
tvItem2.cChildren = 0;
tvItem2.pszText = L"child";
tvItem2.cchTextMax = MAX_PATH;
tvItem2.lParam = reinterpret_cast<LPARAM>(&itData);
tvis2.item = tvItem2;
tvis2.hParent = hParentItem;
tvis2.hInsertAfter = TVI_LAST;
HTREEITEM hItemThis = m_tree.InsertItem(&tvis2);
}
编辑:
如果要在树生成中使用I_CHILDRENCALLBACK,则还必须使用TVN_GETDISPINFO,即
ON_NOTIFY(TVN_GETDISPINFO, IDC_TREE, OnGetdispinfoTreectrl)
void CTreeSortDemoDlg::OnGetdispinfoTreectrl(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
TVITEM* pItem = &(pTVDispInfo)->item;
ItemData *pData = reinterpret_cast<ItemData*>(pItem->lParam);
if (pItem->mask & TVIF_CHILDREN)
{
if (pData != NULL)
{
pItem->cChildren = I_CHILDRENCALLBACK;
}
}
}
奇怪的是行为如何表现出来。