这一节我们讲文件检索,主要讲一讲它用到了哪些COM接口和Shell相关知识。这里,我们重点讲一讲它的本质,至于如何利用设计模式的东西去包装,用没用线程等,这里就不用讲了。我们需要的是关注本质。
其实对于熟悉Shell的人来说,Search这一部分是比较简单的。我们很幸运,由于之前做的一个项目是仅仅是运行在Windows 7之上的,所以我们这个检索模块可以不用支持Windows 7以下的OS,我们找到一个Shell接口------ISearchFolderItemFactory,我们可以利用这个接口来进行检索,严格来说没有检索,我们只是给它设置一定的Scop和一定的Condition,从而得到检索文件夹对应的PIDL(Shell的东西),然后再根据这个PIDL,来枚举它对应的Shell Item,从而根据这些Item得到其对应的文件路径,只要得到文件路径后,就可以得到我们想要的东西了。
1.ISearchFolderItemFactory接口
ISearchFolderItemFactory : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE SetDisplayName(
LPCWSTR pszDisplayName) = 0;
virtual HRESULT STDMETHODCALLTYPE SetFolderTypeID(
FOLDERTYPEID ftid) = 0;
virtual HRESULT STDMETHODCALLTYPE SetFolderLogicalViewMode(
FOLDERLOGICALVIEWMODE flvm) = 0;
virtual HRESULT STDMETHODCALLTYPE SetIconSize(
int iIconSize) = 0;
virtual HRESULT STDMETHODCALLTYPE SetVisibleColumns(
UINT cVisibleColumns,
__RPC__in_ecount_full(cVisibleColumns) PROPERTYKEY *rgKey) = 0;
virtual HRESULT STDMETHODCALLTYPE SetSortColumns(
UINT cSortColumns,
__RPC__in_ecount_full(cSortColumns) SORTCOLUMN *rgSortColumns) = 0;
virtual HRESULT STDMETHODCALLTYPE SetGroupColumn(
__RPC__in REFPROPERTYKEY keyGroup) = 0;
virtual HRESULT STDMETHODCALLTYPE SetStacks(
UINT cStackKeys,
__RPC__in_ecount_full(cStackKeys) PROPERTYKEY *rgStackKeys) = 0;
virtual HRESULT STDMETHODCALLTYPE SetScope(
__RPC__in_opt IShellItemArray *psiaScope) = 0;
virtual HRESULT STDMETHODCALLTYPE SetCondition(
__RPC__in_opt ICondition *pCondition) = 0;
virtual HRESULT STDMETHODCALLTYPE GetShellItem(
__RPC__in REFIID riid,
__RPC__deref_out_opt void **ppv) = 0;
virtual HRESULT STDMETHODCALLTYPE GetIDList(
__RPC__deref_out_opt PIDLIST_ABSOLUTE *ppidl) = 0;
};
对于我们来说,我们只需用到GetIDList、SetContion、SetScope这三个函数。下面分别对这三个函数进行一点说明。
2. GetIDList
HRESULT GetIDList(
PIDLIST_ABSOLUTE *ppidl
);
这个函数用于得到检索文件夹的ITEMIDLIST(PIDL)。这个函数相当重要。
3.SetCondition
HRESULT SetCondition(
ICondition *pCondition
);
这个函数给当前这个Search Factory设置一个检索条件,参数是一个ICondition接口的指针,这里我们先不关注如何创建这个接口的指针,以后的小节会讲到。如果用户不调用这个方法的话,那么这个检索结果就不会有过滤。
4.SetScope
HRESULT SetScope(
IShellItemArray *psiaScope
);
这个函数用来设置检索的范围。它的参数是一个IShellItemArray的指针,表明这是一个IShellItem的数据。我们可以很轻松的利用Shell APIs来创建这个接口指针。这里也不讲,以后会讲到的。
以上就是我们要用到的三个函数及ISearchFolderItemFactory接口。
5.SdkFileSearcher类
在这个类里面,我把检索条件抽象成了一个类SdkQueryCondition,而检索范围抽象也SdkQueryScope。还依赖一个类,SdkFileSearcherNotify,这个类定义了两个接口,用于通知用户检索完毕或检索到一条数据。
下面这张图说明了我们这SdkFileSearcher类的结构。
下面这张图说明了我们这SdkFileSearcher类的结构。
对于用户来说,Shell和Windows Search的接口已经被我们定义的这一层封装了。
SdkFileSearcher.h
#ifdef __cplusplus
#ifndef _MEDIAFILESEARCH_H_
#define _MEDIAFILESEARCH_H_
#include <process.h>
#include "SdkCommon.h"
#include "SdkQueryCondition.h"
#include "SdkQueryScope.h"
#include "SdkFileSearcherNotify.h"
class CLASS_DECLSPEC SdkFileSearcher
{
public:
SdkFileSearcher();
~SdkFileSearcher();
HRESULT SetCondition(INT32 queryKind);
HRESULT SetCondition(LPCWSTR *ppszQuerys, UINT32 uCount);
HRESULT SetCondition(const SdkQueryCondition *pQueryCondition);
INT32 GetCodition() const;
HRESULT SetScope(INT32 scopeType);
HRESULT SetScope(PCWSTR *pszScopePaths, UINT32 uCount);
HRESULT SetScope(const SdkQueryScope *pQueryScope);
HRESULT Search();
HRESULT SearchAsync();
void CancelSearch();
void StopSearch();
void SetSearchNotify(SdkFileSearcherNotify *pSearcherNotify);
const vector<wstring>& GetSearchResult();
protected:
BOOL HasCancelled();
HRESULT EnumSearchResult(IN LPITEMIDLIST pidl);
HRESULT GetShellItemInfo(IN IShellFolder *psf, IN LPCITEMIDLIST pidl);
static unsigned int WINAPI SearchThreadProc(LPVOID lpParameter);
private:
BOOL m_hasStopped;
BOOL m_hasCancel;
INT_PTR m_nTag;
INT32 m_nQueryConditionKind;
HANDLE m_uThreadHandle;
SdkQueryScope *m_pTempQueryScope;
SdkQueryScope *m_pQueryScope;
SdkQueryCondition *m_pTempQueryCondition;
SdkQueryCondition *m_pQueryCondition;
SdkFileSearcherNotify *m_pSearcherNotify;
ISearchFolderItemFactory *m_pSearchFolderItemFactory;
vector<wstring> m_vctSearchResults;
};
#endif // _MEDIAFILESEARCH_H_
#endif // __cplusplus
SdkFileSearcher.cpp
#include "SdkFileSearcher.h"
#include "SdkCommonHelper.h"
#include "SdkLogger.h"
SdkFileSearcher::SdkFileSearcher() : m_pSearchFolderItemFactory(NULL),
m_pTempQueryScope(NULL),
m_pTempQueryCondition(NULL),
m_pSearcherNotify(NULL),
m_hasCancel(FALSE),
m_hasStopped(FALSE),
m_nTag(0),
m_uThreadHandle(NULL),
m_nQueryConditionKind(QUERY_KIND_NONE),
m_pQueryScope(new SdkQueryScope()),
m_pQueryCondition(new SdkQueryCondition())
{
}
SdkFileSearcher::~SdkFileSearcher()
{
SAFE_RELEASE(m_pSearchFolderItemFactory);
SAFE_DELETE(m_pQueryScope);
SAFE_DELETE(m_pQueryCondition);
}
HRESULT SdkFileSearcher::SetCondition(INT32 queryKind)
{
HRESULT hr = E_FAIL;
m_nQueryConditionKind = queryKind;
if (NULL != m_pQueryCondition)
{
ClearSearchCondition();
hr = m_pQueryCondition->SetCondition(queryKind);
}
return hr;
}
HRESULT SdkFileSearcher::SetCondition(LPCWSTR *ppszQuerys, UINT32 uCount)
{
HRESULT hr = E_FAIL;
if (NULL != m_pQueryCondition)
{
ClearSearchCondition();
hr = m_pQueryCondition->SetCondition(ppszQuerys, uCount);
}
return hr;
}
HRESULT SdkFileSearcher::SetCondition(const SdkQueryCondition *pQueryCondition)
{
ClearSearchCondition();
m_pTempQueryCondition = const_cast<SdkQueryCondition*>(pQueryCondition);
return S_OK;
}
INT32 SdkFileSearcher::GetCodition() const
{
return m_nQueryConditionKind;
}
HRESULT SdkFileSearcher::SetScope(INT32 scopeType)
{
HRESULT hr = E_FAIL;
if (NULL != m_pQueryScope)
{
ClearSearchScope();
hr = m_pQueryScope->SetScope(scopeType);
}
return hr;
}
HRESULT SdkFileSearcher::SetScope(PCWSTR *pszScopePaths, UINT32 uCount)
{
HRESULT hr = E_FAIL;
if (NULL != m_pQueryScope)
{
ClearSearchScope();
hr = m_pQueryScope->SetScope(pszScopePaths, uCount);
}
return hr;
}
HRESULT SdkFileSearcher::SetScope(const SdkQueryScope *pQueryScope)
{
ClearSearchScope();
m_pTempQueryScope = const_cast<SdkQueryScope*>(pQueryScope);
return S_OK;
}
HRESULT SdkFileSearcher::Search()
{
SAFE_RELEASE(m_pSearchFolderItemFactory);
CoCreateInstance(CLSID_SearchFolderItemFactory, NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_pSearchFolderItemFactory));
if (NULL == m_pSearchFolderItemFactory)
{
return E_FAIL;
}
m_hasCancel = FALSE;
m_hasStopped = FALSE;
ClearSearchResult();
HRESULT hr = E_FAIL;
SdkQueryScope *pQueryScope =
(NULL != m_pTempQueryScope) ? m_pTempQueryScope : m_pQueryScope;
SdkQueryCondition *pQueryCondition =
(NULL != m_pTempQueryCondition) ? m_pTempQueryCondition : m_pQueryCondition;
IShellItemArray *psiaScope = NULL;
// +1
hr = pQueryScope->GetScope(&psiaScope);
if (SUCCEEDED(hr))
{
hr = m_pSearchFolderItemFactory->SetScope(psiaScope);
}
ICondition *pCondition = NULL;
if (SUCCEEDED(hr))
{
// +2
hr = pQueryCondition->GetCondition(&pCondition);
if (SUCCEEDED(hr))
{
hr = m_pSearchFolderItemFactory->SetCondition(pCondition);
}
}
if (SUCCEEDED(hr))
{
// +3
PIDLIST_ABSOLUTE pSearchIDL = NULL;
hr = m_pSearchFolderItemFactory->GetIDList(&pSearchIDL);
if (SUCCEEDED(hr))
{
EnumSearchResult(pSearchIDL);
// -3
CoTaskMemFree(pSearchIDL);
}
}
// -2
SAFE_RELEASE(pCondition);
// -1
SAFE_RELEASE(psiaScope);
// If user stops searching, do not call finish searching result callback.
if (!m_hasStopped)
{
// Notify caller that the searching operation is finished.
if (NULL != m_pSearcherNotify)
{
m_pSearcherNotify->OnSearchFinish(this);
}
}
return hr;
}
HRESULT SdkFileSearcher::SearchAsync()
{
// Stop search thread first.
StopSearch();
m_hasCancel = FALSE;
unsigned int dwThreadId = 0;
m_uThreadHandle = (HANDLE)_beginthreadex(NULL, 0,
SdkFileSearcher::SearchThreadProc, (LPVOID)this, 0, &dwThreadId);
return S_OK;
}
void SdkFileSearcher::CancelSearch()
{
m_hasCancel = TRUE;
}
void SdkFileSearcher::StopSearch()
{
m_hasStopped = TRUE;
WaitForSingleObject(m_uThreadHandle, 5000);
}
void SdkFileSearcher::SetSearchNotify(SdkFileSearcherNotify *pSearcherNotify)
{
m_pSearcherNotify = pSearcherNotify;
}
const vector<wstring>& SdkFileSearcher::GetSearchResult()
{
return m_vctSearchResults;
}
//
BOOL SdkFileSearcher::HasCancelled()
{
return (m_hasCancel || m_hasStopped);
}
HRESULT SdkFileSearcher::EnumSearchResult(IN LPITEMIDLIST pidl)
{
IShellFolder *psfDesktop = NULL;
IShellFolder *psfSearch = NULL;
IEnumIDList *penumIDList = NULL;
// +1
HRESULT hr = SHGetDesktopFolder(&psfDesktop);
if (SUCCEEDED(hr) && !HasCancelled())
{
// +2
hr = psfDesktop->BindToObject(pidl,
NULL, IID_IShellFolder, (LPVOID*)&psfSearch);
}
if (SUCCEEDED(hr) && !HasCancelled())
{
// +3
hr = psfSearch->EnumObjects(NULL,
SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penumIDList);
}
if (SUCCEEDED(hr))
{
ULONG celtFetched = 0;
PITEMID_CHILD pChild = NULL;
while ( !HasCancelled() && SUCCEEDED(
penumIDList->Next(1, &pChild, &celtFetched)) && (1 == celtFetched) )
{
GetShellItemInfo(psfSearch, pChild);
CoTaskMemFree(pChild);
}
}
// -3
SAFE_RELEASE(penumIDList);
// -2
SAFE_RELEASE(psfSearch);
// -1
SAFE_RELEASE(psfDesktop);
return hr;
}
HRESULT SdkFileSearcher::GetShellItemInfo(IN IShellFolder *psf,
IN LPCITEMIDLIST pidl)
{
LPITEMIDLIST pRealIDL = NULL;
HRESULT hr = SHGetRealIDL(psf, pidl, &pRealIDL);
if (SUCCEEDED(hr))
{
STRRET strName;
hr = psf->GetDisplayNameOf(pRealIDL, SHGDN_FORPARSING, &strName);
if (SUCCEEDED(hr))
{
WCHAR szName[MAX_PATH] = { 0 };
hr = StrRetToBuf(&strName, pRealIDL, szName, MAX_PATH);
if (SUCCEEDED(hr))
{
// Add to vector to use subsequently.
m_vctSearchResults.push_back(wstring(szName));
// Once find one result, notify caller.
if (NULL != m_pSearcherNotify)
{
m_pSearcherNotify->OnSearchOneResult(this, szName);
}
}
}
CoTaskMemFree(pRealIDL);
}
return hr;
}
unsigned int WINAPI SdkFileSearcher::SearchThreadProc(LPVOID lpParameter)
{
CoInitialize(NULL);
SdkFileSearcher *pThis = static_cast<SdkFileSearcher*>(lpParameter);
if (NULL != pThis)
{
pThis->Search();
pThis->m_uThreadHandle = NULL;
}
CoUninitialize();
return 0;
}
下一节,我们就讲一下如何创建检索条件。