Windows文件檢索之——查詢範圍

對於檢索範圍來說,也是比較簡單的,根據第一節,我們知道,ISearcherFolderFactory提供了一個方法SetScope,就是讓用戶傳一個IShellItemArray的指針進去。所以,我們主要的工作就是如何來創建IShellItemArray接口指針。

1.如何創建IShellItemArray

Shell API提供了一個方法,SHCreateShellItemArrayFromIDLists,根據PIDL列表來創建IShellItemArray接口指針,所以,我們的工作就是如何創建出這些PIDL。
我們知道,當用戶去檢索時,在設置檢索範圍時,他最大的可能是設置一個文件夾路徑,如C:\FolderA,也有可能根據一些固定的文件夾,如Windows Librayr,所以,我們設計的類SdkQueryScope至少要提供一種服務,就是把一個或多個路徑或特殊文件夾轉換成PIDL,然後用這些PIDL去創建一個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的設計

對於我們封裝的這個類來說,它本質上就把用戶的一些輸入轉換成一個IShellItemArray指針,以供ISearchFolderItemFactory接口使用,所以我們很容易抽象出它的輸出就是一個IShellItemArray*,那它的輸入是什麼呢?

我們想想,對於用戶的輸入有哪些?

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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章