ADVQUERY.CXX

//+------------------------------------------------------------------------- 
//
// 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 1997 Microsoft Corporation. All Rights Reserved.
//
// PROGRAM: advquery.cxx
//
// PURPOSE: Illustrates a minimal query using Microsoft Index Server.
// Uses native OLE DB, not Index Server helper functions.
//
// PLATFORM: Windows NT
//
//--------------------------------------------------------------------------

#define UNICODE

#include <stdio.h>
#include <windows.h>

#define OLEDBVER 0x0200 // need the command tree definitions
#define DBINITCONSTANTS

#include <oledberr.h>
#include <oledb.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

const GUID CLSID_IndexServerDSO = CLSID_INDEX_SERVER_DSO;
XInterface<IDBInitialize> xIDBInit;
HRESULT hr = CoCreateInstance( CLSID_IndexServerDSO,
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.
//
// Both of these properties are unique to Index Server's OLE DB
// 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;

DBPROP aProp[2];

aProp[0].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
aProp[0].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
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;

DBPROPSET aPropSet[1];

aPropSet[0].rgProperties = &aProp[0];
aPropSet[0].cProperties = 2;
aPropSet[0].guidPropertySet = guidQueryExt;

XInterface<ICommandProperties> xICommandProperties;
HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
xICommandProperties.GetQIPointer() );
if ( FAILED( hr ) )
return hr;

return xICommandProperties->SetProperties( 1, // 1 property set
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[1].dwPropertyID = DBPROP_CI_SCOPE_FLAGS;
aScopeProperties[1].vValue.vt = VT_I4 | VT_ARRAY;
aScopeProperties[1].vValue.parray = pFlags;

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;

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;

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
//
// 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

//+-------------------------------------------------------------------------
//
// 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 complete when Execute() returns

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;

// 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 Index Server.

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; // Index Server 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" );

ULONG cRowsSoFar = 0;

do
{
ULONG 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 ( ULONG 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