//+------------------------------------------------------------------------- // // 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 - 2001. All Rights Reserved. // // PROGRAM: ci.cxx // // PURPOSE: Illustrates a minimal query using Indexing Service. // Uses CIMakeICommand and CITextToFullTree helper functions. // // PLATFORM: Windows 2000 // //-------------------------------------------------------------------------- #define UNICODE #define DBINITCONSTANTS #include #include #include #include #include #include #include #include #include #include "ci.hxx" // This is found in disptree.cxx extern void DisplayCommandTree( DBCOMMANDTREE * pNode, ULONG iLevel = 0 ); // These are found in isrch.cxx extern HRESULT DoISearch( WCHAR const * pwcRestriction, WCHAR const * pwcFilename, BOOL fPrintFile, BOOL fDefineCPP, LCID lcid, ULONG ulDialect ); extern HINSTANCE PrepareForISearch(); extern void DoneWithISearch( HINSTANCE h ); const ULONG MAX_CATALOGS = 8; // // These properties (func and class) are emitted by the C++ filter (cxxflt.dll) // CIPROPERTYDEF aCPPProperties[] = { { 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" } } }; unsigned cCPPProperties = sizeof aCPPProperties / sizeof aCPPProperties[0]; //+--------------------------------------------------------------------------- // // Class: XBStr // // Purpose: Smart BSTR class // //---------------------------------------------------------------------------- class XBStr { public: XBStr(BSTR p = 0) : _p( p ) {} XBStr ( XBStr & x ): _p( x.Acquire() ) {} ~XBStr() { SysFreeString( _p ); } BOOL IsNull() const { return ( 0 == _p ); } void Set ( BSTR pOleStr ) { _p = pOleStr; } BSTR Acquire() { BSTR pTemp = _p; _p = 0; return pTemp; } BSTR GetPointer() const { return _p; } void Free() { SysFreeString( Acquire() ); } private: BSTR _p; }; //+------------------------------------------------------------------------- // // Template: XInterface // // Synopsis: Template for managing ownership of interfaces // //-------------------------------------------------------------------------- template 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; } BOOL IsNull() { return ( 0 == _p ); } private: T * _p; }; //+------------------------------------------------------------------------- // // Template: XPtr // // Synopsis: Template for managing ownership of memory // //-------------------------------------------------------------------------- template class XPtr { public: XPtr( unsigned c ) : _p(0) { if ( 0 != c ) _p = new T [ c ]; } ~XPtr() { Free(); } void SetSize( unsigned c ) { Free(); _p = new T [ c ]; } void Set ( T * p ) { _p = p; } T * Get() const { return _p ; } void Free() { delete [] Acquire(); } T & operator[]( unsigned i ) { return _p[i]; } T const & operator[]( unsigned i ) const { return _p[i]; } T * Acquire() { T * p = _p; _p = 0; return p; } BOOL IsNull() const { return ( 0 == _p ); } private: T * _p; }; //+------------------------------------------------------------------------- // // Template: CResString // // Synopsis: Class for loading string resources // //-------------------------------------------------------------------------- class CResString { public: CResString() { _awc[ 0 ] = 0; } CResString( UINT strIDS ) { Load( strIDS ); } WCHAR const * Get() const { return _awc; } BOOL Load( UINT strIDS ) { _awc[ 0 ] = 0; LoadString( 0, strIDS, _awc, sizeof _awc / sizeof WCHAR ); return ( 0 != _awc[ 0 ] ); } private: WCHAR _awc[ 200 ]; }; //+------------------------------------------------------------------------- // // Function: FormatError // // Synopsis: Formats an error code into a string // // Arguments: [sc] - An Indexing Service or Win32 HRESULT // [pwc] - Where to write the error string // [cwc] - Count of characters in pwc // [lcid] - Locale for the error string // //-------------------------------------------------------------------------- void FormatError( SCODE sc, WCHAR * pwc, ULONG cwc, LCID lcid ) { // FormatMessage works best when based on thread locale. LCID SaveLCID = GetThreadLocale(); BOOL fLocaleSet = SetThreadLocale( lcid ); // Is this an Indexing Service error? These errors are in query.dll. if ( ! FormatMessage( FORMAT_MESSAGE_FROM_HMODULE, GetModuleHandle( L"query.dll" ), sc, 0, pwc, cwc, 0 ) ) { // Is this a Win32 error? These are in kernel32.dll const ULONG facWin32 = ( FACILITY_WIN32 << 16 ); ULONG Win32Error = sc; if ( (Win32Error & facWin32) == facWin32 ) Win32Error &= ~( 0x80000000 | facWin32 ); if ( ! FormatMessage( FORMAT_MESSAGE_FROM_HMODULE, GetModuleHandle( L"kernel32.dll" ), Win32Error, 0, pwc, cwc, 0 ) ) { // It's not from Indexing Service or Win32; display a default error CResString str( IDS_UNKNOWNERROR ); wcscpy( pwc, str.Get() ); } } // Restore the original thread locale if ( fLocaleSet ) SetThreadLocale( SaveLCID ); } //FormatError //+------------------------------------------------------------------------- // // Function: DisplayError // // Synopsis: Prints an error message from a string resource // // Arguments: [uiError] - The error message resource id // [pwcArgument] - A string argument for the error message // [hr] - The error code // [lcid] - Locale for the error string // //-------------------------------------------------------------------------- HRESULT DisplayError( UINT uiError, WCHAR const * pwcArgument, HRESULT hr, LCID lcid ) { WCHAR awcError[ 200 ]; FormatError( hr, awcError, sizeof awcError / sizeof WCHAR, lcid ); CResString str( uiError ); wprintf( str.Get(), pwcArgument, hr, awcError ); return hr; } //DisplayError //+------------------------------------------------------------------------- // // Function: DisplayWin32Error // // Synopsis: Prints an error message taken from GetLastError() // // Arguments: [uiError] - The string resource to use for the error // [pwcArgument] - A string argument for the error message // [lcid] - Locale for the error string // //-------------------------------------------------------------------------- HRESULT DisplayWin32Error( UINT uiError, WCHAR const * pwcArgument, LCID lcid ) { HRESULT hr = HRESULT_FROM_WIN32( GetLastError() ); DisplayError( uiError, pwcArgument, hr, lcid ); return hr; } //DisplayWin32Error void DisplayStat( DWORD dw, UINT uiMsg ) { CResString str( uiMsg ); wprintf( L"%8d %ws\n", dw, str.Get() ); } //DisplayStat void DisplayStat( WCHAR const *pwcMsg, UINT uiMsg ) { CResString str( uiMsg ); wprintf( L"%ws: %ws\n", str.Get(), pwcMsg ); } //DisplayStat void DisplayStat( UINT uiMsg ) { CResString str( uiMsg ); wprintf( L"%ws\n", str.Get() ); } //DisplayStat //+------------------------------------------------------------------------- // // Function: Usage // // Synopsis: Displays information about how to use the app and exits // //-------------------------------------------------------------------------- void Usage() { HRSRC hrc = FindResource( 0, (LPCWSTR) IDR_USAGE, RT_RCDATA ); if ( 0 != hrc ) { HGLOBAL hg = LoadResource( 0, hrc ); if ( 0 != hg ) { void * pv = LockResource( hg ); if ( 0 != pv ) wprintf( L"%ws\n", pv ); } } exit( -1 ); } //Usage //+------------------------------------------------------------------------- // // Function: LocaleToCodepage // // Synopsis: Finds the best matching codepage given a locale id. // // Arguments: [lcid] - Locale to check // // Returns: The best matching codepage. // //-------------------------------------------------------------------------- ULONG LocaleToCodepage( LCID lcid ) { ULONG codepage; int cwc = GetLocaleInfo( lcid, LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE, (WCHAR *) &codepage, sizeof ULONG / sizeof WCHAR ); // If an error occurred, return the Ansi code page if ( 0 == cwc ) return CP_ACP; return codepage; } //LocaleToCodepage //+------------------------------------------------------------------------- // // Function: GetLocaleString // // Synopsis: Looks up a locale string given an LCID // // Arguments: [lcid] - The lcid to look up // // Returns: The matching string (in a static buffer, caller beware). // //-------------------------------------------------------------------------- WCHAR const * GetLocaleString( LCID lcid ) { static WCHAR awcLocale[ 100 ]; wcscpy( awcLocale, L"Neutral" ); XInterface xMultiLang; HRESULT hr = CoCreateInstance( CLSID_CMultiLanguage, 0, CLSCTX_INPROC_SERVER, IID_IMultiLanguage, xMultiLang.GetQIPointer() ); if ( SUCCEEDED( hr ) ) { BSTR bstrLocale; hr = xMultiLang->GetRfc1766FromLcid( lcid, &bstrLocale ); if ( SUCCEEDED( hr ) ) { wcscpy( awcLocale, bstrLocale ); SysFreeString( bstrLocale ); } } return awcLocale; } //GetLocaleString //+------------------------------------------------------------------------- // // Function: LcidFromHttpAcceptLanguage // // Synopsis: Looks up an LCID given an HTTP Accept Language string // // Arguments: [pwc] - The string to look up // // Returns: The matching LCID. // //-------------------------------------------------------------------------- LCID LcidFromHttpAcceptLanguage( WCHAR const * pwc ) { // Default to the system locale LCID lcid = GetSystemDefaultLCID(); if ( 0 != pwc ) { // Check for an integer constant in hex or decimal, then try as a // string. if ( ( L'0' == pwc[0] ) && ( L'X' == towupper( pwc[1] ) ) ) { int x = wcslen( pwc ); WCHAR *pwcEnd; return wcstoul( pwc, &pwcEnd, 16 ); } else { BOOL fInteger = TRUE; for ( WCHAR const * p = pwc; ( 0 != *p ); p++ ) { if ( !iswdigit( *p ) ) { fInteger = FALSE; break; } } if ( fInteger ) return _wtol( pwc ); } XInterface xMultiLang; HRESULT hr = CoCreateInstance( CLSID_CMultiLanguage, 0, CLSCTX_INPROC_SERVER, IID_IMultiLanguage, xMultiLang.GetQIPointer() ); if ( SUCCEEDED( hr ) ) { BSTR bstr = SysAllocString( pwc ); if ( 0 != bstr ) { hr = xMultiLang->GetLcidFromRfc1766( &lcid, bstr ); SysFreeString( bstr ); if ( S_FALSE == hr || E_FAIL == hr ) { if ( !_wcsicmp( pwc, L"neutral" ) || !_wcsicmp( pwc, L"neutr" ) ) lcid = 0; else Usage(); } else if ( FAILED( hr ) ) { Usage(); } } } } return lcid; } //LcidFromHttpAcceptLanguage //+------------------------------------------------------------------------- // // 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. This is set optionally. // // Both of these properties are unique to Indexing Service's // OLE DB implementation. // // Arguments: [pICommand] - The ICommand used to set the property // [fForceUseContentIndex] - TRUE to always use index // FALSE to allow directory enumeration // [lcid] - The locale of the query // [cMaxHits] - The maximum number of rows to return, 0 for all. // [fFirstHits] - TRUE for first N hits, FALSE for best N hits. // // Returns: HRESULT result of setting the properties // //-------------------------------------------------------------------------- HRESULT SetCommandProperties( ICommand * pICommand, BOOL fForceUseContentIndex, LCID lcid, ULONG cMaxHits, BOOL fFirstHits ) { 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]; // Use extened types in accessor bindings -- it's faster and more accurate 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; // Should we force use of the index? Otherwise, enumeration will be slow. aProp[1] = aProp[0]; aProp[1].dwPropertyID = DBPROP_USECONTENTINDEX; aProp[1].vValue.boolVal = fForceUseContentIndex ? VARIANT_TRUE : VARIANT_FALSE; DBPROPSET aPropSet[3]; aPropSet[0].rgProperties = &aProp[0]; aPropSet[0].cProperties = 2; aPropSet[0].guidPropertySet = guidQueryExt; const GUID guidMSIDXS_ROWSETEXT = DBPROPSET_MSIDXS_ROWSETEXT; DBPROP aRowsetExtProp[1]; aPropSet[1].rgProperties = &aRowsetExtProp[0]; aPropSet[1].cProperties = 1; aPropSet[1].guidPropertySet = guidMSIDXS_ROWSETEXT; // Set the default locale of the query aRowsetExtProp[0] = aProp[0]; aRowsetExtProp[0].dwPropertyID = MSIDXSPROP_COMMAND_LOCALE_STRING; aRowsetExtProp[0].vValue.vt = VT_BSTR; aRowsetExtProp[0].vValue.bstrVal = SysAllocString( GetLocaleString( lcid ) ); if ( 0 == aRowsetExtProp[0].vValue.bstrVal ) return E_OUTOFMEMORY; DBPROP aRowsetProp[1]; aPropSet[2].rgProperties = &aRowsetProp[0]; aPropSet[2].cProperties = 1; aPropSet[2].guidPropertySet = DBPROPSET_ROWSET; // Set the first or best maximum number of result rows aRowsetProp[0] = aProp[0]; aRowsetProp[0].dwPropertyID = fFirstHits ? DBPROP_FIRSTROWS : DBPROP_MAXROWS; aRowsetProp[0].vValue.vt = VT_I4; aRowsetProp[0].vValue.lVal = (LONG) cMaxHits; XInterface xICommandProperties; HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties, xICommandProperties.GetQIPointer() ); if ( SUCCEEDED( hr ) ) { hr = xICommandProperties->SetProperties( 3, aPropSet ); // the properties } // Free the locale string HRESULT hr2 = VariantClear( &aRowsetExtProp[0].vValue ); if ( SUCCEEDED( hr ) && FAILED( hr2 ) ) hr = hr2; return hr; } //SetCommandProperties //+------------------------------------------------------------------------- // // Function: Render // // Synopsis: Prints an item in a safearray // // Arguments: [vt] - type of the element // [pa] - pointer to the item // //-------------------------------------------------------------------------- void PrintSafeArray( VARTYPE vt, LPSAFEARRAY pa ); void Render( VARTYPE vt, void * pv ) { if ( VT_ARRAY & vt ) { PrintSafeArray( vt - VT_ARRAY, *(SAFEARRAY **) pv ); return; } switch ( vt ) { case VT_UI1: wprintf( L"%u", (unsigned) *(BYTE *)pv ); break; case VT_I1: wprintf( L"%d", (int) *(CHAR *)pv ); break; case VT_UI2: wprintf( L"%u", (unsigned) *(USHORT *)pv ); break; case VT_I2: wprintf( L"%d", (int) *(SHORT *)pv ); break; case VT_UI4: case VT_UINT: wprintf( L"%u", (unsigned) *(ULONG *)pv ); break; case VT_I4: case VT_ERROR: case VT_INT: wprintf( L"%d", *(LONG *)pv ); break; case VT_UI8: wprintf( L"%I64u", *(unsigned __int64 *)pv ); break; case VT_I8: wprintf( L"%I64d", *(__int64 *)pv ); break; case VT_R4: wprintf( L"%f", *(float *)pv ); break; case VT_R8: wprintf( L"%lf", *(double *)pv ); break; case VT_DECIMAL: { double dbl; HRESULT hr = VarR8FromDec( (DECIMAL *) pv, &dbl ); if ( SUCCEEDED( hr ) ) wprintf( L"%lf", dbl ); break; } case VT_CY: { double dbl; HRESULT hr = VarR8FromCy( * (CY *) pv, &dbl ); if ( SUCCEEDED( hr ) ) wprintf( L"%lf", dbl ); break; } case VT_BOOL: wprintf( *(VARIANT_BOOL *)pv ? L"TRUE" : L"FALSE" ); break; case VT_BSTR: wprintf( L"%ws", *(BSTR *) pv ); break; case VT_VARIANT: { PROPVARIANT * pVar = (PROPVARIANT *) pv; Render( pVar->vt, & pVar->lVal ); break; } case VT_DATE: { SYSTEMTIME st; BOOL fOK = VariantTimeToSystemTime( *(DATE *)pv, &st ); if ( !fOK ) break; BOOL pm = st.wHour >= 12; if ( st.wHour > 12 ) st.wHour -= 12; else if ( 0 == st.wHour ) st.wHour = 12; wprintf( L"%2d-%02d-%04d %2d:%02d%wc", (DWORD) st.wMonth, (DWORD) st.wDay, (DWORD) st.wYear, (DWORD) st.wHour, (DWORD) st.wMinute, pm ? L'p' : L'a' ); break; } case VT_EMPTY: case VT_NULL: break; default : { wprintf( L"(vt 0x%x)", (int) vt ); break; } } } //Render //+------------------------------------------------------------------------- // // Function: PrintSafeArray // // Synopsis: Prints items in a safearray // // Arguments: [vt] - type of elements in the safearray // [pa] - pointer to the safearray // //-------------------------------------------------------------------------- void PrintSafeArray( VARTYPE vt, LPSAFEARRAY pa ) { // Get the dimensions of the array UINT cDim = SafeArrayGetDim( pa ); if ( 0 == cDim ) return; XPtr xDim( cDim ); XPtr xLo( cDim ); XPtr xUp( cDim ); for ( UINT iDim = 0; iDim < cDim; iDim++ ) { HRESULT hr = SafeArrayGetLBound( pa, iDim + 1, &xLo[iDim] ); if ( FAILED( hr ) ) return; xDim[ iDim ] = xLo[ iDim ]; hr = SafeArrayGetUBound( pa, iDim + 1, &xUp[iDim] ); if ( FAILED( hr ) ) return; wprintf( L"{" ); } // slog through the array UINT iLastDim = cDim - 1; BOOL fDone = FALSE; while ( !fDone ) { // inter-element formatting if ( xDim[ iLastDim ] != xLo[ iLastDim ] ) wprintf( L"," ); // Get the element and render it void *pv; HRESULT hr = SafeArrayPtrOfIndex( pa, xDim.Get(), &pv ); if ( FAILED( hr ) ) return; Render( vt, pv ); // Move to the next element and carry if necessary ULONG cOpen = 0; for ( LONG iDim = iLastDim; iDim >= 0; iDim-- ) { if ( xDim[ iDim ] < xUp[ iDim ] ) { xDim[ iDim ] = 1 + xDim[ iDim ]; break; } wprintf( L"}" ); if ( 0 == iDim ) fDone = TRUE; else { cOpen++; xDim[ iDim ] = xLo[ iDim ]; } } for ( ULONG i = 0; !fDone && i < cOpen; i++ ) wprintf( L"{" ); } } //PrintSafeArray //+------------------------------------------------------------------------- // // Function: PrintVectorItems // // Synopsis: Prints items in a PROPVARIANT vector // // Arguments: [pVal] - The array of values // [cVals] - The count of values // [pcFmt] - The format string // //-------------------------------------------------------------------------- template void PrintVectorItems( T * pVal, ULONG cVals, char * pcFmt ) { printf( "{ " ); for( ULONG iVal = 0; iVal < cVals; iVal++ ) { if ( 0 != iVal ) printf( "," ); printf( pcFmt, *pVal++ ); } printf( " }" ); } //PrintVectorItems //+------------------------------------------------------------------------- // // Function: DisplayValue // // Synopsis: Displays a PROPVARIANT value. Limited formatting is done. // // Arguments: [pVar] - The value to display // //-------------------------------------------------------------------------- void DisplayValue( PROPVARIANT const * pVar ) { if ( 0 == pVar ) { wprintf( L"NULL" ); return; } // Display the most typical variant types PROPVARIANT const & v = *pVar; switch ( v.vt ) { case VT_EMPTY : break; case VT_NULL : break; case VT_I4 : wprintf( L"%10d", v.lVal ); break; case VT_UI1 : wprintf( L"%10d", v.bVal ); break; case VT_I2 : wprintf( L"%10d", v.iVal ); break; case VT_R4 : wprintf( L"%10f", v.fltVal ); break; case VT_R8 : wprintf( L"%10lf", v.dblVal ); break; case VT_BOOL : wprintf( v.boolVal ? L"TRUE" : L"FALSE" ); break; case VT_I1 : wprintf( L"%10d", v.cVal ); break; case VT_UI2 : wprintf( L"%10u", v.uiVal ); break; case VT_UI4 : wprintf( L"%10u", v.ulVal ); break; case VT_INT : wprintf( L"%10d", v.lVal ); break; case VT_UINT : wprintf( L"%10u", v.ulVal ); break; case VT_I8 : wprintf( L"%20I64d", v.hVal ); break; case VT_UI8 : wprintf( L"%20I64u", v.hVal ); break; case VT_ERROR : wprintf( L"%#x", v.scode ); break; case VT_LPSTR : wprintf( L"%S", v.pszVal ); break; case VT_LPWSTR : wprintf( L"%ws", v.pwszVal ); break; case VT_BSTR : wprintf( L"%ws", v.bstrVal ); break; case VT_CY: { double dbl; HRESULT hr = VarR8FromCy( v.cyVal, &dbl ); if ( SUCCEEDED( hr ) ) wprintf( L"%lf", dbl ); break; } case VT_DECIMAL : { double dbl; HRESULT hr = VarR8FromDec( (DECIMAL *) &v.decVal, &dbl ); if ( SUCCEEDED( hr ) ) wprintf( L"%lf", dbl ); break; } case VT_FILETIME : case VT_DATE : { SYSTEMTIME st; if ( VT_DATE == v.vt ) { BOOL fOK = VariantTimeToSystemTime( v.date, &st ); if ( !fOK ) break; } else { FILETIME ft; BOOL fOK = FileTimeToLocalFileTime( &v.filetime, &ft ); if ( fOK ) fOK = FileTimeToSystemTime( &ft, &st ); if ( !fOK ) break; } BOOL pm = st.wHour >= 12; if ( st.wHour > 12 ) st.wHour -= 12; else if ( 0 == st.wHour ) st.wHour = 12; wprintf( L"%2d-%02d-%04d %2d:%02d%wc", (DWORD) st.wMonth, (DWORD) st.wDay, (DWORD) st.wYear, (DWORD) st.wHour, (DWORD) st.wMinute, pm ? L'p' : L'a' ); break; } case VT_VECTOR | VT_I1: PrintVectorItems( v.cac.pElems, v.cac.cElems, "%d" ); break; case VT_VECTOR | VT_I2: PrintVectorItems( v.cai.pElems, v.cai.cElems, "%d" ); break; case VT_VECTOR | VT_I4: PrintVectorItems( v.cal.pElems, v.cal.cElems, "%d" ); break; case VT_VECTOR | VT_I8: PrintVectorItems( v.cah.pElems, v.cah.cElems, "%I64d" ); break; case VT_VECTOR | VT_UI1: PrintVectorItems( v.caub.pElems, v.caub.cElems, "%u" ); break; case VT_VECTOR | VT_UI2: PrintVectorItems( v.caui.pElems, v.caui.cElems, "%u" ); break; case VT_VECTOR | VT_UI4: PrintVectorItems( v.caul.pElems, v.caul.cElems, "%u" ); break; case VT_VECTOR | VT_ERROR: PrintVectorItems( v.cascode.pElems, v.cascode.cElems, "%#x" ); break; case VT_VECTOR | VT_UI8: PrintVectorItems( v.cauh.pElems, v.cauh.cElems, "%I64u" ); break; case VT_VECTOR | VT_BSTR: PrintVectorItems( v.cabstr.pElems, v.cabstr.cElems, "%ws" ); break; case VT_VECTOR | VT_LPSTR: PrintVectorItems( v.calpstr.pElems, v.calpstr.cElems, "%S" ); break; case VT_VECTOR | VT_LPWSTR: PrintVectorItems( v.calpwstr.pElems, v.calpwstr.cElems, "%ws" ); break; case VT_VECTOR | VT_R4: PrintVectorItems( v.caflt.pElems, v.caflt.cElems, "%f" ); break; case VT_VECTOR | VT_R8: PrintVectorItems( v.cadbl.pElems, v.cadbl.cElems, "%lf" ); break; default : { if ( VT_ARRAY & v.vt ) PrintSafeArray( v.vt - VT_ARRAY, v.parray ); else wprintf( L"vt 0x%05x", v.vt ); break; } } } //DisplayValue //----------------------------------------------------------------------------- // // Function: GetOleDBErrorInfo // // Synopsis: Retrieves the secondary error from the OLE DB error object. // // Arguments: [pErrSrc] - Pointer to object that posted the error. // [riid] - Interface that posted the error. // [lcid] - Locale in which the text is desired. // [pErrorInfo] - Pointer to memory where ERRORINFO should be. // [ppIErrorInfo] - Holds the returning IErrorInfo. Caller // should release this. // // Returns: HRESULT for whether the error info was retrieved // //----------------------------------------------------------------------------- HRESULT GetOleDBErrorInfo( IUnknown * pErrSrc, REFIID riid, LCID lcid, ERRORINFO * pErrorInfo, IErrorInfo ** ppIErrorInfo ) { *ppIErrorInfo = 0; // See if an error is available that is of interest to us. XInterface xSupportErrorInfo; HRESULT hr = pErrSrc->QueryInterface( IID_ISupportErrorInfo, xSupportErrorInfo.GetQIPointer() ); if ( FAILED( hr ) ) return hr; hr = xSupportErrorInfo->InterfaceSupportsErrorInfo( riid ); if ( FAILED( hr ) ) return hr; // Get the current error object. Return if none exists. XInterface xErrorInfo; hr = GetErrorInfo( 0, xErrorInfo.GetPPointer() ); if ( xErrorInfo.IsNull() ) return hr; // Get the IErrorRecord interface and get the count of errors. XInterface xErrorRecords; hr = xErrorInfo->QueryInterface( IID_IErrorRecords, xErrorRecords.GetQIPointer() ); if ( FAILED( hr ) ) return hr; ULONG cErrRecords; hr = xErrorRecords->GetRecordCount( &cErrRecords ); if ( 0 == cErrRecords ) return hr; #if 1 // A good way to get the complete error message... XInterface xErrorInfoRec; ERRORINFO ErrorInfo; for ( unsigned i=0; iGetBasicErrorInfo( i, &ErrorInfo ); // Get error description and source through the IErrorInfo interface // pointer on a particular record. xErrorRecords->GetErrorInfo( i, lcid, xErrorInfoRec.GetPPointer() ); XBStr bstrDescriptionOfError; XBStr bstrSourceOfError; BSTR bstrDesc = bstrDescriptionOfError.GetPointer(); BSTR bstrSrc = bstrSourceOfError.GetPointer(); xErrorInfoRec->GetDescription( &bstrDesc ); xErrorInfoRec->GetSource( &bstrSrc ); // At this point, you could call GetCustomErrorObject and query for // additional interfaces to determine what else happened. wprintf( L"%s (%#x)\n%s\n", bstrDesc, ErrorInfo.hrError, bstrSrc ); } #endif // Get basic error information for the most recent error ULONG iRecord = cErrRecords - 1; hr = xErrorRecords->GetBasicErrorInfo( iRecord, pErrorInfo ); if ( FAILED( hr ) ) return hr; return xErrorRecords->GetErrorInfo( iRecord, lcid, ppIErrorInfo ); } //GetOleDBErrorInfo //----------------------------------------------------------------------------- // // Function: DisplayRowsetStatus // // Synopsis: Retrieves status information about the rowset and catalog. // // Arguments: [xIRowset] - Rowset about which information is retrieved. // // Returns: HRESULT result of retrieving the status // //----------------------------------------------------------------------------- HRESULT DisplayRowsetStatus( XInterface & xIRowset ) { XInterface xIRowsetInfo; HRESULT hr = xIRowset->QueryInterface( IID_IRowsetInfo, xIRowsetInfo.GetQIPointer() ); if ( SUCCEEDED( hr ) ) { // This rowset property is Indexing-Service specific DBPROPID propId = MSIDXSPROP_ROWSETQUERYSTATUS; DBPROPIDSET propSet; propSet.rgPropertyIDs = &propId; propSet.cPropertyIDs = 1; const GUID guidRowsetExt = DBPROPSET_MSIDXS_ROWSETEXT; propSet.guidPropertySet = guidRowsetExt; ULONG cPropertySets = 0; DBPROPSET * pPropertySets; hr = xIRowsetInfo->GetProperties( 1, &propSet, &cPropertySets, &pPropertySets ); if ( SUCCEEDED( hr ) ) { DWORD dwStatus = pPropertySets->rgProperties->vValue.ulVal; CoTaskMemFree( pPropertySets->rgProperties ); CoTaskMemFree( pPropertySets ); DWORD dwFill = QUERY_FILL_STATUS( dwStatus ); if ( STAT_ERROR == dwFill ) DisplayStat( IDS_ROWSET_STAT_ERROR ); DWORD dwReliability = QUERY_RELIABILITY_STATUS( dwStatus ); if ( 0 != ( STAT_PARTIAL_SCOPE & dwReliability ) ) DisplayStat( IDS_ROWSET_STAT_PARTIAL_SCOPE ); if ( 0 != ( STAT_NOISE_WORDS & dwReliability ) ) DisplayStat( IDS_ROWSET_STAT_NOISE_WORDS ); if ( 0 != ( STAT_CONTENT_OUT_OF_DATE & dwReliability ) ) DisplayStat( IDS_ROWSET_STAT_CONTENT_OUT_OF_DATE ); if ( 0 != ( STAT_REFRESH_INCOMPLETE & dwReliability ) ) DisplayStat( IDS_ROWSET_STAT_REFRESH_INCOMPLETE ); if ( 0 != ( STAT_CONTENT_QUERY_INCOMPLETE & dwReliability ) ) DisplayStat( IDS_ROWSET_STAT_CONTENT_QUERY_INCOMPLETE ); if ( 0 != ( STAT_TIME_LIMIT_EXCEEDED & dwReliability ) ) DisplayStat( IDS_ROWSET_STAT_TIME_LIMIT_EXCEEDED ); if ( 0 != ( STAT_SHARING_VIOLATION & dwReliability ) ) DisplayStat( IDS_ROWSET_STAT_SHARING_VIOLATION ); } } return hr; } //DisplayRowsetStatus ULONG CountEntries( WCHAR const * pwc, WCHAR wc ) { WCHAR const * p = wcschr( pwc, wc ); ULONG c = 1; while ( 0 != p ) { c++; p++; p = wcschr( p, wc ); } return c; } //CountEntries //+------------------------------------------------------------------------- // // Function: IssueQuery // // 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 // [pwcQueryScope] - Scope of the query // [dwScopeFlags] - Scope flags // [pwcQueryRestrition] - The actual query string // [pwcColumns] - Output column names // [pwcSort] - Sort order names, may be 0 // [fDisplayTree] - TRUE to display the command tree // [fQuiet] - if TRUE, don't display hitcount // [fForceUseContentIndex] - TRUE to always use index // FALSE to allow directory enumeration // [fNoQuery] - if TRUE, just parse and display query // [fSearchHit] - if TRUE invoke hit-hilighting // [ulDialect] - Query dialect (1 or 2) // [cMaxHits] - Maximum # of hits, or 0 for no limit // [fFirstHits] - TRUE for first N or FALSE for best N // [lcid] - Locale for the query // [fDefineCPP] - TRUE to define func and class props // // Returns: HRESULT result of the query // //-------------------------------------------------------------------------- HRESULT IssueQuery( WCHAR const * pwcQueryCatalog, WCHAR const * pwcQueryMachine, WCHAR const * pwcQueryScope, DWORD dwScopeFlags, WCHAR const * pwcQueryRestriction, WCHAR const * pwcColumns, WCHAR const * pwcSort, BOOL fDisplayTree, BOOL fQuiet, BOOL fForceUseContentIndex, BOOL fNoQuery, BOOL fSearchHit, ULONG ulDialect, ULONG cMaxHits, BOOL fFirstHits, LCID lcid, BOOL fDefineCPP ) { // Create an ICommand object. CIMakeICommand is a shortcut for making an // ICommand. The ADVQUERY sample shows the OLE DB equivalent. XInterface xICommand; HRESULT hr; // Handle distributed and single catalog queries if ( ( 0 != wcschr( pwcQueryCatalog, L',' ) ) || ( 0 != wcschr( pwcQueryMachine, L',' ) ) ) { ULONG cCat = CountEntries( pwcQueryCatalog, L',' ); ULONG cMac = CountEntries( pwcQueryMachine, L',' ); if ( ( ( cCat != cMac ) && ( 1 != cCat ) && ( 1 != cMac ) ) || ( cCat > MAX_CATALOGS || cMac > MAX_CATALOGS ) ) Usage(); WCHAR awcCat[ MAX_PATH ]; wcscpy( awcCat, pwcQueryCatalog ); WCHAR awcMac[ MAX_PATH ]; wcscpy( awcMac, pwcQueryMachine ); WCHAR * aCat[ MAX_CATALOGS ]; WCHAR * aMac[ MAX_CATALOGS ]; WCHAR const * aSco[ MAX_CATALOGS ]; DWORD aFla[ MAX_CATALOGS ]; WCHAR * pwcCat = awcCat; WCHAR * pwcMac = awcMac; for ( ULONG i = 0; i < __max( cCat, cMac ); i++ ) { aFla[i] = dwScopeFlags; aSco[i] = pwcQueryScope; aMac[i] = pwcMac; if ( 1 != cMac ) { pwcMac = wcschr( pwcMac, L',' ); if ( 0 != pwcMac ) *pwcMac++ = 0; } aCat[i] = pwcCat; if ( 1 != cCat ) { pwcCat = wcschr( pwcCat, L',' ); if ( 0 != pwcCat ) *pwcCat++ = 0; } } hr = CIMakeICommand( xICommand.GetPPointer(), cCat, aFla, aSco, aCat, aMac ); } else { hr = CIMakeICommand( xICommand.GetPPointer(), // result 1, // 1 scope &dwScopeFlags, // scope flags &pwcQueryScope, // scope path &pwcQueryCatalog, // catalog &pwcQueryMachine ); // machine } if ( FAILED( hr ) ) return hr; // Set required properties on the ICommand hr = SetCommandProperties( xICommand.GetPointer(), fForceUseContentIndex, lcid, cMaxHits, fFirstHits ); if ( FAILED( hr ) ) return hr; // Get a command tree object XInterface xICommandTree; hr = xICommand->QueryInterface( IID_ICommandTree, xICommandTree.GetQIPointer() ); if ( FAILED( hr ) ) return hr; if ( ulDialect < 3 ) { // Create an OLE DB query tree based on query parameters. DBCOMMANDTREE * pTree; ULONG cDefinedProperties = fDefineCPP ? cCPPProperties : 0; hr = CITextToFullTreeEx( pwcQueryRestriction, // the query itself ulDialect, // query dialect pwcColumns, // columns to return pwcSort, // sort order, may be 0 0, // reserved &pTree, // resulting tree cDefinedProperties, // C++ properties aCPPProperties, // C++ properties lcid ); // default locale if ( FAILED( hr ) ) return hr; // If directed, display the command tree. if ( fDisplayTree ) { wprintf( L"%ws\n", pwcQueryRestriction ); DisplayCommandTree( pTree ); } // If directed, don't issue the query. Parsing it was sufficient. if ( fNoQuery ) { xICommandTree->FreeCommandTree( &pTree ); return S_OK; } // Set the tree in the ICommandTree. Ownership of the tree is transferred. hr = xICommandTree->SetCommandTree( &pTree, DBCOMMANDREUSE_NONE, FALSE ); if ( FAILED( hr ) ) { xICommandTree->FreeCommandTree( &pTree ); return hr; } } else { // Get a command text object XInterface xICommandText; hr = xICommand->QueryInterface( IID_ICommandText, xICommandText.GetQIPointer() ); if ( FAILED( hr ) ) return hr; hr = xICommandText->SetCommandText( DBGUID_SQL, pwcQueryRestriction ); if ( FAILED( hr ) ) return hr; // If directed, display the command tree. if ( fDisplayTree ) { DBCOMMANDTREE * pTree = 0; hr = xICommandTree->GetCommandTree( &pTree ); if ( FAILED( hr ) ) return hr; wprintf( L"%ws\n", pwcQueryRestriction ); DisplayCommandTree( pTree ); } // If directed, don't issue the query. Parsing it was sufficient. if ( fNoQuery ) return S_OK; } // Execute the query. The query is complete when Execute() returns. XInterface 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 ) ) { // Get the real error; OLE DB permits few Execute() return codes ERRORINFO ErrorInfo; XInterface xErrorInfo; HRESULT hr2 = GetOleDBErrorInfo( xICommand.GetPointer(), IID_ICommand, lcid, &ErrorInfo, xErrorInfo.GetPPointer() ); // Post IErrorInfo only if we have a valid pointer to it. if ( SUCCEEDED( hr2 ) && !xErrorInfo.IsNull() ) hr = ErrorInfo.hrError; return hr; } // Get the count of columns XInterface xColumnsInfo; hr = xIRowset->QueryInterface( IID_IColumnsInfo, xColumnsInfo.GetQIPointer() ); if ( FAILED( hr ) ) return hr; DBCOLUMNINFO *pColumnInfo = 0; WCHAR *pColumnNames = 0; DBORDINAL cColumns = 0; hr = xColumnsInfo->GetColumnInfo( &cColumns, &pColumnInfo, &pColumnNames ); if ( FAILED( hr ) ) return hr; // If bookmark was added as column 0, ignore it if ( 0 != ( pColumnInfo[0].dwFlags & DBCOLUMNFLAGS_ISBOOKMARK ) ) cColumns--; CoTaskMemFree( pColumnInfo ); CoTaskMemFree( pColumnNames ); // Create an accessor, so data can be retrieved from the rowset. XInterface 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. XPtr xBindings( (ULONG) cColumns ); if ( xBindings.IsNull() ) return E_OUTOFMEMORY; memset( xBindings.Get(), 0, sizeof DBBINDING * cColumns ); for ( ULONG i = 0; i < cColumns; i++ ) { xBindings[i].iOrdinal = 1 + i; // 1-based column number xBindings[i].obValue = i * sizeof( PROPVARIANT * ); // offset xBindings[i].dwPart = DBPART_VALUE; // retrieve value, not status xBindings[i].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // provider owned xBindings[i].wType = DBTYPE_VARIANT | DBTYPE_BYREF; // VARIANT * } HACCESSOR hAccessor; hr = xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, // rowdata accessor cColumns, // # of columns xBindings.Get(), // columns 0, // ignored &hAccessor, // result 0 ); // no status if ( FAILED( hr ) ) return hr; DBORDINAL iPathColumn = ~0; if ( fSearchHit ) { const DBID dbcolPath = { PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) (ULONG_PTR) PID_STG_PATH }; hr = xColumnsInfo->MapColumnIDs( 1, & dbcolPath, & iPathColumn ); if ( FAILED( hr ) ) return hr; // Change from 1-based to 0-based. iPathColumn--; } // Display the results of the query. XPtr xData( (ULONG) cColumns ); if ( xData.IsNull() ) hr = E_OUTOFMEMORY; else { DBCOUNTITEM cRowsSoFar = 0; do { DBCOUNTITEM cRowsReturned = 0; const ULONG cRowsAtATime = 20; 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++ ) { HRESULT hr2 = xIRowset->GetData( aHRow[iRow], // hrow being accessed hAccessor, // accessor to use xData.Get() ); // resulting data if ( FAILED( hr2 ) ) { hr = hr2; break; } if ( ( 1 != cColumns ) || !fSearchHit ) { for ( ULONG iCol = 0; iCol < cColumns; iCol++ ) { if ( 0 != iCol ) wprintf( L" " ); DisplayValue( xData[ iCol ] ); } wprintf( L"\n" ); } if ( fSearchHit ) { PROPVARIANT * pPropVar = xData[ (unsigned int)iPathColumn ]; if ( ( VT_LPWSTR == pPropVar->vt ) && ( 0 != pPropVar->pwszVal ) ) { DoISearch( pwcQueryRestriction, pPropVar->pwszVal, ( 1 == cColumns ), fDefineCPP, lcid, ulDialect ); } } } // Release the HROWs retrived in GetNextRows if ( 0 != cRowsReturned ) { cRowsSoFar += cRowsReturned; xIRowset->ReleaseRows( cRowsReturned, // # of rows to release aHRow, // rows to release 0, // no options 0, // no refcounts 0 ); // no status } // Check if all rows are now retrieved. if ( DB_S_ENDOFROWSET == hr || DB_S_ROWLIMITEXCEEDED == hr ) { hr = S_OK; // succeeded, return S_OK from DoQuery break; } // Check if the query aborted because it was too costly. if ( DB_S_STOPLIMITREACHED == hr ) { CResString str( IDS_QUERYTIMEDOUT ); wprintf( L"%ws\n", str.Get() ); hr = S_OK; break; } if ( FAILED( hr ) ) break; } while ( TRUE ); if ( !fQuiet ) { CResString str( IDS_QUERYDONE ); wprintf( str.Get(), cRowsSoFar, pwcQueryRestriction ); } } xIAccessor->ReleaseAccessor( hAccessor, 0 ); // Get query status information if ( SUCCEEDED( hr ) && !fQuiet ) hr = DisplayRowsetStatus( xIRowset ); return hr; } //IssueQuery //+------------------------------------------------------------------------- // // Function: DoQuery // // Synopsis: Issues a query and displays an error message on failure // // Arguments: [pwcQueryCatalog] - Catalog name over which query is run // [pwcQueryMachine] - Machine name on which query is run // [pwcQueryScope] - Scope of the query // [dwScopeFlags] - Scope flags // [pwcQueryRestrition] - The actual query string // [pwcColumns] - Output column names // [pwcSort] - Sort order names, may be 0 // [fDisplayTree] - TRUE to display the command tree // [fQuiet] - if TRUE, don't display hitcount // [fForceUseContentIndex] - TRUE to always use index // FALSE to allow directory enumeration // [fNoQuery] - if TRUE, just parse and display query // [fSearchHit] - if TRUE invoke isrchdmp.exe // [ulDialect] - Query dialect (1 or 2) // [cMaxHits] - Maximum # of hits, or 0 for no limit // [fFirstHits] - TRUE for first N or FALSE for best N // [lcid] - Locale for the query // [fDefineCPP] - TRUE to define func and class props // // Returns: HRESULT result of the query // //-------------------------------------------------------------------------- HRESULT DoQuery( WCHAR const * pwcCatalog, WCHAR const * pwcMachine, WCHAR const * pwcScope, DWORD dwScopeFlags, WCHAR const * pwcRestriction, WCHAR const * pwcColumns, WCHAR const * pwcSort, BOOL fDisplayTree, BOOL fQuiet, BOOL fForceUseContentIndex, BOOL fNoQuery, BOOL fSearchHit, ULONG ulDialect, ULONG cMaxHits, BOOL fFirstHits, LCID lcid, BOOL fDefineCPP ) { HRESULT hr = IssueQuery( pwcCatalog, pwcMachine, pwcScope, dwScopeFlags, pwcRestriction, pwcColumns, pwcSort, fDisplayTree, fQuiet, fForceUseContentIndex, fNoQuery, fSearchHit, ulDialect, cMaxHits, fFirstHits, lcid, fDefineCPP ); if ( FAILED( hr ) ) DisplayError( IDS_QUERYFAILED, pwcRestriction, hr, lcid ); return hr; } //DoQuery //+------------------------------------------------------------------------- // // Function: DoQueryFile // // Synopsis: Issues each query in the specified query file. A query file // is just a text file where each line contains a query. // // Arguments: [pwcQueryCatalog] - Catalog name over which query is run // [pwcQueryMachine] - Machine name on which query is run // [pwcQueryScope] - Scope of the query // [dwScopeFlags] - Scope flags // [pwcColumns] - Output column names // [pwcSort] - Sort order names, may be 0 // [fDisplayTree] - TRUE to display the command tree // [fQuiet] - if TRUE, don't display hitcount // [fForceUseContentIndex] - TRUE to always use index // FALSE to allow directory enumeration // [fNoQuery] - if TRUE, just parse and display query // [fSearchHit] - if TRUE invoke isrchdmp.exe // [ulDialect] - Query dialect (1 or 2) // [cMaxHits] - Maximum # of hits, or 0 for no limit // [fFirstHits] - TRUE for first N or FALSE for best N // [lcid] - Locale for the query // [pwcQueryFile] - File containing queries, 1 per line // [fDefineCPP] - TRUE to define func and class props // // Returns: HRESULT result of the query // //-------------------------------------------------------------------------- HRESULT DoQueryFile( WCHAR const * pwcQueryCatalog, WCHAR const * pwcQueryMachine, WCHAR const * pwcQueryScope, DWORD dwScopeFlags, WCHAR const * pwcColumns, WCHAR const * pwcSort, BOOL fDisplayTree, BOOL fQuiet, BOOL fForceUseContentIndex, BOOL fNoQuery, BOOL fSearchHit, ULONG ulDialect, ULONG cMaxHits, BOOL fFirstHits, LCID lcid, WCHAR const * pwcQueryFile, BOOL fDefineCPP ) { // Open and read the query file HANDLE hFile = CreateFile( pwcQueryFile, FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0 ); if ( 0 == hFile || INVALID_HANDLE_VALUE == hFile ) return DisplayWin32Error( IDS_CANTOPENFILE, pwcQueryFile, lcid ); DWORD cbFile = GetFileSize( hFile, 0 ); if ( 0xffffffff == cbFile ) { CloseHandle( hFile ); return DisplayWin32Error( IDS_CANTGETFILESIZE, pwcQueryFile, lcid ); } // Allocate a buffer for the file XPtr xQueries( cbFile + sizeof WCHAR ); if ( xQueries.IsNull() ) { CloseHandle( hFile ); SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return DisplayWin32Error( IDS_CANTGETMEMORY, pwcQueryFile, lcid ); } // Read the file into the buffer DWORD cbRead; BOOL fRead = ReadFile( hFile, xQueries.Get(), cbFile, &cbRead, 0 ); CloseHandle( hFile ); if ( ! fRead ) return DisplayWin32Error( IDS_CANTREADFROMFILE, pwcQueryFile, lcid ); if ( cbRead != cbFile ) { SetLastError( ERROR_INVALID_DATA ); return DisplayWin32Error( IDS_CANTREADFROMFILE, pwcQueryFile, lcid ); } // Check if the file is Unicode already BOOL fUnicode = FALSE; if ( cbRead >= 2 ) fUnicode = ( 0xfeff == ( * (WCHAR *) xQueries.Get() ) ); WCHAR * pwcIn = 0; DWORD cwcIn = 0; if ( fUnicode ) { pwcIn = (WCHAR *) xQueries.Get(); // skip past the Unicode marker pwcIn++; cwcIn = ( cbFile / sizeof WCHAR ) - 1; } else { // Convert to Unicode. Leave a little room for slack. DWORD cbTmp = cbFile * sizeof WCHAR + cbFile / 8; XPtr xTmp( cbTmp + sizeof WCHAR ); if ( xTmp.IsNull() ) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return DisplayWin32Error( IDS_CANTGETMEMORY, pwcQueryFile, lcid ); } cwcIn = MultiByteToWideChar( LocaleToCodepage( lcid ), 0, (const char *) xQueries.Get(), cbFile, (WCHAR *) xTmp.Get(), cbTmp ); if ( 0 == cwcIn ) return DisplayWin32Error( IDS_CANTCONVERTTOUNICODE, pwcQueryFile, lcid ); pwcIn = (WCHAR *) xTmp.Get(); xQueries.Free(); xQueries.Set( xTmp.Acquire() ); } // Read each line in the file and issue the query pwcIn[ cwcIn ] = 0; WCHAR * pwc = pwcIn; do { while ( 0 != *pwcIn && L'\r' != *pwcIn && L'\n' != *pwcIn ) pwcIn++; BOOL fEOF = ( 0 == *pwcIn ); *pwcIn = 0; if ( pwc != pwcIn ) { DoQuery( pwcQueryCatalog, pwcQueryMachine, pwcQueryScope, dwScopeFlags, pwc, pwcColumns, pwcSort, fDisplayTree, fQuiet, fForceUseContentIndex, fNoQuery, fSearchHit, ulDialect, cMaxHits, fFirstHits, lcid, fDefineCPP ); wprintf( L"\n\n" ); } if ( fEOF ) break; pwcIn++; while ( '\r' == *pwcIn || '\n' == *pwcIn ) pwcIn++; pwc = pwcIn; } while ( TRUE ); return S_OK; } //DoQueryFile //+------------------------------------------------------------------------- // // Function: LookupCatalog // // Synopsis: Looks for a catalog and machine matching the scope // // Arguments: [pwcScope] - The scope used to find the catalog // [pwcMachine] - Returns the machine name // [cwcMachine] - In/Out: Count of characters in pwcMachine // [pwcCatalog] - Returns the catalog name // [cwcCatalog] - In/Out: Count of characters in pwcCatalog // [lcid] - Locale to use for errors // //-------------------------------------------------------------------------- HRESULT LookupCatalog( WCHAR const * pwcScope, WCHAR * pwcMachine, ULONG & cwcMachine, WCHAR * pwcCatalog, ULONG & cwcCatalog, LCID lcid ) { HRESULT hr = LocateCatalogs( pwcScope, // scope to lookup 0, // go with the first match pwcMachine, // returns the machine &cwcMachine, // buffer size in/out pwcCatalog, // returns the catalog &cwcCatalog ); // buffer size in/out if ( FAILED( hr ) || ( S_FALSE == hr ) ) { DisplayError( IDS_CANTFINDCATALOG, pwcScope, hr, lcid ); hr = E_FAIL; } return hr; } //LookupCatalog //+------------------------------------------------------------------------- // // Function: NormalizeScope // // Synopsis: Normalizes a scope and sets scope flags. // // Arguments: [pwcIn] - The scope for the query // [pwcOut] - Returns the scope for the query // [dwScopeFlags] - Returns the scope flags for the query // //-------------------------------------------------------------------------- HRESULT NormalizeScope( WCHAR const * pwcIn, WCHAR * pwcOut, BOOL fShallow, DWORD & dwScopeFlags ) { if ( wcslen( pwcIn ) >= MAX_PATH ) return E_INVALIDARG; if ( fShallow ) dwScopeFlags = QUERY_SHALLOW; else dwScopeFlags = QUERY_DEEP; wcscpy( pwcOut, pwcIn ); // Check if the scope is an IIS virtual scope. WCHAR wc = pwcIn[0]; if ( L'/' == wc ) { // Set the virtual scope flag and flip the slashes. dwScopeFlags |= QUERY_VIRTUAL_PATH; for ( WCHAR * pwc = pwcOut; *pwc; pwc++ ) if ( '/' == *pwc ) *pwc = '\\'; } else if ( ( !( L'\\' == wc && L'\\' == pwcIn[1] ) ) && ( !( L'\\' == wc && 0 == pwcIn[1] ) ) && L':' != pwcIn[1] && 0 != wc ) { // Turn the relative path into a full path based on the current dir. _wfullpath( pwcOut, pwcIn, MAX_PATH ); } return S_OK; } //NormalizeScope //+------------------------------------------------------------------------- // // Function: DisplayStatus // // Synopsis: Displays status information about a catalog // // Arguments: [pwcCatalog] - Catalog name // [pwcMachine] - Machine on which catalog resides // [lcid] - Locale to use // //-------------------------------------------------------------------------- HRESULT DisplayStatus( WCHAR const * pwcCatalog, WCHAR const * pwcMachine, LCID lcid ) { CI_STATE state; state.cbStruct = sizeof state; DisplayStat( pwcMachine, IDS_STAT_MACHINE ); DisplayStat( pwcCatalog, IDS_STAT_CATALOG ); HRESULT hr = CIState( pwcCatalog, pwcMachine, &state ); if ( SUCCEEDED( hr ) ) { DisplayStat( state.cTotalDocuments, IDS_STAT_TOTALDOCUMENTS ); DisplayStat( state.cFreshTest, IDS_STAT_FRESHTEST ); DisplayStat( state.cFilteredDocuments, IDS_STAT_FILTEREDDOCUMENTS ); DisplayStat( state.cDocuments, IDS_STAT_DOCUMENTS ); DisplayStat( state.cSecQDocuments, IDS_STAT_SECQDOCUMENTS ); DisplayStat( state.cUniqueKeys, IDS_STAT_UNIQUEKEYS ); DisplayStat( state.cWordList, IDS_STAT_WORDLIST ); DisplayStat( state.cPersistentIndex, IDS_STAT_PERSISTENTINDEX ); DisplayStat( state.cQueries, IDS_STAT_QUERIES ); DisplayStat( state.dwIndexSize, IDS_STAT_INDEXSIZE ); DisplayStat( state.dwPropCacheSize / 1024, IDS_STAT_PROPCACHESIZE ); DisplayStat( ( state.eState & CI_STATE_SCANNING ) ? state.cPendingScans : 0, IDS_STAT_SCANS ); const DWORD ALL_CI_MERGE = ( CI_STATE_SHADOW_MERGE | CI_STATE_ANNEALING_MERGE | CI_STATE_INDEX_MIGRATION_MERGE | CI_STATE_MASTER_MERGE | CI_STATE_MASTER_MERGE_PAUSED ); if ( 0 != ( ALL_CI_MERGE & state.eState ) ) { UINT idStr; if ( state.eState & CI_STATE_SHADOW_MERGE ) idStr = IDS_STAT_MERGE_SHADOW; else if ( state.eState & CI_STATE_ANNEALING_MERGE ) idStr = IDS_STAT_MERGE_ANNEALING; else if ( state.eState & CI_STATE_INDEX_MIGRATION_MERGE ) idStr = IDS_STAT_MERGE_INDEX_MIGRATION; else if ( state.eState & CI_STATE_MASTER_MERGE ) idStr = IDS_STAT_MERGE_MASTER; else idStr = IDS_STAT_MERGE_MASTER_PAUSED; DisplayStat( state.dwMergeProgress, idStr ); } if ( CI_STATE_READ_ONLY & state.eState ) DisplayStat( IDS_STAT_READ_ONLY ); if ( CI_STATE_RECOVERING & state.eState ) DisplayStat( IDS_STAT_RECOVERING ); if ( CI_STATE_LOW_MEMORY & state.eState ) DisplayStat( IDS_STAT_LOW_MEMORY ); if ( CI_STATE_HIGH_IO & state.eState ) DisplayStat( IDS_STAT_HIGH_IO ); if ( CI_STATE_BATTERY_POWER & state.eState ) DisplayStat( IDS_STAT_BATTERY_POWER ); if ( CI_STATE_USER_ACTIVE & state.eState ) DisplayStat( IDS_STAT_USER_ACTIVE ); if ( CI_STATE_STARTING & state.eState ) DisplayStat( IDS_STAT_STARTING ); if ( CI_STATE_READING_USNS & state.eState ) DisplayStat( IDS_STAT_READING_USNS ); } else { DisplayError( IDS_CANTDISPLAYSTATUS, pwcCatalog, hr, lcid ); } return hr; } //DisplayStatus //+------------------------------------------------------------------------- // // Function: ForceMerge // // Synopsis: Forces a master merge on the catalog // // Arguments: [pwcCatalog] - Catalog name // [pwcMachine] - Machine on which catalog resides // [lcid] - Locale to use // //-------------------------------------------------------------------------- HRESULT ForceMerge( WCHAR const * pwcCatalog, WCHAR const * pwcMachine, LCID lcid ) { // Create the main Indexing Service administration object. CLSID clsid; HRESULT hr = CLSIDFromProgID( L"Microsoft.ISAdm", &clsid ); if ( FAILED( hr ) ) return DisplayError( IDS_CANTFORCEMERGE, pwcCatalog, hr, lcid ); XInterface xAdmin; hr = CoCreateInstance( clsid, 0, CLSCTX_INPROC_SERVER, __uuidof(IAdminIndexServer), xAdmin.GetQIPointer() ); if ( FAILED( hr ) ) return DisplayError( IDS_CANTFORCEMERGE, pwcCatalog, hr, lcid ); // Set the machine name. BSTR bstrMachine = SysAllocString( pwcMachine ); if ( 0 == bstrMachine ) return DisplayError( IDS_CANTFORCEMERGE, pwcCatalog, E_OUTOFMEMORY, lcid ); XBStr xbstr( bstrMachine ); hr = xAdmin->put_MachineName( bstrMachine ); if ( FAILED( hr ) ) return DisplayError( IDS_CANTFORCEMERGE, pwcCatalog, hr, lcid ); // Get a catalog administration object. BSTR bstrCatalog = SysAllocString( pwcCatalog ); if ( 0 == bstrCatalog ) return DisplayError( IDS_CANTFORCEMERGE, pwcCatalog, E_OUTOFMEMORY, lcid ); xbstr.Free(); xbstr.Set( bstrCatalog ); XInterface xCatAdmin; hr = xAdmin->GetCatalogByName( bstrCatalog, (IDispatch **) xCatAdmin.GetQIPointer() ); if ( FAILED( hr ) ) return DisplayError( IDS_CANTFORCEMERGE, pwcCatalog, hr, lcid ); // Force the merge. hr = xCatAdmin->ForceMasterMerge(); if ( FAILED( hr ) ) return DisplayError( IDS_CANTFORCEMERGE, pwcCatalog, hr, lcid ); return hr; } //ForceMerge //+------------------------------------------------------------------------- // // Function: DisplayUpToDate // // Synopsis: Checks if the index is up to date. // // Arguments: [pwcCatalog] - Catalog name // [pwcMachine] - Machine on which catalog resides // [lcid] - Locale to use // //-------------------------------------------------------------------------- HRESULT DisplayUpToDate( WCHAR const * pwcCatalog, WCHAR const * pwcMachine, LCID lcid ) { CI_STATE state; state.cbStruct = sizeof state; HRESULT hr = CIState( pwcCatalog, pwcMachine, &state ); if ( SUCCEEDED( hr ) ) { // It's up to date if there are no documents to filter, no scans or // usn activity, and the index isn't starting or recovering. BOOL fUpToDate = ( ( 0 == state.cDocuments ) && ( 0 == ( state.eState & CI_STATE_SCANNING ) ) && ( 0 == ( state.eState & CI_STATE_READING_USNS ) ) && ( 0 == ( state.eState & CI_STATE_STARTING ) ) && ( 0 == ( state.eState & CI_STATE_RECOVERING ) ) ); DisplayStat( fUpToDate ? IDS_STAT_UP_TO_DATE : IDS_STAT_NOT_UP_TO_DATE ); } else { DisplayError( IDS_CANTDISPLAYSTATUS, pwcCatalog, hr, lcid ); } return hr; } //DisplayUpToDate //+------------------------------------------------------------------------- // // 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 = 0; // default: lookup catalog WCHAR const * pwcMachine = L"."; // default: local machine WCHAR const * pwcScope = L"\\"; // default: entire catalog WCHAR const * pwcRestriction = 0; // no default restriction WCHAR const * pwcColumns = L"path"; // default output column(s) WCHAR const * pwcSort = 0; // no sort is the default WCHAR const * pwcQueryFile = 0; // no query file specified WCHAR const * pwcLocale = 0; // default: system locale BOOL fDisplayTree = FALSE; // don't display the tree BOOL fForceUseContentIndex = TRUE; // always use the index ULONG ulDialect = 1; // original query language dialect BOOL fQuiet = FALSE; // show the hitcount ULONG cMaxHits = 0; // default: retrieve all hits BOOL fFirstHits = FALSE; // First vs Best N hits BOOL fDisplayStatus = FALSE; // default: don't show status BOOL fDisplayUpToDate = FALSE; // default: don't show up to date ULONG cRepetitions = 1; // # of times to repeat command BOOL fShallow = FALSE; // default: all subdirectories BOOL fNoQuery = FALSE; // default: execute query BOOL fSearchHit = FALSE; // default: don't isrchdmp.exe BOOL fDefineCPP = FALSE; // default: don't define props BOOL fForceMerge = FALSE; // default: don't force a MM BOOL fSmart = FALSE; // default: use all the options provided // Parse command line parameters for ( int i = 1; i < argc; i++ ) { if ( L'-' == argv[i][0] || L'/' == argv[i][0] ) { WCHAR wc = (WCHAR) toupper( (char) argv[i][1] ); if ( ':' != argv[i][2] && 'D' != wc && 'G' != wc && 'H' != wc && 'J' != wc && 'N' != wc && 'Q' != wc && 'U' != wc && 'S' != wc && 'T' != wc && 'Z' != wc ) Usage(); if ( 'C' == wc ) { pwcCatalog = argv[i] + 3; if ( wcslen( pwcCatalog ) >= MAX_PATH ) Usage(); } else if ( 'M' == wc ) { pwcMachine = argv[i] + 3; if ( wcslen( pwcMachine ) >= MAX_PATH ) Usage(); } else if ( 'P' == wc ) pwcScope = argv[i] + 3; else if ( 'O' == wc ) pwcColumns = argv[i] + 3; else if ( 'S' == wc ) { if ( _wcsicmp (argv[i]+1, L"smart") == 0 ) { fSmart = TRUE; } else { if (argv[i][2] != L':') Usage(); pwcSort = argv[i] + 3; } } else if ( 'X' == wc ) cMaxHits = _wtoi( argv[i] + 3 ); else if ( 'Y' == wc ) { cMaxHits = _wtoi( argv[i] + 3 ); fFirstHits = TRUE; } else if ( 'I' == wc ) { if ( 0 != pwcRestriction ) Usage(); pwcQueryFile = argv[i] + 3; } else if ( 'R' == wc) { // get the next arg as a number cRepetitions = _wtol(argv[i]+3); } else if ( 'D' == wc ) fDisplayTree = TRUE; else if ( 'G' == wc ) fForceMerge = TRUE; else if ( 'H' == wc ) fSearchHit = TRUE; else if ( 'J' == wc ) fShallow = TRUE; else if ( 'N' == wc ) fNoQuery = TRUE; else if ( 'Q' == wc ) fQuiet = TRUE; else if ( 'T' == wc ) fDisplayStatus = TRUE; else if ( 'U' == wc ) fDisplayUpToDate = TRUE; else if ( 'E' == wc ) pwcLocale = argv[i] + 3; else if ( 'L' == wc ) { if ( '1' == argv[i][3] ) ulDialect = 1; else if ( '2' == argv[i][3] ) ulDialect = 2; else if ( '3' == argv[i][3] ) ulDialect = 3; else Usage(); } else if ( 'F' == wc ) { if ( '+' == argv[i][3] ) fForceUseContentIndex = TRUE; else if ( '-' == argv[i][3] ) fForceUseContentIndex = FALSE; else Usage(); } else if ( 'Z' == wc ) fDefineCPP = TRUE; else Usage(); } else if ( 0 != pwcRestriction || 0 != pwcQueryFile ) Usage(); else pwcRestriction = argv[i]; } // A query restriction, query file, or status request is necessary. if ( 0 == pwcRestriction && 0 == pwcQueryFile && !fDisplayStatus && !fDisplayUpToDate && !fForceMerge ) Usage(); if ( fSmart ) { // If we're in smart mode, don't let people specify machine, path, or catalog if ( ( pwcCatalog != 0 ) || ( _wcsicmp( pwcMachine, L"." ) != 0 ) || ( _wcsicmp( pwcScope, L"\\" ) != 0 ) ) { Usage(); } // Fix the scope, to figure out the catalog, but we'll reset it later. // We set it to the current directory now, which will be expanded to the // full path we're sitting in, so that we'll find the right catalog. pwcScope = L"."; } // Normalize relative and virtual scopes WCHAR awcScope[ MAX_PATH ]; DWORD dwScopeFlags; HRESULT hr = NormalizeScope( pwcScope, awcScope, fShallow, dwScopeFlags ); // Initialize OLE BOOL fCoInit = FALSE; if ( SUCCEEDED( hr ) ) { hr = CoInitialize( 0 ); if ( SUCCEEDED( hr ) ) fCoInit = TRUE; } if ( FAILED( hr ) ) return -1; // Get the locale identifier to use for the query LCID lcid = LcidFromHttpAcceptLanguage( pwcLocale ); HINSTANCE hISearch = 0; if ( fSearchHit ) { hISearch = PrepareForISearch(); if ( 0 == hISearch ) Usage(); } // If no catalog was specified, infer one based on the scope WCHAR awcMachine[ MAX_PATH ], awcCatalog[ MAX_PATH ]; if ( SUCCEEDED( hr ) && ( 0 == pwcCatalog ) && !fNoQuery ) { ULONG cwcMachine = sizeof awcMachine / sizeof WCHAR; ULONG cwcCatalog = sizeof awcCatalog / sizeof WCHAR; hr = LookupCatalog( awcScope, awcMachine, cwcMachine, awcCatalog, cwcCatalog, lcid ); pwcMachine = awcMachine; pwcCatalog = awcCatalog; // Turn scopes like \\machine into \ now that the lookup is done // and we've found a catalog and machine name. if ( SUCCEEDED( hr ) && L'\\' == awcScope[0] && L'\\' == awcScope[1] && 0 == wcschr( awcScope + 2, L'\\' ) ) awcScope[1] = 0; } if ( fSmart ) { // Now that we've got the catalog, if we're in smart mode, put // the scope back to what it was. awcScope[0] = L'\\'; awcScope[1] = L'\0'; } if ( SUCCEEDED( hr ) ) { for (ULONG j = 0; j < cRepetitions; j++) { if ( 0 != pwcQueryFile ) hr = DoQueryFile( fNoQuery ? L"::_noquery" : pwcCatalog, pwcMachine, awcScope, dwScopeFlags, pwcColumns, pwcSort, fDisplayTree, fQuiet, fForceUseContentIndex, fNoQuery, fSearchHit, ulDialect, cMaxHits, fFirstHits, lcid, pwcQueryFile, fDefineCPP ); else if ( 0 != pwcRestriction ) hr = DoQuery( fNoQuery ? L"::_noquery" : pwcCatalog, pwcMachine, awcScope, dwScopeFlags, pwcRestriction, pwcColumns, pwcSort, fDisplayTree, fQuiet, fForceUseContentIndex, fNoQuery, fSearchHit, ulDialect, cMaxHits, fFirstHits, lcid, fDefineCPP ); if ( SUCCEEDED( hr ) && fForceMerge ) hr = ForceMerge( pwcCatalog, pwcMachine, lcid ); if ( SUCCEEDED( hr ) && fDisplayStatus ) hr = DisplayStatus( pwcCatalog, pwcMachine, lcid ); if ( SUCCEEDED( hr ) && fDisplayUpToDate ) hr = DisplayUpToDate( pwcCatalog, pwcMachine, lcid ); } } if ( fCoInit ) CoUninitialize(); if ( 0 != hISearch ) DoneWithISearch( hISearch ); if ( FAILED( hr ) ) return -1; return 0; } //wmain