Windows文件檢索之——接口設計

這一節我們講文件檢索,主要講一講它用到了哪些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類的結構。



對於用戶來說,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;
}

下一節,我們就講一下如何創建檢索條件。





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