這一節我們講文件檢索,主要講一講它用到了哪些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;
};
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來創建這個接口指針。這裏也不講,以後會講到的。5.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;
}
下一節,我們就講一下如何創建檢索條件。