I_CHILDRENCALLBACK 64位失败

时间:2016-04-26 21:44:21

标签: c++ windows tree mfc

以下适用于32位模式,但不适用于64位,适用于TVN_ITEMEXPANDING鼠标点击&键盘事件。

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()&amp;的按钮。 tree.Expand()调用OnTvnItemexpandingTreectrl(),但是在64位模式下鼠标&amp;键盘不会调用TVN_ITEMEXPANDING,对于其他事件,它可以正常工作。

我怀疑它可能与

的初始化有关
    TVINSERTSTRUCT tvis;
    TVITEM tvItem = { 0 };

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

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

由于

2 个答案:

答案 0 :(得分:0)

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

此处引用文档TVM_EXPAND

  

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

答案 1 :(得分:0)

想想我可能找到了原因:

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

奇怪的是,行为表现得如何。