对于检索范围来说,也是比较简单的,根据第一节,我们知道,ISearcherFolderFactory提供了一个方法SetScope,就是让用户传一个IShellItemArray的指针进去。所以,我们主要的工作就是如何来创建IShellItemArray接口指针。
1.如何创建IShellItemArray
Shell API SHGetKnownFolderIDList、SHGetIDListFromObject都可以得到一个PIDL。
1、SHGetKnownFolderIDList:根据Known folder id创建一个PIDL,比如:FOLDERID_MusicLibrary、FOLDERID_DocumentsLibrary等,这些定义可以从MSDN上面找到。
2、SHGetIDListFromObject:就是根据一个IShellItem创建一个PIDL。而这些IShellItem从哪里来?可以用SHCreateItemFromParsingName API来创建,根据路径解析,生成一个IShellItem接口指针。
上面所说的都是怎么创建PIDL,对于熟悉Shell的人来说,这些都很简单。
2.关于SdkQueryScope的设计
我们想想,对于用户的输入有哪些?
1,文件夹路径,可能提供多个,也就是一个WCHAR*的数组。
2,特殊文件夹的ID,如FOLDERID_MusicLibrary、FOLDERID_DocumentsLibrary等。但我们用这些ID可能很麻烦,所以需要我们定义一种对于用户很友好的枚举,以供用户使用。
目前来说这两种对于检索来说是够用了。 下面这张图说明了用户的输入与SdkQueryScope的输出。

注意,对于枚举类型,用户可能把几种枚举类型联合起来,所以,对于SdkQueryScope来说,它只能接收一个INT32值,这个值可能是一些定义枚举的联合。
还有,由于我们定义了一种友好的枚举来表示一个known folder id,所以,在这个类里面就应当有一个对应的转换关系,我在程序内部分把我们所支持的known folder id与所定义的枚举一一映射起来,存在一个map中。
SdkQueryScope.h
#ifdef __cplusplus
#ifndef _QUERYSCOPE_H_
#define _QUERYSCOPE_H_
#include "SdkCommon.h"
typedef enum _QUERYSCOPETYPE
{
QUERY_SCOPE_NONE = 0x00000000,
QUERY_SCOPE_PICTURES = 0x00000001,
QUERY_SCOPE_MUSIC = 0x00000002,
QUERY_SCOPE_VIDEO = 0x00000004,
QUERY_SCOPE_DECUMENT = 0x00000008,
QUERY_SCOPE_PUBLIC = 0x00000010,
QUERY_SCOPE_SAMPLEMUSIC = 0x00000020,
QUERY_SCOPE_DESKTOP = 0x00000040,
QUERY_SCOPE_STARTMENU = 0x00000080,
QUERY_SCOPE_FAVORITES = 0x00000100,
QUERY_SCOPE_COMPUTER = 0x00000200,
QUERY_SCOPE_EXTERNALSTORAGE = 0x00000400,
} QUERYSCOPETYPE;
/*!
* @brief QueryScope class.
*/
class CLASS_DECLSPEC SdkQueryScope
{
public:
SdkQueryScope();
~SdkQueryScope();
HRESULT SetScope(INT32 scopeType);
HRESULT SetScope(PCWSTR *pszScopePaths, UINT32 uCount);
HRESULT GetScope(OUT IShellItemArray **ppsia);
private:
HRESULT GetScopeType(OUT vector<QUERYSCOPETYPE> &vctScopeTypes);
static void InitializeMap();
private:
typedef map <QUERYSCOPETYPE, KNOWNFOLDERID> TYPETOFOLDERIDMAP;
typedef pair<QUERYSCOPETYPE, KNOWNFOLDERID> TYPETOFOLDERIDPAIR;
INT32 m_nScopeType;
UINT32 m_uScopePathsCount;
PCWSTR *m_pszScopePaths;
static TYPETOFOLDERIDMAP s_mapTypeToFolderID;
};
#endif // _QUERYSCOPE_H_
#endif // __cplusplus
QUERYSCOPETYPE 就个枚举就是定义的一系列特殊文件夹的值。
static void InitializeMap();这个静态方法就是用于初始化knonw folder id到enum之间的映射关系。
HRESULT GetScopeType(OUT vector<QUERYSCOPETYPE> &vctScopeTypes); 就是根据当前的文件夹类型的值(INT32)转换成相应的枚举值,并存放到一个vector中。
SdkQueryScope.cpp
#include "SdkQueryScope.h"
#define MAKE_PAIR(key, value) TYPETOFOLDERIDPAIR(key, value)
#define MAKE_STRUCT(key, value) { key, value }
SdkQueryScope::TYPETOFOLDERIDMAP SdkQueryScope::s_mapTypeToFolderID;
void SdkQueryScope::InitializeMap()
{
static BOOL isFirst = TRUE;
if (!isFirst)
{
return;
}
isFirst = FALSE;
struct
{
QUERYSCOPETYPE type;
KNOWNFOLDERID folderId;
}
static const g_szKeyValuePairs[] =
{
MAKE_STRUCT(QUERY_SCOPE_PICTURES, FOLDERID_PicturesLibrary),
MAKE_STRUCT(QUERY_SCOPE_MUSIC, FOLDERID_MusicLibrary),
MAKE_STRUCT(QUERY_SCOPE_VIDEO, FOLDERID_VideosLibrary),
MAKE_STRUCT(QUERY_SCOPE_DECUMENT, FOLDERID_DocumentsLibrary),
MAKE_STRUCT(QUERY_SCOPE_FAVORITES, FOLDERID_Favorites),
MAKE_STRUCT(QUERY_SCOPE_COMPUTER, FOLDERID_ComputerFolder),
MAKE_STRUCT(QUERY_SCOPE_PUBLIC, FOLDERID_Public),
MAKE_STRUCT(QUERY_SCOPE_DESKTOP, FOLDERID_Desktop),
};
s_mapTypeToFolderID.clear();
int size = ARRAYSIZE(g_szKeyValuePairs);
for (int i = 0; i < size; ++i)
{
s_mapTypeToFolderID.insert(MAKE_PAIR(g_szKeyValuePairs[i].type,
g_szKeyValuePairs[i].folderId));
}
}
SdkQueryScope::SdkQueryScope() : m_nScopeType(0),
m_uScopePathsCount(0),
m_pszScopePaths(NULL)
{
InitializeMap();
}
SdkQueryScope::~SdkQueryScope()
{
if (NULL != m_pszScopePaths)
{
for (UINT32 i = 0; i < m_uScopePathsCount; ++i)
{
SAFE_DELETE_ARRAY(m_pszScopePaths[i]);
}
SAFE_DELETE_ARRAY(m_pszScopePaths);
}
m_uScopePathsCount = 0;
}
HRESULT SdkQueryScope::SetScope(INT32 scopeType)
{
m_nScopeType = scopeType;
return S_OK;
}
HRESULT SdkQueryScope::SetScope(PCWSTR *pszScopePaths, UINT32 uCount)
{
if ( (NULL == pszScopePaths) || (0 == uCount) )
{
return E_INVALIDARG;
}
ClearScope();
m_uScopePathsCount = uCount;
m_pszScopePaths = new PCWSTR[uCount];
memset(m_pszScopePaths, 0, uCount * sizeof(PCWSTR));
for (UINT32 i = 0; i < uCount; ++i)
{
int length = wcslen(pszScopePaths[i]);
int ccbDest = sizeof(WCHAR) * (length + 1);
int ccbSrc = sizeof(WCHAR) * length;
WCHAR *pDest = new WCHAR[length + 1];
memset(pDest, 0, ccbDest);
memcpy_s(pDest, ccbDest, pszScopePaths[i], ccbSrc);
m_pszScopePaths[i] = pDest;
}
return S_OK;
}
HRESULT SdkQueryScope::GetScope(OUT IShellItemArray **ppsia)
{
if (NULL == ppsia)
{
return E_INVALIDARG;
}
vector<PIDLIST_ABSOLUTE> vctIDLists;
vector<QUERYSCOPETYPE> vctScopeTypes;
HRESULT hr = GetScopeType(vctScopeTypes);
for each (QUERYSCOPETYPE scope in vctScopeTypes)
{
map<QUERYSCOPETYPE, KNOWNFOLDERID>::const_iterator itor =
s_mapTypeToFolderID.find(scope);
if (itor == s_mapTypeToFolderID.end())
{
continue;
}
PIDLIST_ABSOLUTE pItemID = NULL;
hr = SHGetKnownFolderIDList(itor->second, KF_FLAG_DEFAULT, NULL, &pItemID);
if (SUCCEEDED(hr))
{
vctIDLists.push_back(pItemID);
}
}
for (UINT32 i = 0; i < m_uScopePathsCount; ++i)
{
IShellItem2 *pShellItem2 = NULL;
hr = SHCreateItemFromParsingName(m_pszScopePaths[i],
NULL, IID_PPV_ARGS(&pShellItem2));
if (SUCCEEDED(hr))
{
PIDLIST_ABSOLUTE pItemID = NULL;
hr = SHGetIDListFromObject(pShellItem2, &pItemID);
if (SUCCEEDED(hr))
{
vctIDLists.push_back(pItemID);
}
}
SAFE_RELEASE(pShellItem2);
}
int idlSize = vctIDLists.size();
if (idlSize > 0)
{
PIDLIST_ABSOLUTE *rgItemIDs = new PIDLIST_ABSOLUTE[idlSize];
for (int i = 0; i < idlSize; ++i)
{
rgItemIDs[i] = vctIDLists[i];
}
IShellItemArray *psia = NULL;
hr = SHCreateShellItemArrayFromIDLists(idlSize,
(LPCITEMIDLIST*)rgItemIDs, &psia);
if (SUCCEEDED(hr))
{
hr = psia->QueryInterface(IID_PPV_ARGS(ppsia));
}
SAFE_RELEASE(psia);
for (int i = 0; i < idlSize; ++i)
{
CoTaskMemFree(rgItemIDs[i]);
}
SAFE_DELETE_ARRAY(rgItemIDs);
}
return hr;
}
HRESULT SdkQueryScope::GetScopeType(OUT vector<QUERYSCOPETYPE> &vctScopeTypes)
{
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_PICTURES, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_MUSIC, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_VIDEO, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_DECUMENT, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_PUBLIC, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_SAMPLEMUSIC, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_DESKTOP, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_STARTMENU, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_FAVORITES, vctScopeTypes);
FILTER_BITVALUE(m_nScopeType, QUERY_SCOPE_COMPUTER, vctScopeTypes);
return S_OK;
}
上面最关键的就是函数GetScope类了,我就不解释代码了。
关于GetScopeType的实现,我用了一个宏FILTER_BITVALUE,它的定义如下:
#ifndef FILTER_BITVALUE
#define FILTER_BITVALUE(val, bit, vct) \
{ \
if (val & bit) \
{ \
vct.push_back(bit); \
} \
}
#endif // FILTER_BITVALUE