2/05/2012

Integrating Windows Desktop Search in Visual C++


This is a revised version of an earlier blog at http://blog.joycode.com/jiangsheng/archives/2005/07/19/59725.joy,  published July 19, 2005. The source code is available here:
.


The beta version of Windows Desktop Search (WDS) is released for a while. The SDK with a sample in C# is available for download at http://addins.msn.com/devguide.aspx. And using it in Visual C applications is quite easy, too.



First, you need to import ADO, since WDS uses ADO to return search results

#import "c:\Program Files\Common Files\system\ado\Msado15.dll" rename("EOF","adoEOF") rename("DataTypeEnum","adoDataTypeEnum")
using namespace ADODB;

Then import the wdsQuery.tlb file, which can be found in the SDK
#import "wdsQuery.tlb" rename("EOF","adoEOF") rename("DataTypeEnum","adoDataTypeEnum")
using namespace WDSQuery;

You need a CLSID to create the search engine object. The CLSID of the WDS object is not too hard to find in the wdsQuery.idl, which is also part of the WDS SDK.

DEFINE_GUID(CLSID_SearchDesktop,0x1AD68C99,0x00FB,0x416d,0x80,0x4B,0xC3,0x8D,0xEE,0x75,0xD5,0x5E);

Now you can create the object like this

ISearchDesktopPtr m_pSearchDesktop;

if(m_pSearchDesktop.CreateInstance(CLSID_SearchDesktop))
{
    AfxMessageBox(IDS_FAILED_TO_CREATE_SEARCH_ENGINE);
    return FALSE;
}

The object has two search methods that return an ADO recordset. The ExecuteSQLQuery method is the friendly one, takes a string and return the search result. The ExecuteSQLQuery method, on the contrary, accept a SQL statement. Believe me, you won't eager to construct the SQL by yourself. The parameter list of the ExecuteSQLQuery method is long enough. Here are the columns I choose:

DocTitle,DocFormat,Url,DocAuthor,PrimaryDate,FileName, FileExt,IsAttachment,Characterization,Rank,PerceivedType, HasAttach,DocTitlePrefix,FileExtDesc,DisplayFolder, DocKeywords,DocComments,ConversationID,Size, Create,Write
void CWDSSampleView::Search(LPCTSTR lpszQuery,LPCTSTR lpszSort)
{
 CString strQuery(lpszQuery);if(strQuery.IsEmpty())return;
 CString strSort(lpszSort);
 USES_CONVERSION;
 HRESULT hr=S_OK;
 GetListCtrl().SetItemCount(0);
 ClearCache();
 try{
  CString strColumns;
  VERIFY(strColumns.LoadString(IDS_COLUMNS_GENERAL));
  if(strSort.IsEmpty())
   m_pRecordset=m_pSearchDesktop->ExecuteQuery(T2OLE(strQuery),
    T2OLE(strColumns),NULL,NULL);
  else
   m_pRecordset=m_pSearchDesktop->ExecuteQuery(T2OLE(strQuery),
    T2OLE(strColumns),T2OLE(strSort),NULL);
  int nItemCount=m_pRecordset->GetRecordCount();
  GetListCtrl().SetItemCount(nItemCount);
  
 }
 catch(_com_error&e)
 {
  ……
 }
}

See, to increase performance, I used a cache and a virtual list control. But the application is still a little slow, if the search result list is too large. A better way to return the result set is to reduce date transfer. The search should only return the primary key column and the sorting columns, and when an item is about to be displayed, search again to get the information of this item.

No comments: