Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

710 lines
22 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  4. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  5. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  6. // PARTICULAR PURPOSE.
  7. //
  8. // Copyright 1997 Microsoft Corporation. All Rights Reserved.
  9. //
  10. // PROGRAM: qsample.cxx
  11. //
  12. // PURPOSE: Illustrates a minimal query using Indexing Service.
  13. // Uses CICreateCommand and CITextToFullTree helper functions.
  14. //
  15. // PLATFORM: Windows NT
  16. //
  17. //--------------------------------------------------------------------------
  18. //#define UNICODE
  19. #define OLEDBVER 0x0250 // need the command tree definitions
  20. #define DBINITCONSTANTS
  21. #include <stdio.h>
  22. #include <windows.h>
  23. #include <oledberr.h>
  24. #include <oledb.h>
  25. #include <cmdtree.h>
  26. #include <ntquery.h>
  27. #include <cierror.h>
  28. //
  29. // Local prototypes
  30. //
  31. BOOL FindSlmRoot( WCHAR const * pwszPath, char * pszName, char * pszProject, char * pszRoot, char * pszSubDir );
  32. struct SlmModifications
  33. {
  34. char szLogname[100];
  35. char szLastChange[50];
  36. unsigned cMods;
  37. };
  38. unsigned WhoModified( char const * pszName,
  39. char const * pszProject,
  40. char const * pszRoot,
  41. char const * pszSubDir,
  42. SlmModifications & aMods,
  43. unsigned cLogname );
  44. //
  45. // Local constants
  46. //
  47. CIPROPERTYDEF aProperties[] = { { L"Func",
  48. DBTYPE_WSTR | DBTYPE_BYREF,
  49. { { 0x8dee0300, 0x16c2, 0x101b, 0xb1, 0x21, 0x08, 0x00, 0x2b, 0x2e, 0xcd, 0xa9 },
  50. DBKIND_GUID_NAME,
  51. L"func"
  52. }
  53. },
  54. { L"CLASS",
  55. DBTYPE_WSTR | DBTYPE_BYREF,
  56. { { 0x8dee0300, 0x16c2, 0x101b, 0xb1, 0x21, 0x08, 0x00, 0x2b, 0x2e, 0xcd, 0xa9 },
  57. DBKIND_GUID_NAME,
  58. L"class"
  59. }
  60. }
  61. };
  62. // This is found in disptree.cxx
  63. //extern void DisplayCommandTree( DBCOMMANDTREE * pNode, ULONG iLevel = 0 );
  64. //+-------------------------------------------------------------------------
  65. //
  66. // Template: XInterface
  67. //
  68. // Synopsis: Template for managing ownership of interfaces
  69. //
  70. //--------------------------------------------------------------------------
  71. template<class T> class XInterface
  72. {
  73. public:
  74. XInterface( T * p = 0 ) : _p( p ) {}
  75. ~XInterface() { if ( 0 != _p ) _p->Release(); }
  76. T * operator->() { return _p; }
  77. T * GetPointer() const { return _p; }
  78. IUnknown ** GetIUPointer() { return (IUnknown **) &_p; }
  79. T ** GetPPointer() { return &_p; }
  80. void ** GetQIPointer() { return (void **) &_p; }
  81. T * Acquire() { T * p = _p; _p = 0; return p; }
  82. private:
  83. T * _p;
  84. };
  85. BOOL FindSlmRoot( WCHAR const * pwszPath, char * pszName, char * pszProject, char * pszRoot, char * pszSubDir )
  86. {
  87. //
  88. // Compute path to slm.ini
  89. //
  90. char szSlmIni[MAX_PATH];
  91. wcstombs( szSlmIni, pwszPath, sizeof(szSlmIni) );
  92. char * pcLastSlash = strrchr( szSlmIni, '\\' );
  93. if ( 0 == pcLastSlash )
  94. return FALSE;
  95. strcpy( pszName, pcLastSlash + 1 );
  96. strcpy( pcLastSlash + 1, "slm.ini" );
  97. //
  98. // Open file and read project / root
  99. //
  100. FILE * pf = fopen( szSlmIni, "r" );
  101. if ( 0 == pf )
  102. return FALSE;
  103. //
  104. // Project
  105. //
  106. if ( 0 == fgets( pszProject, MAX_PATH, pf ) ||
  107. 0 != strncmp( pszProject, "project = ", 10 ) )
  108. {
  109. fclose( pf );
  110. return FALSE;
  111. }
  112. unsigned cc = strlen( pszProject ) - 10 - 1; // Preface and newline
  113. memmove( pszProject, pszProject + 10, cc );
  114. pszProject[cc] = 0;
  115. //
  116. // Root
  117. //
  118. if ( 0 == fgets( pszRoot, MAX_PATH, pf ) ||
  119. 0 != strncmp( pszRoot, "slm root = ", 11 ) )
  120. {
  121. fclose( pf );
  122. return FALSE;
  123. }
  124. cc = strlen( pszRoot ) - 11 - 1; // Preface and newline
  125. memmove( pszRoot, pszRoot + 11, cc );
  126. pszRoot[cc] = 0;
  127. for ( char * p = pszRoot; *p; p++ )
  128. {
  129. if ( '/' == *p )
  130. *p = '\\';
  131. }
  132. //
  133. // Subdir
  134. //
  135. if ( 0 == fgets( pszSubDir, MAX_PATH, pf ) ||
  136. 0 == fgets( pszSubDir, MAX_PATH, pf ) ||
  137. 0 != strncmp( pszSubDir, "sub dir = ", 10 ) )
  138. {
  139. fclose( pf );
  140. return FALSE;
  141. }
  142. cc = strlen( pszSubDir ) - 10 - 1; // Preface and newline
  143. unsigned ccQuote = 0;
  144. if ( pszSubDir[10] == '"' )
  145. ccQuote = 1;
  146. memmove( pszSubDir, pszSubDir + 10 + ccQuote, cc - 2*ccQuote);
  147. pszSubDir[cc - 2*ccQuote] = 0;
  148. for ( p = pszSubDir; *p; p++ )
  149. {
  150. if ( '/' == *p )
  151. *p = '\\';
  152. }
  153. fclose( pf );
  154. return TRUE;
  155. }
  156. unsigned WhoModified( char const * pszName,
  157. char const * pszProject,
  158. char const * pszRoot,
  159. char const * pszSubDir,
  160. SlmModifications * aMods,
  161. unsigned cMods )
  162. {
  163. //
  164. // Initialize
  165. //
  166. memset( aMods, 0, sizeof(SlmModifications) * cMods );
  167. unsigned iNext = 0;
  168. //
  169. // Put the pieces together.
  170. //
  171. char szPath[MAX_PATH];
  172. strcpy( szPath, pszRoot );
  173. strcat( szPath, "\\diff\\" );
  174. strcat( szPath, pszProject );
  175. strcat( szPath, pszSubDir );
  176. strcat( szPath, "\\" );
  177. strcat( szPath, pszName );
  178. FILE * pf = fopen( szPath, "r" );
  179. if ( 0 == pf )
  180. return 0;
  181. char szTemp[500];
  182. char szDate[50];
  183. while ( 0 != fgets( szTemp, sizeof(szTemp), pf ) )
  184. {
  185. if ( 0 == _strnicmp( szTemp, "#T ", 3 ) )
  186. {
  187. //
  188. // Record the date, sans <cr>.
  189. //
  190. unsigned cc = strlen(szTemp + 3) - 1;
  191. memcpy( szDate, szTemp + 3, cc );
  192. szDate[cc] = 0;
  193. continue;
  194. }
  195. if ( 0 == _strnicmp( szTemp, "#A ", 3 ) )
  196. {
  197. //
  198. // Have we seen this user before?
  199. //
  200. char * pUser = szTemp + 3;
  201. pUser[strlen(pUser)-1] = 0; // Remove newline
  202. for ( unsigned i = 0; i < cMods; i++ )
  203. {
  204. if ( 0 == _stricmp( pUser, aMods[i].szLogname ) )
  205. {
  206. aMods[i].cMods++;
  207. strcpy( aMods[i].szLastChange, szDate );
  208. //
  209. // Move this user to the end of the list.
  210. //
  211. SlmModifications Temp;
  212. memcpy( &Temp, &aMods[i], sizeof(Temp) );
  213. for ( unsigned j = i+1; j < iNext; j++ )
  214. memcpy( &aMods[j-1], &aMods[j], sizeof(aMods[0]) );
  215. memcpy( &aMods[j], &Temp, sizeof(Temp) );
  216. break;
  217. }
  218. }
  219. //
  220. // New user?
  221. //
  222. if ( i == cMods )
  223. {
  224. strcpy( aMods[iNext].szLogname, pUser );
  225. aMods[iNext].cMods = 1;
  226. strcpy( aMods[iNext].szLastChange, szDate );
  227. iNext++;
  228. if ( iNext == cMods )
  229. iNext = 0;
  230. }
  231. }
  232. }
  233. fclose( pf );
  234. return iNext;
  235. }
  236. //+-------------------------------------------------------------------------
  237. //
  238. // Function: SetCommandProperties
  239. //
  240. // Synopsis: Sets the DBPROP_USEEXTENDEDDBTYPES property to TRUE, so
  241. // data is returned in PROPVARIANTs, as opposed to the
  242. // default, which is OLE automation VARIANTs. PROPVARIANTS
  243. // allow a superset of VARIANT data types. Use of these
  244. // types avoids costly coercions.
  245. //
  246. // Also sets the DBPROP_USECONTENTINDEX property to TRUE, so
  247. // the index will always be used to resolve the query (as
  248. // opposed to enumerating all the files on the disk), even
  249. // if the index is out of date.
  250. //
  251. // Both of these properties are unique to Index Server's OLE DB
  252. // implementation.
  253. //
  254. // Arguments: [pICommand] - The ICommand used to set the property
  255. //
  256. // Returns: HRESULT result of setting the properties
  257. //
  258. //--------------------------------------------------------------------------
  259. HRESULT SetCommandProperties( ICommand * pICommand )
  260. {
  261. static const DBID dbcolNull = { { 0,0,0, { 0,0,0,0,0,0,0,0 } },
  262. DBKIND_GUID_PROPID, 0 };
  263. static const GUID guidQueryExt = DBPROPSET_QUERYEXT;
  264. DBPROP aProp[2];
  265. aProp[0].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
  266. aProp[0].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
  267. aProp[0].dwStatus = 0;
  268. aProp[0].colid = dbcolNull;
  269. aProp[0].vValue.vt = VT_BOOL;
  270. aProp[0].vValue.boolVal = VARIANT_TRUE;
  271. aProp[1] = aProp[0];
  272. aProp[1].dwPropertyID = DBPROP_USECONTENTINDEX;
  273. DBPROPSET aPropSet[1];
  274. aPropSet[0].rgProperties = &aProp[0];
  275. aPropSet[0].cProperties = 2;
  276. aPropSet[0].guidPropertySet = guidQueryExt;
  277. XInterface<ICommandProperties> xICommandProperties;
  278. HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
  279. xICommandProperties.GetQIPointer() );
  280. if ( FAILED( hr ) )
  281. return hr;
  282. return xICommandProperties->SetProperties( 1, // 1 property set
  283. aPropSet ); // the properties
  284. } //SetCommandProperties
  285. //+-------------------------------------------------------------------------
  286. //
  287. // Function: DoQuery
  288. //
  289. // Synopsis: Creates and executes a query, then displays the results.
  290. //
  291. // Arguments: [pwcQueryCatalog] - Catalog name over which query is run
  292. // [pwcQueryMachine] - Machine name on which query is run
  293. // [pwcQueryRestrition] - The actual query string
  294. // [fDisplayTree] - TRUE to display the command tree
  295. //
  296. // Returns: HRESULT result of the query
  297. //
  298. //--------------------------------------------------------------------------
  299. HRESULT DoQuery(
  300. WCHAR const * pwcQueryCatalog,
  301. WCHAR const * pwcQueryMachine,
  302. WCHAR const * pwcQueryRestriction,
  303. BOOL fDisplayTree )
  304. {
  305. // Create an ICommand object. The default scope for the query is the
  306. // entire catalog. CICreateCommand is a shortcut for making an
  307. // ICommand. The ADVQUERY sample shows the OLE DB equivalent.
  308. XInterface<ICommand> xICommand;
  309. HRESULT hr = CICreateCommand( xICommand.GetIUPointer(), // result
  310. 0, // controlling unknown
  311. IID_ICommand, // IID requested
  312. pwcQueryCatalog, // catalog name
  313. pwcQueryMachine ); // machine name
  314. if ( FAILED( hr ) )
  315. return hr;
  316. // Set required properties on the ICommand
  317. hr = SetCommandProperties( xICommand.GetPointer() );
  318. if ( FAILED( hr ) )
  319. return hr;
  320. //
  321. // @func
  322. //
  323. // Create an OLE DB query tree from a text restriction, column
  324. // set, and sort order.
  325. DBCOMMANDTREE * pTree;
  326. hr = CITextToFullTree( pwcQueryRestriction, // the query itself
  327. L"Size,Path", // columns to return
  328. L"Rank[d]", // rank descending
  329. 0, // reserved
  330. &pTree, // resulting tree
  331. sizeof(aProperties)/sizeof(aProperties[0]), // custom properties
  332. aProperties, // custom properties
  333. GetSystemDefaultLCID() ); // default locale
  334. //
  335. // The user may have a DefineColumns.txt file with func/class in it.
  336. //
  337. if ( QPLIST_E_DUPLICATE == hr )
  338. {
  339. hr = CITextToFullTree( pwcQueryRestriction, // the query itself
  340. L"Size,Path", // columns to return
  341. L"Rank[d]", // rank descending
  342. 0, // reserved
  343. &pTree, // resulting tree
  344. 0, // Custom props from global column def file
  345. 0,
  346. GetSystemDefaultLCID() ); // default locale
  347. }
  348. if ( FAILED( hr ) )
  349. return hr;
  350. // If directed, display the command tree
  351. //if ( fDisplayTree )
  352. // DisplayCommandTree( pTree );
  353. // Set the tree in the ICommandTree
  354. XInterface<ICommandTree> xICommandTree;
  355. hr = xICommand->QueryInterface( IID_ICommandTree,
  356. xICommandTree.GetQIPointer() );
  357. if ( FAILED( hr ) )
  358. return hr;
  359. hr = xICommandTree->SetCommandTree( &pTree,
  360. DBCOMMANDREUSE_NONE,
  361. FALSE );
  362. if ( FAILED( hr ) )
  363. return hr;
  364. // Execute the query. The query is complete when Execute() returns
  365. XInterface<IRowset> xIRowset;
  366. hr = xICommand->Execute( 0, // no aggregating IUnknown
  367. IID_IRowset, // IID for interface to return
  368. 0, // no DBPARAMs
  369. 0, // no rows affected
  370. xIRowset.GetIUPointer() ); // result
  371. if ( FAILED( hr ) )
  372. return hr;
  373. // Create an accessor, so data can be retrieved from the rowset
  374. XInterface<IAccessor> xIAccessor;
  375. hr = xIRowset->QueryInterface( IID_IAccessor,
  376. xIAccessor.GetQIPointer() );
  377. if ( FAILED( hr ) )
  378. return hr;
  379. // Column iOrdinals are parallel with those passed to CiTextToFullTree,
  380. // so MapColumnIDs isn't necessary. These binding values for dwPart,
  381. // dwMemOwner, and wType are the most optimal bindings for Index Server.
  382. const ULONG cColumns = 2; // 2 for Size and Path
  383. DBBINDING aColumns[ cColumns ];
  384. memset( aColumns, 0, sizeof aColumns );
  385. aColumns[0].iOrdinal = 1; // first column specified above (size)
  386. aColumns[0].obValue = 0; // offset where value is written in GetData
  387. aColumns[0].dwPart = DBPART_VALUE; // retrieve value, not status
  388. aColumns[0].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // Index Server owned
  389. aColumns[0].wType = DBTYPE_VARIANT | DBTYPE_BYREF; // VARIANT *
  390. aColumns[1] = aColumns[0];
  391. aColumns[1].iOrdinal = 2; // second column specified above (path)
  392. aColumns[1].obValue = sizeof (PROPVARIANT *); // offset for value
  393. HACCESSOR hAccessor;
  394. hr = xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, // rowdata accessor
  395. cColumns, // # of columns
  396. aColumns, // columns
  397. 0, // ignored
  398. &hAccessor, // result
  399. 0 ); // no status
  400. if ( FAILED( hr ) )
  401. return hr;
  402. // Display the results of the query. Print file size and file path.
  403. DBCOUNTITEM cRowsSoFar = 0;
  404. do
  405. {
  406. DBCOUNTITEM cRowsReturned = 0;
  407. const ULONG cRowsAtATime = 10;
  408. HROW aHRow[cRowsAtATime];
  409. HROW * pgrHRows = aHRow;
  410. hr = xIRowset->GetNextRows( 0, // no chapter
  411. 0, // no rows to skip
  412. cRowsAtATime, // # rows to get
  413. &cRowsReturned, // # rows returned
  414. &pgrHRows); // resulting hrows
  415. if ( FAILED( hr ) )
  416. break;
  417. for ( DBCOUNTITEM iRow = 0; iRow < cRowsReturned; iRow++ )
  418. {
  419. PROPVARIANT * aData[cColumns];
  420. hr = xIRowset->GetData( aHRow[iRow], // hrow being accessed
  421. hAccessor, // accessor to use
  422. &aData ); // resulting data
  423. if ( FAILED( hr ) )
  424. break;
  425. if ( VT_I8 == aData[0]->vt &&
  426. VT_LPWSTR == aData[1]->vt )
  427. {
  428. char szName[MAX_PATH];
  429. char szProject[MAX_PATH];
  430. char szRoot[MAX_PATH];
  431. char szSubDir[MAX_PATH];
  432. if ( FindSlmRoot( aData[1]->pwszVal,
  433. szName,
  434. szProject,
  435. szRoot,
  436. szSubDir ) )
  437. {
  438. printf( "SERVER: %s, PROJECT: %s, FILE: %s\\%s\n",
  439. szRoot, szProject, szSubDir, szName );
  440. SlmModifications aMods[10];
  441. unsigned iStart = WhoModified( szName,
  442. szProject,
  443. szRoot,
  444. szSubDir,
  445. aMods,
  446. sizeof(aMods)/sizeof(aMods[0]) );
  447. BOOL fHeader = TRUE;
  448. for ( unsigned i = 0; i < sizeof(aMods)/sizeof(aMods[0]); i++ )
  449. {
  450. if ( aMods[iStart].cMods > 0 )
  451. {
  452. if ( fHeader )
  453. {
  454. printf( " LAST MODIFIED BY: " );
  455. fHeader = FALSE;
  456. }
  457. else
  458. printf( " " );
  459. printf( "%s on %s", aMods[iStart].szLogname, aMods[iStart].szLastChange );
  460. if ( aMods[iStart].cMods > 1 )
  461. printf( " (%u changes)\n", aMods[iStart].cMods );
  462. else
  463. printf( "\n" );
  464. }
  465. iStart++;
  466. if ( iStart == sizeof(aMods)/sizeof(aMods[0]) )
  467. iStart = 0;
  468. }
  469. }
  470. else
  471. {
  472. printf( "NON SLM FILE: %ws\n", aData[1]->pwszVal );
  473. }
  474. }
  475. else
  476. printf( "could not retrieve a file's values\n" );
  477. }
  478. if ( 0 != cRowsReturned )
  479. xIRowset->ReleaseRows( cRowsReturned, // # of rows to release
  480. aHRow, // rows to release
  481. 0, // no options
  482. 0, // no refcounts
  483. 0 ); // no status
  484. if ( DB_S_ENDOFROWSET == hr )
  485. {
  486. hr = S_OK; // succeeded, return S_OK from DoQuery
  487. break;
  488. }
  489. if ( FAILED( hr ) )
  490. break;
  491. cRowsSoFar += cRowsReturned;
  492. } while ( TRUE );
  493. printf( "%d files matched the query '%ws'\n",
  494. cRowsSoFar,
  495. pwcQueryRestriction );
  496. xIAccessor->ReleaseAccessor( hAccessor, 0 );
  497. return hr;
  498. } //DoQuery
  499. //+-------------------------------------------------------------------------
  500. //
  501. // Function: Usage
  502. //
  503. // Synopsis: Displays information about how to use the app and exits
  504. //
  505. //--------------------------------------------------------------------------
  506. void Usage()
  507. {
  508. printf( "usage: QSAMPLE query [/c:catalog] [/m:machine] [/d]\n\n" );
  509. printf( " query an Indexing Service query\n" );
  510. printf( " /c:catalog name of the catalog, default is SYSTEM\n" );
  511. printf( " /m:machine name of the machine, default is local machine\n" );
  512. printf( " /d display the DBCOMMANDTREE, default is off\n" );
  513. exit( -1 );
  514. } //Usage
  515. //+-------------------------------------------------------------------------
  516. //
  517. // Function: wmain
  518. //
  519. // Synopsis: Entry point for the app. Parses command line arguments
  520. // and issues a query.
  521. //
  522. // Arguments: [argc] - Argument count
  523. // [argv] - Arguments
  524. //
  525. //--------------------------------------------------------------------------
  526. extern "C" int __cdecl wmain( int argc, WCHAR * argv[] )
  527. {
  528. WCHAR const * pwcCatalog = L"sources"; // default: system catalog
  529. WCHAR const * pwcMachine = L"index2"; // default: Index2
  530. WCHAR const * pwcRestriction = 0; // no default restriction
  531. BOOL fDisplayTree = FALSE; // don't display the tree
  532. // Parse command line parameters
  533. for ( int i = 1; i < argc; i++ )
  534. {
  535. if ( L'-' == argv[i][0] || L'/' == argv[i][0] )
  536. {
  537. WCHAR wc = (WCHAR) toupper( argv[i][1] );
  538. if ( ':' != argv[i][2] && 'D' != wc )
  539. Usage();
  540. if ( 'C' == wc )
  541. pwcCatalog = argv[i] + 3;
  542. else if ( 'M' == wc )
  543. pwcMachine = argv[i] + 3;
  544. else if ( 'D' == wc )
  545. fDisplayTree = TRUE;
  546. else
  547. Usage();
  548. }
  549. else if ( 0 != pwcRestriction )
  550. Usage();
  551. else
  552. pwcRestriction = argv[i];
  553. }
  554. // A query restriction is necessary. Fail if none is given.
  555. if ( 0 == pwcRestriction )
  556. Usage();
  557. // Run the query
  558. HRESULT hr = DoQuery( pwcCatalog,
  559. pwcMachine,
  560. pwcRestriction,
  561. fDisplayTree );
  562. if ( FAILED( hr ) )
  563. {
  564. printf( "the query '%ws' failed with error %#x\n",
  565. pwcRestriction, hr );
  566. return -1;
  567. }
  568. return 0;
  569. } //wmain