VS2010新增加(相较于VC6)了一个CMFCShellTreeCtrl类,说实话,这个类确实很好,但是有一点你会发现,在展开某些节点的时候可能会很慢很慢。这严重影响了效率。为什么呢?很长一段时间,一直百思不得其解!甚至抓狂!原来问题出现在一个很小的函数上。
HRESULT CMFCShellTreeCtrl::EnumObjects(HTREEITEM hParentItem, LPSHELLFOLDER pParentFolder, LPITEMIDLIST pidlParent) { ASSERT_VALID(this); ASSERT_VALID(afxShellManager); LPENUMIDLIST pEnum = NULL; HRESULT hr = pParentFolder->EnumObjects(NULL, m_dwFlags, &pEnum); if (FAILED(hr) || pEnum == NULL) { return hr; } LPITEMIDLIST pidlTemp; DWORD dwFetched = 1; // Enumerate the item's PIDLs: while (SUCCEEDED(pEnum->Next(1, &pidlTemp, &dwFetched)) && dwFetched) { TVITEM tvItem; ZeroMemory(&tvItem, sizeof(tvItem)); // Fill in the TV_ITEM structure for this item: tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN; // AddRef the parent folder so it's pointer stays valid: pParentFolder->AddRef(); // Put the private information in the lParam: LPAFX_SHELLITEMINFO pItem = (LPAFX_SHELLITEMINFO)GlobalAlloc(GPTR, sizeof(AFX_SHELLITEMINFO)); ENSURE(pItem != NULL); pItem->pidlRel = pidlTemp; pItem->pidlFQ = afxShellManager->ConcatenateItem(pidlParent, pidlTemp); pItem->pParentFolder = pParentFolder; tvItem.lParam = (LPARAM)pItem; CString strItem = OnGetItemText(pItem); tvItem.pszText = strItem.GetBuffer(strItem.GetLength()); tvItem.iImage = OnGetItemIcon(pItem, FALSE); tvItem.iSelectedImage = OnGetItemIcon(pItem, TRUE); //问题出现在这里,接下来要检查文件的属性,判断是否有子文件夹。正常情况下我们其实是不需要这么多属性的,前两个足够了SFGAO_HASSUBFOLDER 和 SFGAO_FOLDER 。将后面的其他属性全部屏蔽,再运行程序时,就会发现很快了。 // Determine if the item has children: /*DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_DISPLAYATTRMASK | SFGAO_CANRENAME | SFGAO_FILESYSANCESTOR;*/ DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER ; pParentFolder->GetAttributesOf(1, (LPCITEMIDLIST*) &pidlTemp, &dwAttribs); tvItem.cChildren = (dwAttribs & (SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR)); // Determine if the item is shared: if (dwAttribs & SFGAO_SHARE) { tvItem.mask |= TVIF_STATE; tvItem.stateMask |= TVIS_OVERLAYMASK; tvItem.state |= INDEXTOOVERLAYMASK(1); //1 is the index for the shared overlay image } // Fill in the TV_INSERTSTRUCT structure for this item: TVINSERTSTRUCT tvInsert; tvInsert.item = tvItem; tvInsert.hInsertAfter = TVI_LAST; tvInsert.hParent = hParentItem; InsertItem(&tvInsert); dwFetched = 0; } pEnum->Release(); return S_OK; }
2014/10/16 :今天有网友提问如何在MFC程序中使用CMFCShellTreeCtrl,其实很简单。首先新建一个基于 CMFCShellTreeCtrl的MFC类,然后将新建的类和控件绑定。 CMFCShellTreeCtrl有几个比较重要的虚函数:
/* 获取Item的文字*/
virtual CString OnGetItemText(LPAFX_SHELLITEMINFO pItem);
/* 获取Item的图标 */
virtual int OnGetItemIcon(LPAFX_SHELLITEMINFO pItem, BOOL bSelected);
/* 枚举目录下所有的文件 */
virtual HRESULT EnumObjects(HTREEITEM hParentItem, LPSHELLFOLDER pParentFolder, LPITEMIDLIST pidlParent);
其中第3个函数就是上文提到的可以修改的函数。但是有一点需要注意的是,MFC框架内使用了一个全局变量:
extern CShellManager* afxShellManager;
这个变量并没有被MFC框架导出,所以在我们自己的源码中是不能引用这个变量的。解决这个问题也很简单。
afxShellManager被定义在afxshellmanager.cpp文件中,而且要求全局范围内只有一个变量。
所以我们在MFC框架生成的App类的InitInstance函数中能够看到:CShellManager* afxShellManager = NULL;CShellManager::CShellManager() {// 实际上要求 CShellManager在全局范围内只有一个变量ENSURE(afxShellManager == NULL);afxShellManager = this;...}
CShellManager pShellManager = new CShellManager;
delete pShellManager;我们只需要将此处局部性质的pShellManager提高到全局范围内,然后在程序中使用它。
我们说使用它,不是说将所有使用
afxShellManager变量的地方全部改为pShellManager,而是仅限于我们自己的项目代码,
MFC框架的源代码是不能被更改的,而且是不应被更改的。
原文链接:http://blog.163.com/lvan100@yeah/blog/static/68117214201111111548469/