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;
}

下一节,我们就讲一下如何创建检索条件。





  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值