windows-nt/Source/XPSP1/NT/inetsrv/query/apps/srchslm/srchslm.cxx

711 lines
22 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// 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: qsample.cxx
//
// PURPOSE: Illustrates a minimal query using Indexing Service.
// Uses CICreateCommand and CITextToFullTree helper functions.
//
// PLATFORM: Windows NT
//
//--------------------------------------------------------------------------
//#define UNICODE
#define OLEDBVER 0x0250 // need the command tree definitions
#define DBINITCONSTANTS
#include <stdio.h>
#include <windows.h>
#include <oledberr.h>
#include <oledb.h>
#include <cmdtree.h>
#include <ntquery.h>
#include <cierror.h>
//
// Local prototypes
//
BOOL FindSlmRoot( WCHAR const * pwszPath, char * pszName, char * pszProject, char * pszRoot, char * pszSubDir );
struct SlmModifications
{
char szLogname[100];
char szLastChange[50];
unsigned cMods;
};
unsigned WhoModified( char const * pszName,
char const * pszProject,
char const * pszRoot,
char const * pszSubDir,
SlmModifications & aMods,
unsigned cLogname );
//
// Local constants
//
CIPROPERTYDEF aProperties[] = { { L"Func",
DBTYPE_WSTR | DBTYPE_BYREF,
{ { 0x8dee0300, 0x16c2, 0x101b, 0xb1, 0x21, 0x08, 0x00, 0x2b, 0x2e, 0xcd, 0xa9 },
DBKIND_GUID_NAME,
L"func"
}
},
{ L"CLASS",
DBTYPE_WSTR | DBTYPE_BYREF,
{ { 0x8dee0300, 0x16c2, 0x101b, 0xb1, 0x21, 0x08, 0x00, 0x2b, 0x2e, 0xcd, 0xa9 },
DBKIND_GUID_NAME,
L"class"
}
}
};
// 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;
};
BOOL FindSlmRoot( WCHAR const * pwszPath, char * pszName, char * pszProject, char * pszRoot, char * pszSubDir )
{
//
// Compute path to slm.ini
//
char szSlmIni[MAX_PATH];
wcstombs( szSlmIni, pwszPath, sizeof(szSlmIni) );
char * pcLastSlash = strrchr( szSlmIni, '\\' );
if ( 0 == pcLastSlash )
return FALSE;
strcpy( pszName, pcLastSlash + 1 );
strcpy( pcLastSlash + 1, "slm.ini" );
//
// Open file and read project / root
//
FILE * pf = fopen( szSlmIni, "r" );
if ( 0 == pf )
return FALSE;
//
// Project
//
if ( 0 == fgets( pszProject, MAX_PATH, pf ) ||
0 != strncmp( pszProject, "project = ", 10 ) )
{
fclose( pf );
return FALSE;
}
unsigned cc = strlen( pszProject ) - 10 - 1; // Preface and newline
memmove( pszProject, pszProject + 10, cc );
pszProject[cc] = 0;
//
// Root
//
if ( 0 == fgets( pszRoot, MAX_PATH, pf ) ||
0 != strncmp( pszRoot, "slm root = ", 11 ) )
{
fclose( pf );
return FALSE;
}
cc = strlen( pszRoot ) - 11 - 1; // Preface and newline
memmove( pszRoot, pszRoot + 11, cc );
pszRoot[cc] = 0;
for ( char * p = pszRoot; *p; p++ )
{
if ( '/' == *p )
*p = '\\';
}
//
// Subdir
//
if ( 0 == fgets( pszSubDir, MAX_PATH, pf ) ||
0 == fgets( pszSubDir, MAX_PATH, pf ) ||
0 != strncmp( pszSubDir, "sub dir = ", 10 ) )
{
fclose( pf );
return FALSE;
}
cc = strlen( pszSubDir ) - 10 - 1; // Preface and newline
unsigned ccQuote = 0;
if ( pszSubDir[10] == '"' )
ccQuote = 1;
memmove( pszSubDir, pszSubDir + 10 + ccQuote, cc - 2*ccQuote);
pszSubDir[cc - 2*ccQuote] = 0;
for ( p = pszSubDir; *p; p++ )
{
if ( '/' == *p )
*p = '\\';
}
fclose( pf );
return TRUE;
}
unsigned WhoModified( char const * pszName,
char const * pszProject,
char const * pszRoot,
char const * pszSubDir,
SlmModifications * aMods,
unsigned cMods )
{
//
// Initialize
//
memset( aMods, 0, sizeof(SlmModifications) * cMods );
unsigned iNext = 0;
//
// Put the pieces together.
//
char szPath[MAX_PATH];
strcpy( szPath, pszRoot );
strcat( szPath, "\\diff\\" );
strcat( szPath, pszProject );
strcat( szPath, pszSubDir );
strcat( szPath, "\\" );
strcat( szPath, pszName );
FILE * pf = fopen( szPath, "r" );
if ( 0 == pf )
return 0;
char szTemp[500];
char szDate[50];
while ( 0 != fgets( szTemp, sizeof(szTemp), pf ) )
{
if ( 0 == _strnicmp( szTemp, "#T ", 3 ) )
{
//
// Record the date, sans <cr>.
//
unsigned cc = strlen(szTemp + 3) - 1;
memcpy( szDate, szTemp + 3, cc );
szDate[cc] = 0;
continue;
}
if ( 0 == _strnicmp( szTemp, "#A ", 3 ) )
{
//
// Have we seen this user before?
//
char * pUser = szTemp + 3;
pUser[strlen(pUser)-1] = 0; // Remove newline
for ( unsigned i = 0; i < cMods; i++ )
{
if ( 0 == _stricmp( pUser, aMods[i].szLogname ) )
{
aMods[i].cMods++;
strcpy( aMods[i].szLastChange, szDate );
//
// Move this user to the end of the list.
//
SlmModifications Temp;
memcpy( &Temp, &aMods[i], sizeof(Temp) );
for ( unsigned j = i+1; j < iNext; j++ )
memcpy( &aMods[j-1], &aMods[j], sizeof(aMods[0]) );
memcpy( &aMods[j], &Temp, sizeof(Temp) );
break;
}
}
//
// New user?
//
if ( i == cMods )
{
strcpy( aMods[iNext].szLogname, pUser );
aMods[iNext].cMods = 1;
strcpy( aMods[iNext].szLastChange, szDate );
iNext++;
if ( iNext == cMods )
iNext = 0;
}
}
}
fclose( pf );
return iNext;
}
//+-------------------------------------------------------------------------
//
// 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: DoQuery
//
// Synopsis: Creates and executes a query, then displays the results.
//
// Arguments: [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 * pwcQueryCatalog,
WCHAR const * pwcQueryMachine,
WCHAR const * pwcQueryRestriction,
BOOL fDisplayTree )
{
// Create an ICommand object. The default scope for the query is the
// entire catalog. CICreateCommand is a shortcut for making an
// ICommand. The ADVQUERY sample shows the OLE DB equivalent.
XInterface<ICommand> xICommand;
HRESULT hr = CICreateCommand( xICommand.GetIUPointer(), // result
0, // controlling unknown
IID_ICommand, // IID requested
pwcQueryCatalog, // catalog name
pwcQueryMachine ); // machine name
if ( FAILED( hr ) )
return hr;
// Set required properties on the ICommand
hr = SetCommandProperties( xICommand.GetPointer() );
if ( FAILED( hr ) )
return hr;
//
// @func
//
// Create an OLE DB query tree from a text restriction, column
// set, and sort order.
DBCOMMANDTREE * pTree;
hr = CITextToFullTree( pwcQueryRestriction, // the query itself
L"Size,Path", // columns to return
L"Rank[d]", // rank descending
0, // reserved
&pTree, // resulting tree
sizeof(aProperties)/sizeof(aProperties[0]), // custom properties
aProperties, // custom properties
GetSystemDefaultLCID() ); // default locale
//
// The user may have a DefineColumns.txt file with func/class in it.
//
if ( QPLIST_E_DUPLICATE == hr )
{
hr = CITextToFullTree( pwcQueryRestriction, // the query itself
L"Size,Path", // columns to return
L"Rank[d]", // rank descending
0, // reserved
&pTree, // resulting tree
0, // Custom props from global column def file
0,
GetSystemDefaultLCID() ); // default locale
}
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 = 2; // 2 for Size and Path
DBBINDING aColumns[ cColumns ];
memset( aColumns, 0, sizeof aColumns );
aColumns[0].iOrdinal = 1; // first column specified above (size)
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 (path)
aColumns[1].obValue = 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.
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_I8 == aData[0]->vt &&
VT_LPWSTR == aData[1]->vt )
{
char szName[MAX_PATH];
char szProject[MAX_PATH];
char szRoot[MAX_PATH];
char szSubDir[MAX_PATH];
if ( FindSlmRoot( aData[1]->pwszVal,
szName,
szProject,
szRoot,
szSubDir ) )
{
printf( "SERVER: %s, PROJECT: %s, FILE: %s\\%s\n",
szRoot, szProject, szSubDir, szName );
SlmModifications aMods[10];
unsigned iStart = WhoModified( szName,
szProject,
szRoot,
szSubDir,
aMods,
sizeof(aMods)/sizeof(aMods[0]) );
BOOL fHeader = TRUE;
for ( unsigned i = 0; i < sizeof(aMods)/sizeof(aMods[0]); i++ )
{
if ( aMods[iStart].cMods > 0 )
{
if ( fHeader )
{
printf( " LAST MODIFIED BY: " );
fHeader = FALSE;
}
else
printf( " " );
printf( "%s on %s", aMods[iStart].szLogname, aMods[iStart].szLastChange );
if ( aMods[iStart].cMods > 1 )
printf( " (%u changes)\n", aMods[iStart].cMods );
else
printf( "\n" );
}
iStart++;
if ( iStart == sizeof(aMods)/sizeof(aMods[0]) )
iStart = 0;
}
}
else
{
printf( "NON SLM FILE: %ws\n", aData[1]->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: QSAMPLE query [/c:catalog] [/m:machine] [/d]\n\n" );
printf( " query an Indexing Service query\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( " /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 * pwcCatalog = L"sources"; // default: system catalog
WCHAR const * pwcMachine = L"index2"; // default: Index2
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 = (WCHAR) 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 ( '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();
// Run the query
HRESULT hr = DoQuery( pwcCatalog,
pwcMachine,
pwcRestriction,
fDisplayTree );
if ( FAILED( hr ) )
{
printf( "the query '%ws' failed with error %#x\n",
pwcRestriction, hr );
return -1;
}
return 0;
} //wmain