I_CHILDRENCALLBACK 64bit failing

I_CHILDRENCALLBACK 64bit failing

本文关键字:failing 64bit CHILDRENCALLBACK      更新时间:2023-10-16

以下内容适用于32位模式,但不适用于64位模式,用于TVN_ITEMEXPANDING鼠标单击和键盘事件。

对象.h

afx_msg void OnTvnItemexpandingTreectrl(NMHDR *pNMHDR, LRESULT *pResult);

对象.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 };

但真的不知道我在找什么。

如果我需要发布更多代码,请告诉我。

谢谢

解决方案仅仅是消息的设计吗?

此处引用了文档TVM_EXPAND

当项目首次由TVM_EXPAND消息展开时,该操作将生成TVN_ITEMEXPANDING并TVN_ITEMEXPANDED通知代码,并设置项目的TVIS_EXPANDEDONCE状态标志。只要此状态标志保持设置状态,后续TVM_EXPAND消息就不会生成TVN_ITEMEXPANDING或TVN_ITEMEXPANDED通知。若要重置TVIS_EXPANDEDONCE状态标志,必须发送设置了TVE_COLLAPSE和TVE_COLLAPSERESET标志的TVM_EXPAND消息。尝试显式设置TVIS_EXPANDEDONCE将导致不可预知的行为。

我想我可能已经找到了原因:

    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;
        }
    }
}

奇怪的是行为如何表现自己。