您的位置:首页 > 运维架构 > Shell

CMFCShellTreeCtrl控件的效率改进

2015-07-22 14:37 1196 查看
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;
}


[/code]

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文件中,而且要求全局范围内只有一个变量。

CShellManager* afxShellManager = NULL;CShellManager::CShellManager() {	// 实际上要求CShellManager在全局范围内只有一个变量	ENSURE(afxShellManager == NULL);	afxShellManager = this;	...}

所以我们在MFC框架生成的App类的InitInstance函数中能够看到:

CShellManager pShellManager = new CShellManager;

delete pShellManager;

我们只需要将此处局部性质的pShellManager提高到全局范围内,然后在程序中使用它。
我们说使用它,不是说将所有使用afxShellManager变量的地方全部改为pShellManager,而是仅限于我们自己的项目代码,MFC框架的源代码是不能被更改的,而且是不应被更改的

原文链接:http://blog.163.com/lvan100@yeah/blog/static/68117214201111111548469/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: