windows-nt/Source/XPSP1/NT/inetsrv/query/sdk/advquery/advquery.cxx
2020-09-26 16:20:57 +08:00

1155 lines
42 KiB
C++

//+-------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation, 1997 - 1999. All Rights Reserved.
//
// PROGRAM: advquery.cxx
//
// PURPOSE: Illustrates an advanced query using Indexing Service.
// Uses the OLE DB Provider for Indexing Service, not
// Indexing Service helper functions.
//
// PLATFORM: Windows 2000
//
//--------------------------------------------------------------------------
#ifndef UNICODE
#define UNICODE
#endif
#include <stdio.h>
#include <windows.h>
#define OLEDBVER 0x0250 // need the command tree definitions
#define DBINITCONSTANTS
#include <oledberr.h>
#include <oledb.h>
#include <cmdtree.h>
#include <olectl.h>
#include <ntquery.h>
// This is found in disptree.cxx
extern void DisplayCommandTree( DBCOMMANDTREE * pNode, ULONG iLevel = 0 );
//+-------------------------------------------------------------------------
//
// Template: XInterface
//
// Synopsis: Template for managing ownership of interfaces
//
//--------------------------------------------------------------------------
template<class T> class XInterface
{
public:
XInterface( T * p = 0 ) : _p( p ) {}
~XInterface() { if ( 0 != _p ) _p->Release(); }
T * operator->() { return _p; }
T * GetPointer() const { return _p; }
IUnknown ** GetIUPointer() { return (IUnknown **) &_p; }
T ** GetPPointer() { return &_p; }
void ** GetQIPointer() { return (void **) &_p; }
T * Acquire() { T * p = _p; _p = 0; return p; }
private:
T * _p;
};
//+-------------------------------------------------------------------------
//
// Function: CreateICommand
//
// Synopsis: Creates an ICommand.
//
// Arguments: [ppICommand] - Where the ICommand is returned on success
//
// Returns: HRESULT result
//
//--------------------------------------------------------------------------
HRESULT CreateICommand( ICommand ** ppICommand )
{
// Instantiate the data source object
XInterface<IDBInitialize> xIDBInit;
static const GUID guidIndexingServiceDSO = CLSID_INDEX_SERVER_DSO;
HRESULT hr = CoCreateInstance( guidIndexingServiceDSO,
0,
CLSCTX_INPROC_SERVER,
IID_IDBInitialize,
xIDBInit.GetQIPointer() );
if ( FAILED(hr) )
return hr;
// Initialize, verifying that we supplied the right variables
hr = xIDBInit->Initialize();
if ( FAILED(hr) )
return hr;
// Get a session object
XInterface<IDBCreateSession> xIDBSess;
hr = xIDBInit->QueryInterface( IID_IDBCreateSession,
xIDBSess.GetQIPointer() );
if ( FAILED(hr) )
return hr;
// Get a Create Command object
XInterface<IDBCreateCommand> xICreateCommand;
hr = xIDBSess->CreateSession( 0,
IID_IDBCreateCommand,
xICreateCommand.GetIUPointer() );
if ( FAILED(hr) )
return hr;
// Create the ICommand
XInterface<ICommand> xICommand;
hr = xICreateCommand->CreateCommand( 0,
IID_ICommand,
xICommand.GetIUPointer() );
if ( FAILED(hr) )
return hr;
*ppICommand = xICommand.Acquire();
return hr;
} //CreateICommand
//+-------------------------------------------------------------------------
//
// Function: SetCommandProperties
//
// Synopsis: Sets the DBPROP_USEEXTENDEDDBTYPES property to TRUE, so
// data is returned in PROPVARIANTs, as opposed to the
// default, which is OLE automation VARIANTs. PROPVARIANTS
// allow a superset of VARIANT data types. Use of these
// types avoids costly coercions.
//
// Also sets the DBPROP_USECONTENTINDEX property to TRUE, so
// the index will always be used to resolve the query (as
// opposed to enumerating all the files on the disk), even
// if the index is out of date.
//
// Also sets the asynchronous property, so the Execute() call
// returns before the query is complete.
//
// Both of these properties are unique to the OLE DB Provider
// for Indexing Service implementation.
//
// Arguments: [pICommand] - The ICommand used to set the property
//
// Returns: HRESULT result of setting the properties
//
//--------------------------------------------------------------------------
HRESULT SetCommandProperties( ICommand * pICommand )
{
static const DBID dbcolNull = { { 0,0,0, { 0,0,0,0,0,0,0,0 } },
DBKIND_GUID_PROPID, 0 };
static const GUID guidQueryExt = DBPROPSET_QUERYEXT;
static const GUID guidRowsetProps = DBPROPSET_ROWSET;
DBPROP aProp[3];
aProp[0].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
aProp[0].dwOptions = DBPROPOPTIONS_OPTIONAL;
aProp[0].dwStatus = 0;
aProp[0].colid = dbcolNull;
aProp[0].vValue.vt = VT_BOOL;
aProp[0].vValue.boolVal = VARIANT_TRUE;
aProp[1] = aProp[0];
aProp[1].dwPropertyID = DBPROP_USECONTENTINDEX;
aProp[2] = aProp[0];
aProp[2].dwOptions = DBPROPOPTIONS_REQUIRED;
aProp[2].dwPropertyID = DBPROP_IDBAsynchStatus;
DBPROPSET aPropSet[2];
aPropSet[0].rgProperties = &aProp[0];
aPropSet[0].cProperties = 2;
aPropSet[0].guidPropertySet = guidQueryExt;
aPropSet[1].rgProperties = &aProp[2];
aPropSet[1].cProperties = 1;
aPropSet[1].guidPropertySet = guidRowsetProps;
XInterface<ICommandProperties> xICommandProperties;
HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
xICommandProperties.GetQIPointer() );
if ( FAILED( hr ) )
return hr;
return xICommandProperties->SetProperties( 2, // 2 property sets
aPropSet ); // the properties
} //SetCommandProperties
//+-------------------------------------------------------------------------
//
// Function: SetScopeCatalogAndMachine
//
// Synopsis: Sets the catalog and machine properties in the ICommand.
// Also sets a default scope.
//
// Arguments: [pICommand] - ICommand to set props on
// [pwcQueryScope] - Scope for the query
// [pwcQueryCatalog] - Catalog name over which query is run
// [pwcQueryMachine] - Machine name on which query is run
//
// Returns: HRESULT result of the operation
//
//--------------------------------------------------------------------------
HRESULT SetScopeCatalogAndMachine(
ICommand * pICommand,
WCHAR const * pwcQueryScope,
WCHAR const * pwcQueryCatalog,
WCHAR const * pwcQueryMachine )
{
// Get an ICommandProperties so we can set the properties
XInterface<ICommandProperties> xICommandProperties;
HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
xICommandProperties.GetQIPointer() );
if ( FAILED( hr ) )
return hr;
// note: SysAllocString, SafeArrayCreate, and SafeArrayPutElement can
// fail, but this isn't checked here for brevity.
SAFEARRAYBOUND rgBound[1];
rgBound[0].lLbound = 0;
rgBound[0].cElements = 1;
SAFEARRAY * pMachines = SafeArrayCreate( VT_BSTR, 1, rgBound );
long i = 0;
SafeArrayPutElement( pMachines, &i, SysAllocString( pwcQueryMachine ) );
SAFEARRAY * pCatalogs = SafeArrayCreate( VT_BSTR, 1, rgBound );
SafeArrayPutElement( pCatalogs, &i, SysAllocString( pwcQueryCatalog ) );
SAFEARRAY * pScopes = SafeArrayCreate( VT_BSTR, 1, rgBound );
SafeArrayPutElement( pScopes, &i, SysAllocString( pwcQueryScope ) );
LONG lFlags = QUERY_DEEP;
SAFEARRAY * pFlags = SafeArrayCreate( VT_I4, 1, rgBound );
SafeArrayPutElement( pFlags, &i, &lFlags );
DBPROP aScopeProperties[2];
memset( aScopeProperties, 0, sizeof aScopeProperties );
aScopeProperties[0].dwPropertyID = DBPROP_CI_INCLUDE_SCOPES;
aScopeProperties[0].vValue.vt = VT_BSTR | VT_ARRAY;
aScopeProperties[0].vValue.parray = pScopes;
aScopeProperties[0].colid.eKind = DBKIND_GUID_PROPID;
aScopeProperties[1].dwPropertyID = DBPROP_CI_SCOPE_FLAGS;
aScopeProperties[1].vValue.vt = VT_I4 | VT_ARRAY;
aScopeProperties[1].vValue.parray = pFlags;
aScopeProperties[1].colid.eKind = DBKIND_GUID_PROPID;
DBPROP aCatalogProperties[1];
memset( aCatalogProperties, 0, sizeof aCatalogProperties );
aCatalogProperties[0].dwPropertyID = DBPROP_CI_CATALOG_NAME;
aCatalogProperties[0].vValue.vt = VT_BSTR | VT_ARRAY;
aCatalogProperties[0].vValue.parray = pCatalogs;
aCatalogProperties[0].colid.eKind = DBKIND_GUID_PROPID;
DBPROP aMachineProperties[1];
memset( aMachineProperties, 0, sizeof aMachineProperties );
aMachineProperties[0].dwPropertyID = DBPROP_MACHINE;
aMachineProperties[0].vValue.vt = VT_BSTR | VT_ARRAY;
aMachineProperties[0].vValue.parray = pMachines;
aMachineProperties[0].colid.eKind = DBKIND_GUID_PROPID;
const GUID guidFSCI = DBPROPSET_FSCIFRMWRK_EXT;
DBPROPSET aAllPropsets[3];
aAllPropsets[0].rgProperties = aScopeProperties;
aAllPropsets[0].cProperties = 2;
aAllPropsets[0].guidPropertySet = guidFSCI;
aAllPropsets[1].rgProperties = aCatalogProperties;
aAllPropsets[1].cProperties = 1;
aAllPropsets[1].guidPropertySet = guidFSCI;
const GUID guidCI = DBPROPSET_CIFRMWRKCORE_EXT;
aAllPropsets[2].rgProperties = aMachineProperties;
aAllPropsets[2].cProperties = 1;
aAllPropsets[2].guidPropertySet = guidCI;
const ULONG cPropertySets = sizeof aAllPropsets / sizeof aAllPropsets[0];
hr = xICommandProperties->SetProperties( cPropertySets, // # of propsets
aAllPropsets ); // the propsets
SafeArrayDestroy( pScopes );
SafeArrayDestroy( pFlags );
SafeArrayDestroy( pCatalogs );
SafeArrayDestroy( pMachines );
return hr;
} //SetScopeCatalogAndMachine
//+-------------------------------------------------------------------------
//
// Function: AllocAndCopy
//
// Synopsis: Allocates and duplicates a string.
//
// Arguments: [pwcIn] - The string to copy
//
// Returns: A string
//
//--------------------------------------------------------------------------
WCHAR * AllocAndCopy( WCHAR const * pwcIn )
{
ULONG cwc = wcslen( pwcIn ) + 1;
// note: CoTaskMemAlloc can return 0 if out of memory, not checked
WCHAR * pwc = (WCHAR *) CoTaskMemAlloc( cwc * sizeof WCHAR );
wcscpy( pwc, pwcIn );
return pwc;
} //AllocAndCopy
//+-------------------------------------------------------------------------
//
// Function: NewTreeNode
//
// Synopsis: Allocates and initializes a DBCOMMANDTREE object
//
// Arguments: [op] - The node's operator
// [wKind] - The kind of node
//
// Returns: an initialized DBCOMMANDTREE object
//
//--------------------------------------------------------------------------
DBCOMMANDTREE * NewTreeNode(
DBCOMMANDOP op,
WORD wKind )
{
DBCOMMANDTREE * pTree = (DBCOMMANDTREE *)
CoTaskMemAlloc( sizeof DBCOMMANDTREE );
memset( pTree, 0, sizeof DBCOMMANDTREE );
pTree->op = op;
pTree->wKind = wKind;
return pTree;
} //NewTreeNode
//+-------------------------------------------------------------------------
//
// Function: CreateQueryTree
//
// Synopsis: Creates a DBCOMMANDTREE for the query
//
// Arguments: [pwcQueryRestrition] - The actual query string
// [ppTree] - Resulting query tree
//
// Returns: HRESULT result of the operation
//
// Notes: The query tree has a string restriction, a list of
// columns to return (rank, size, and path), and a sort
// order (rank).
// Here are two views of the query tree
//
// sort:
// child: project
// sibling: sort_list_anchor
// child: sort_list_element
// value: SORTINFO (descending, lcid)
// child: column_name
// value: DBID rank
// child: select
// sibling: project_list_anchor
// child: project_list_element
// child: column_name
// value: DBID rank
// sibling: project_list_element
// child: column_name
// value: DBID size
// sibling: project_list_element
// child: column_name
// value: DBID path
// child: table_name
// value: WSTR: "Table"
// sibling: content
// value: DBCONTENT (restriction, weight, lcid)
// child: column_name
// value: DBID contents
//
// +---------------------------+
// | DBOP_sort |
// | DBVALUEKIND_EMPTY |
// +---------------------------+
// |
// |child
// | sibling
// +---------------------------+-------+---------------------------+
// | DBOP_project | | DBOP_sort_list_anchor |
// | DBVALUEKIND_EMPTY | | DBVALUEKIND_EMPTY |
// +---------------------------+ +---------------------------+
// | |
// |child |child
// | |
// | +---------------------------+
// | | DBOP_sort_list_element |
// | | DBVALUEKIND_SORTINFO |
// | +---------------------------+
// | | |
// | |child | pdbsrtinfValue
// | | |
// | | +-------------+
// | | | DBSORTIFO |
// | | | fDesc TRUE |
// | | | lcid system |
// | | +-------------+
// | |
// | |
// | |
// | +---------------------------+
// | | DBOP_column_name |
// | | DBVALUEKIND_ID |
// | +---------------------------+
// | |
// | | pdbidValue
// | |
// | +------+
// | | DBID |
// | | rank |
// | +------+
// |
// | sibling
// +---------------------------+-------+---------------------------+-------+
// | DBOP_select | | DBOP_project_list_anchor | | s
// | DBVALUEKIND_EMPTY | | DBVALUEKIND_EMPTY | | i
// +---------------------------+ +---------------------------+ | b
// | | | l
// |child |child | i
// | | | n
// | +---------------------------+ | g
// | | DBOP_project_list_element | |
// | | DBVALUEKIND_EMPTY | |
// | +---------------------------+ |
// | | |
// | |child |
// | | |
// | +---------------------------+ |
// | | DBOP_column_name | |
// | | DBVALUEKIND_ID | |
// | +---------------------------+ |
// | | |
// | | pdbidValue |
// | | |
// | +------+ |
// | | DBID | |
// | | rank | |
// | +------+ |
// | |
// | +-----------------------------------+
// | |
// | +---------------------------+-------+
// | | DBOP_project_list_element | | s
// | | DBVALUEKIND_EMPTY | | i
// | +---------------------------+ | b
// | | | l
// | |child | i
// | | | n
// | +---------------------------+ | g
// | | DBOP_column_name | |
// | | DBVALUEKIND_ID | |
// | +---------------------------+ |
// | | |
// | | pdbidValue |
// | | |
// | +------+ |
// | | DBID | |
// | | size | |
// | +------+ |
// | |
// | +-----------------------------------+
// | |
// | +---------------------------+
// | | DBOP_project_list_element |
// | | DBVALUEKIND_EMPTY |
// | +---------------------------+
// | |
// | |child
// | |
// | +---------------------------+
// | | DBOP_column_name |
// | | DBVALUEKIND_ID |
// | +---------------------------+
// | |
// | | pdbidValue
// | |
// | +------+
// | | DBID |
// | | path |
// | +------+
// |
// | sibling
// +---------------------------+-------+---------------------------+
// | DBOP_table_name | | DBOP_content |
// | DBVALUEKIND_WSTR: 'Table' | | DBVALUEKIND_CONTENT |
// +---------------------------+ +---------------------------+
// | |
// |child | pdbcntntValue
// | |
// | +---------------------------+
// | | DBCONTENT |
// | | dwGenerateMethod: GENERATE_METHOD_EXACT |
// | | lWeight: 1000 |
// | | lcid: system |
// | | pwszPhrase: the query |
// | +---------------------------+
// |
// |
// |
// +---------------------------+
// | DBOP_column_name |
// | DBVALUEKIND_ID |
// +---------------------------+
// |
// | pdbidValue
// |
// +----------+
// | DBID |
// | contents |
// +----------+
//
//--------------------------------------------------------------------------
HRESULT CreateQueryTree(
WCHAR const * pwcQueryRestriction,
DBCOMMANDTREE ** ppTree )
{
// These are the properties that'll be referenced below
const DBID dbidContents = { PSGUID_STORAGE, DBKIND_GUID_PROPID,
(LPOLESTR) PID_STG_CONTENTS };
const DBID dbidPath = { PSGUID_STORAGE, DBKIND_GUID_PROPID,
(LPOLESTR) PID_STG_PATH };
const DBID dbidSize = { PSGUID_STORAGE, DBKIND_GUID_PROPID,
(LPOLESTR) PID_STG_SIZE };
DBID dbidRank;
dbidRank.uGuid.guid = PSGUID_QUERY;
dbidRank.eKind = DBKIND_GUID_PROPID;
dbidRank.uName.ulPropid = PROPID_QUERY_RANK ;
// The restriction is a content node with either a word or a phrase.
// This is the most simple possible query. Other types of nodes include
// AND, OR, NOT, etc.
// The CITextToFullTree function is available for building more complex
// queries given a text string.
DBCOMMANDTREE *pRestriction = NewTreeNode( DBOP_content,
DBVALUEKIND_CONTENT );
DBCONTENT * pDBContent = (DBCONTENT *) CoTaskMemAlloc( sizeof DBCONTENT );
memset( pDBContent, 0, sizeof DBCONTENT );
pRestriction->value.pdbcntntValue = pDBContent;
pDBContent->dwGenerateMethod = GENERATE_METHOD_EXACT;
pDBContent->lWeight = 1000; // maximum possible weight
pDBContent->lcid = GetSystemDefaultLCID();
pDBContent->pwszPhrase = AllocAndCopy( pwcQueryRestriction );
// This identifies "file contents" as the property for the restrition
DBCOMMANDTREE *pPropID = NewTreeNode( DBOP_column_name, DBVALUEKIND_ID );
pRestriction->pctFirstChild = pPropID;
DBID *pDBID = (DBID *) CoTaskMemAlloc( sizeof DBID );
*pDBID = dbidContents;
pPropID->value.pdbidValue = pDBID;
DBCOMMANDTREE *pSelect = NewTreeNode( DBOP_select, DBVALUEKIND_EMPTY );
DBCOMMANDTREE *pTableId = NewTreeNode( DBOP_table_name, DBVALUEKIND_WSTR );
pSelect->pctFirstChild = pTableId;
pTableId->value.pwszValue = AllocAndCopy( L"Table" );
pTableId->pctNextSibling = pRestriction;
DBCOMMANDTREE *pProject = NewTreeNode( DBOP_project, DBVALUEKIND_EMPTY );
pProject->pctFirstChild = pSelect;
// The project anchor holds the list of columns that are retrieved
DBCOMMANDTREE * pProjectAnchor = NewTreeNode( DBOP_project_list_anchor,
DBVALUEKIND_EMPTY );
pSelect->pctNextSibling = pProjectAnchor;
// Retrieve rank as column 1
DBCOMMANDTREE * pProjectRank = NewTreeNode( DBOP_project_list_element,
DBVALUEKIND_EMPTY );
pProjectAnchor->pctFirstChild = pProjectRank;
DBCOMMANDTREE * pColumnRank = NewTreeNode( DBOP_column_name,
DBVALUEKIND_ID );
pProjectRank->pctFirstChild = pColumnRank;
DBID *pDBIDRank = (DBID *) CoTaskMemAlloc( sizeof DBID );
pColumnRank->value.pdbidValue = pDBIDRank;
*pDBIDRank = dbidRank;
// Retrieve file size as column 2
DBCOMMANDTREE * pProjectSize = NewTreeNode( DBOP_project_list_element,
DBVALUEKIND_EMPTY );
pProjectRank->pctNextSibling = pProjectSize;
DBCOMMANDTREE * pColumnSize = NewTreeNode( DBOP_column_name,
DBVALUEKIND_ID );
pProjectSize->pctFirstChild = pColumnSize;
DBID *pDBIDSize = (DBID *) CoTaskMemAlloc( sizeof DBID );
pColumnSize->value.pdbidValue = pDBIDSize;
*pDBIDSize = dbidSize;
// Retrieve file path as column 3
DBCOMMANDTREE * pProjectPath = NewTreeNode( DBOP_project_list_element,
DBVALUEKIND_EMPTY );
pProjectSize->pctNextSibling = pProjectPath;
DBCOMMANDTREE * pColumnPath = NewTreeNode( DBOP_column_name,
DBVALUEKIND_ID );
pProjectPath->pctFirstChild = pColumnPath;
DBID *pDBIDPath = (DBID *) CoTaskMemAlloc( sizeof DBID );
pColumnPath->value.pdbidValue = pDBIDPath;
*pDBIDPath = dbidPath;
// The sort node specifies the sort order for the results
DBCOMMANDTREE * pSort = NewTreeNode( DBOP_sort, DBVALUEKIND_EMPTY );
pSort->pctFirstChild = pProject;
// The sort anchor is the start of the list of sort properties
DBCOMMANDTREE * pSortAnchor = NewTreeNode( DBOP_sort_list_anchor,
DBVALUEKIND_EMPTY );
pProject->pctNextSibling = pSortAnchor;
// The sort order is rank
DBCOMMANDTREE * pSortRank = NewTreeNode( DBOP_sort_list_element,
DBVALUEKIND_SORTINFO );
pSortAnchor->pctFirstChild = pSortRank;
DBSORTINFO * pSortInfo = (DBSORTINFO *) CoTaskMemAlloc( sizeof DBSORTINFO );
memset( pSortInfo, 0, sizeof DBSORTINFO );
pSortRank->value.pdbsrtinfValue = pSortInfo;
pSortInfo->fDesc = TRUE; // descending, not ascending
pSortInfo->lcid = GetSystemDefaultLCID();
DBCOMMANDTREE * pSortColumnRank = NewTreeNode( DBOP_column_name,
DBVALUEKIND_ID );
pSortRank->pctFirstChild = pSortColumnRank;
DBID *pDBIDSortRank = (DBID *) CoTaskMemAlloc( sizeof DBID );
pSortColumnRank->value.pdbidValue = pDBIDSortRank;
*pDBIDSortRank = dbidRank;
// The sort node is the head of the tree
*ppTree = pSort;
return S_OK;
} //CreateQueryTree
//+---------------------------------------------------------------------------
//
// Class: CAsynchNotify
//
// Synopsis: Class for the IDBAsynchNotify callbacks
//
//----------------------------------------------------------------------------
class CAsynchNotify : public IDBAsynchNotify
{
public:
CAsynchNotify() :
_cRef( 1 ),
_cLowResource( 0 ),
_hEvent( 0 )
{
_hEvent = CreateEventW( 0, TRUE, FALSE, 0 );
}
~CAsynchNotify()
{
if ( 0 != _hEvent )
CloseHandle( _hEvent );
}
BOOL IsValid() const { return 0 != _hEvent; }
//
// IUnknown methods.
//
STDMETHOD(QueryInterface) ( REFIID riid, LPVOID *ppiuk )
{
*ppiuk = (void **) this; // hold our breath and jump
AddRef();
return S_OK;
}
STDMETHOD_( ULONG, AddRef ) () { return InterlockedIncrement( &_cRef ); }
STDMETHOD_( ULONG, Release) () { return InterlockedDecrement( &_cRef ); }
//
// IDBAsynchNotify methods
//
STDMETHOD( OnLowResource ) ( DB_DWRESERVE dwReserved )
{
_cLowResource++;
// If we've failed a few times due to low resource, give up
// on the query since there may not be sufficient resources
// to ever get an OnStop call.
if ( _cLowResource >= 5 )
SetEvent( _hEvent );
return S_OK;
}
STDMETHOD( OnProgress ) ( HCHAPTER hChap, DBASYNCHOP ulOp,
DBCOUNTITEM ulProg, DBCOUNTITEM ulProgMax,
DBASYNCHPHASE ulStat, LPOLESTR pwszStatus )
{
return S_OK;
}
STDMETHOD( OnStop ) ( HCHAPTER hChap, ULONG ulOp,
HRESULT hrStat, LPOLESTR pwszStatus )
{
// If the query is complete (successfully or not), set the event
if ( DBASYNCHOP_OPEN == ulOp )
SetEvent( _hEvent );
return S_OK;
}
void Wait()
{
WaitForSingleObject( _hEvent, INFINITE );
}
private:
LONG _cRef;
LONG _cLowResource;
HANDLE _hEvent;
};
//+-------------------------------------------------------------------------
//
// Function: WaitForQueryToComplete
//
// Synopsis: Waits for the query to complete. This function polls for
// completion. Alternatively, the IConnectionPointContainer
// and IRowsetWatchNotify interfaces could be used for
// asynchronous notification when the query completes.
//
// Arguments: [pRowset] -- The rowset to wait for
//
// Returns: HRESULT result
//
//--------------------------------------------------------------------------
HRESULT WaitForQueryToComplete( IRowset * pRowset )
{
HRESULT hr;
//
// Both methods (notifications and polling) work. It depends on the
// application which is the best choice.
//
#if 1
// Register for notifications
XInterface<IConnectionPointContainer> xCPC;
hr = pRowset->QueryInterface( IID_IConnectionPointContainer,
xCPC.GetQIPointer() );
if (FAILED(hr))
return hr;
XInterface<IConnectionPoint> xCP;
hr = xCPC->FindConnectionPoint( IID_IDBAsynchNotify,
xCP.GetPPointer() );
if (FAILED(hr) && CONNECT_E_NOCONNECTION != hr )
return hr;
CAsynchNotify Notify;
if ( !Notify.IsValid() )
return HRESULT_FROM_WIN32( GetLastError() );
DWORD dwAdviseID;
hr = xCP->Advise( (IUnknown *) &Notify, &dwAdviseID );
if (FAILED(hr))
return hr;
//
// In a real app, we'd be off doing other work rather than waiting
// for the query to complete, but this will do.
// MsgWaitForSingleObject is a good choice for a GUI app. You could
// also post a user-defined windows message when a notification is
// received.
//
Notify.Wait();
hr = xCP->Unadvise( dwAdviseID );
if ( S_OK != hr )
return hr;
Notify.Release();
#else
// Poll for query completion
XInterface<IDBAsynchStatus> xIDBAsynch;
hr = pRowset->QueryInterface( IID_IDBAsynchStatus,
xIDBAsynch.GetQIPointer() );
if ( FAILED( hr ) )
return hr;
do
{
DBCOUNTITEM Numerator, Denominator;
DBASYNCHPHASE Phase;
hr = xIDBAsynch->GetStatus( DB_NULL_HCHAPTER,
DBASYNCHOP_OPEN,
&Numerator,
&Denominator,
&Phase,
0 );
if ( FAILED( hr ) )
return hr;
if ( DBASYNCHPHASE_COMPLETE == Phase )
break;
Sleep( 50 ); // Give the query a chance to run
} while ( TRUE );
#endif
return hr;
} //WaitForQueryToComplete
//+-------------------------------------------------------------------------
//
// Function: DoQuery
//
// Synopsis: Creates and executes a query, then displays the results.
//
// Arguments: [pwcQueryScope] - Root path for all results
// [pwcQueryCatalog] - Catalog name over which query is run
// [pwcQueryMachine] - Machine name on which query is run
// [pwcQueryRestrition] - The actual query string
// [fDisplayTree] - TRUE to display the command tree
//
// Returns: HRESULT result of the query
//
//--------------------------------------------------------------------------
HRESULT DoQuery(
WCHAR const * pwcQueryScope,
WCHAR const * pwcQueryCatalog,
WCHAR const * pwcQueryMachine,
WCHAR const * pwcQueryRestriction,
BOOL fDisplayTree )
{
// Create an ICommand object. The default scope for the query is the
// entire catalog.
XInterface<ICommand> xICommand;
HRESULT hr = CreateICommand( xICommand.GetPPointer() );
if ( FAILED( hr ) )
return hr;
// Set the scope, catalog, and machine in the ICommand
hr = SetScopeCatalogAndMachine( xICommand.GetPointer(),
pwcQueryScope,
pwcQueryCatalog,
pwcQueryMachine );
if ( FAILED( hr ) )
return hr;
// Set required properties on the ICommand
hr = SetCommandProperties( xICommand.GetPointer() );
if ( FAILED( hr ) )
return hr;
// Create an OLE DB query tree from a text restriction
DBCOMMANDTREE * pTree;
hr = CreateQueryTree( pwcQueryRestriction, // the input query
&pTree ); // the output tree
if ( FAILED( hr ) )
return hr;
// If directed, display the command tree
if ( fDisplayTree )
DisplayCommandTree( pTree );
// Set the tree in the ICommandTree
XInterface<ICommandTree> xICommandTree;
hr = xICommand->QueryInterface( IID_ICommandTree,
xICommandTree.GetQIPointer() );
if ( FAILED( hr ) )
return hr;
hr = xICommandTree->SetCommandTree( &pTree,
DBCOMMANDREUSE_NONE,
FALSE );
if ( FAILED( hr ) )
return hr;
// Execute the query. The query is asynchronously executed.
XInterface<IRowset> xIRowset;
hr = xICommand->Execute( 0, // no aggregating IUnknown
IID_IRowset, // IID for interface to return
0, // no DBPARAMs
0, // no rows affected
xIRowset.GetIUPointer() ); // result
if ( FAILED( hr ) )
return hr;
// Wait for the query to complete, since DBPROP_IDBAsynchStatus was set
// as a command property. If DBPROP_IDBAsynchStatus isn't set, Execute()
// is synchronous and there is no need to wait for completion here.
hr = WaitForQueryToComplete( xIRowset.GetPointer() );
if ( FAILED( hr ) )
return hr;
// Create an accessor, so data can be retrieved from the rowset
XInterface<IAccessor> xIAccessor;
hr = xIRowset->QueryInterface( IID_IAccessor,
xIAccessor.GetQIPointer() );
if ( FAILED( hr ) )
return hr;
// Column iOrdinals are parallel with those passed to CiTextToFullTree,
// so MapColumnIDs isn't necessary. These binding values for dwPart,
// dwMemOwner, and wType are the most optimal bindings for Indexing
// Service.
const ULONG cColumns = 3; // 3 for Rank, Size, and Path
DBBINDING aColumns[ cColumns ];
memset( aColumns, 0, sizeof aColumns );
aColumns[0].iOrdinal = 1; // first column specified above (rank)
aColumns[0].obValue = 0; // offset where value is written in GetData
aColumns[0].dwPart = DBPART_VALUE; // retrieve value, not status
aColumns[0].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // Provider owned
aColumns[0].wType = DBTYPE_VARIANT | DBTYPE_BYREF; // VARIANT *
aColumns[1] = aColumns[0];
aColumns[1].iOrdinal = 2; // second column specified above (size)
aColumns[1].obValue = sizeof (PROPVARIANT *); // offset for value
aColumns[2] = aColumns[0];
aColumns[2].iOrdinal = 3; // third column specified above (path)
aColumns[2].obValue = 2 * sizeof (PROPVARIANT *); // offset for value
HACCESSOR hAccessor;
hr = xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, // rowdata accessor
cColumns, // # of columns
aColumns, // columns
0, // ignored
&hAccessor, // result
0 ); // no status
if ( FAILED( hr ) )
return hr;
// Display the results of the query. Print file size and file path.
printf( " Rank Size Path\n" );
DBCOUNTITEM cRowsSoFar = 0;
do
{
DBCOUNTITEM cRowsReturned = 0;
const ULONG cRowsAtATime = 10;
HROW aHRow[cRowsAtATime];
HROW * pgrHRows = aHRow;
hr = xIRowset->GetNextRows( 0, // no chapter
0, // no rows to skip
cRowsAtATime, // # rows to get
&cRowsReturned, // # rows returned
&pgrHRows); // resulting hrows
if ( FAILED( hr ) )
break;
for ( DBCOUNTITEM iRow = 0; iRow < cRowsReturned; iRow++ )
{
PROPVARIANT * aData[cColumns];
hr = xIRowset->GetData( aHRow[iRow], // hrow being accessed
hAccessor, // accessor to use
&aData ); // resulting data
if ( FAILED( hr ) )
break;
if ( VT_I4 == aData[0]->vt &&
VT_I8 == aData[1]->vt &&
VT_LPWSTR == aData[2]->vt )
printf( "%5d %10I64d %ws\n",
aData[0]->lVal,
aData[1]->hVal,
aData[2]->pwszVal );
else
printf( "could not retrieve a file's values\n" );
}
if ( 0 != cRowsReturned )
xIRowset->ReleaseRows( cRowsReturned, // # of rows to release
aHRow, // rows to release
0, // no options
0, // no refcounts
0 ); // no status
if ( DB_S_ENDOFROWSET == hr )
{
hr = S_OK; // succeeded, return S_OK from DoQuery
break;
}
if ( FAILED( hr ) )
break;
cRowsSoFar += cRowsReturned;
} while ( TRUE );
printf( "%d files matched the query '%ws'\n",
cRowsSoFar,
pwcQueryRestriction );
xIAccessor->ReleaseAccessor( hAccessor, 0 );
return hr;
} //DoQuery
//+-------------------------------------------------------------------------
//
// Function: Usage
//
// Synopsis: Displays information about how to use the app and exits
//
//--------------------------------------------------------------------------
void Usage()
{
printf( "usage: ADVQUERY query [/c:catalog] [/m:machine] [/s:scope] [/d]\n\n" );
printf( " query word or phrase used for the search\n" );
printf( " /c:catalog name of the catalog, default is SYSTEM\n" );
printf( " /m:machine name of the machine, default is local machine\n" );
printf( " /s:scope root path, default is entire catalog (\\) \n" );
printf( " /d display the DBCOMMANDTREE, default is off\n" );
exit( -1 );
} //Usage
//+-------------------------------------------------------------------------
//
// Function: wmain
//
// Synopsis: Entry point for the app. Parses command line arguments
// and issues a query.
//
// Arguments: [argc] - Argument count
// [argv] - Arguments
//
//--------------------------------------------------------------------------
extern "C" int __cdecl wmain( int argc, WCHAR * argv[] )
{
WCHAR const * pwcScope = L"\\"; // default scope: entire catalog
WCHAR const * pwcCatalog = L"system"; // default: system catalog
WCHAR const * pwcMachine = L"."; // default: local machine
WCHAR const * pwcRestriction = 0; // no default restriction
BOOL fDisplayTree = FALSE; // don't display the tree
// Parse command line parameters
for ( int i = 1; i < argc; i++ )
{
if ( L'-' == argv[i][0] || L'/' == argv[i][0] )
{
WCHAR wc = toupper( argv[i][1] );
if ( ':' != argv[i][2] && 'D' != wc )
Usage();
if ( 'C' == wc )
pwcCatalog = argv[i] + 3;
else if ( 'M' == wc )
pwcMachine = argv[i] + 3;
else if ( 'S' == wc )
pwcScope = argv[i] + 3;
else if ( 'D' == wc )
fDisplayTree = TRUE;
else
Usage();
}
else if ( 0 != pwcRestriction )
Usage();
else
pwcRestriction = argv[i];
}
// A query restriction is necessary. Fail if none is given.
if ( 0 == pwcRestriction )
Usage();
// Initialize COM
HRESULT hr = CoInitialize( 0 );
if ( SUCCEEDED( hr ) )
{
// Run the query
hr = DoQuery( pwcScope,
pwcCatalog,
pwcMachine,
pwcRestriction,
fDisplayTree );
CoUninitialize();
}
if ( FAILED( hr ) )
{
printf( "the query '%ws' failed with error %#x\n",
pwcRestriction, hr );
return -1;
}
return 0;
} //wmain