1155 lines
42 KiB
C++
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
|
|
|