//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 2000. // // File: srchq.cxx // // Contents: // // History: 15 Aug 1996 DLee Created // //-------------------------------------------------------------------------- #include "pch.cxx" #pragma hdrstop #define guidCPP { 0x8DEE0300, 0x16C2, 0x101B, { 0xB1, 0x21, 0x08, 0x00, 0x2B, 0x2E, 0xCD, 0xA9 } } static GUID guidCPlusPlus = guidCPP; static GUID guidBmk = DBBMKGUID; static const GUID guidQueryExt = DBPROPSET_QUERYEXT; static const GUID guidRowsetProps = DBPROPSET_ROWSET; static const DBID dbcolNull = { {0,0,0,{0,0,0,0,0,0,0,0}},DBKIND_GUID_PROPID,0}; #define guidZero { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } const DBID dbcolCPlusPlusClass = { guidCPP, DBKIND_GUID_NAME, L"class" }; const DBID dbcolCPlusPlusFunc = { guidCPP, DBKIND_GUID_NAME, L"func" }; const DBID dbcolBookMark = { DBBMKGUID, DBKIND_GUID_PROPID, (LPWSTR) (ULONG_PTR) PROPID_DBBMK_BOOKMARK }; const DBID dbcolPath = { PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) ULongToPtr( PID_STG_PATH ) }; const DBBINDING dbbindingPath = { 0, 0, 0, 0, 0, 0, 0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, sizeof (WCHAR *), 0, DBTYPE_WSTR|DBTYPE_BYREF, 0, 0, }; DBBINDING aBmkColumn[] = { 0, sizeof DBLENGTH, 0, 0, 0, 0, 0, DBPART_VALUE | DBPART_LENGTH, DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM, MAX_BOOKMARK_LENGTH, 0, DBTYPE_BYTES, 0, 0, }; 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]; SCODE ParseQuery( WCHAR * pwcQuery, ULONG ulDialect, LCID lcid, DBCOMMANDTREE ** ppQuery ) { *ppQuery = 0; return CITextToSelectTreeEx( pwcQuery, ulDialect, ppQuery, cCPPProperties, aCPPProperties, lcid ); } //ParseQuery //----------------------------------------------------------------------------- // // 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 0 // 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 // // CSearchQuery // CSearchQuery::CSearchQuery( const XGrowable & xCatList, WCHAR *pwcQuery, HWND hNotify, int cRowsDisp, LCID lcid, ESearchType srchType, IColumnMapper & columnMapper, CColumnList &columns, CSortList &sort, ULONG ulDialect, ULONG ulLimit, ULONG ulFirstRows ) : _hwndNotify(hNotify), _hwndList (0), _cRowsTotal(0), _cHRows(0), _cRowsDisp(cRowsDisp), _lcid(lcid), _fDone(FALSE), _srchType(srchType), _columns(columns), _columnMapper(columnMapper), _hAccessor(0), _hBmkAccessor(0), _hBrowseAccessor(0), _iRowCurrent(0), _hRegion(0), _pctDone(0), _dwQueryStatus(0), _scLastError(0), _dwStartTime(0), _dwEndTime(0), _prstQuery(0), _xCatList( xCatList ) { srchDebugOut((DEB_TRACE,"top of CSearchQuery()\n")); _dwStartTime = GetTickCount(); _bmkTop.MakeFirst(); for (int i = 0; i < cMaxRowCache; i++) _aHRows [i] = 0; ICommand *pICommand = 0; SCODE sc = InstantiateICommand( &pICommand ); if ( FAILED( sc ) ) THROW( CException( sc ) ); XInterface< ICommand> xCommand( pICommand ); _xwcQuery.SetSize(wcslen(pwcQuery) + 1); wcscpy(_xwcQuery.Get(),pwcQuery); switch ( ulDialect ) { case ISQLANG_V1: case ISQLANG_V2: { if (srchNormal == _srchType) { sc = ParseQuery( _xwcQuery.Get(), ulDialect, lcid, &_prstQuery ); if ( FAILED(sc) ) THROW( CException(sc) ); } else { XArray xQuery( wcslen( _xwcQuery.Get() ) + 50 ); wcscpy( xQuery.Get(), ( _srchType == srchClass ) ? L"@class " : L"@func " ); wcscat( xQuery.Get(), _xwcQuery.Get() ); sc = ParseQuery( xQuery.Get(), ulDialect, lcid, &_prstQuery ); if ( FAILED(sc) ) THROW( CException(sc) ); } XArray xColumns; columns.MakeList( xColumns ); XArray xSort; sort.MakeList( xSort ); DBCOMMANDTREE *pTree; sc = CIRestrictionToFullTree( _prstQuery, xColumns.Get(), xSort.Get(), 0, &pTree, 0, 0, lcid ); if ( FAILED( sc ) ) THROW( CException(sc) ); XInterface xCommandTree; sc = pICommand->QueryInterface( IID_ICommandTree, xCommandTree.GetQIPointer() ); if ( FAILED( sc ) ) THROW( CException(sc) ); sc = xCommandTree->SetCommandTree( &pTree, DBCOMMANDREUSE_NONE, FALSE); if (FAILED (sc) ) THROW( CException( sc ) ); break; } case SQLTEXT: { WCHAR awcCommand[cwcMaxQuery]; WCHAR awcPropList[cwcMaxQuery] = L"Path"; CResString wstrQueryFormat(IDS_SQLQUERY_FORMAT); XInterface xCommandText; sc = pICommand->QueryInterface( IID_ICommandText, xCommandText.GetQIPointer() ); if ( FAILED( sc ) ) THROW( CException( sc ) ); // set the list of columns // !! Allways select the PATH column !! for ( ULONG iCol = 0; iCol < _columns.NumberOfColumns(); iCol++ ) { if ( _wcsicmp ( _columns.GetColumn(iCol), L"Path" ) ) { wcscat( awcPropList, L", " ); wcscat( awcPropList, _columns.GetColumn( iCol ) ); } } WCHAR awcMachine[MAX_PATH]; WCHAR awcCatalog[MAX_PATH]; WCHAR awcPath[MAX_PATH]; BOOL fDeep; // What about ditributed queries? Not supported, but that's OK // since this is a test tool. GetCatListItem( _xCatList, 0, awcMachine, awcCatalog, awcPath, fDeep ); swprintf( awcCommand, wstrQueryFormat.Get(), awcPropList, awcCatalog, awcPath, pwcQuery ); srchDebugOut((DEB_TRACE, "SQL query: %ws\n", awcCommand)); sc = xCommandText->SetCommandText( DBGUID_SQL, awcCommand); if ( FAILED (sc) ) THROW( CException( sc ) ); XInterface xCommandPrepare; sc = pICommand->QueryInterface( IID_ICommandPrepare, xCommandPrepare.GetQIPointer() ); if ( FAILED (sc) ) THROW( CException( sc ) ); sc = xCommandPrepare->Prepare( 1 ); if ( FAILED (sc) ) THROW( CException( sc ) ); XInterface xCommandProperties; sc = pICommand->QueryInterface( IID_ICommandProperties, xCommandProperties.GetQIPointer() ); if ( FAILED (sc) ) THROW( CException( sc ) ); // set the machine name DBPROPSET PropSet; DBPROP Prop; const GUID guidQueryCorePropset = DBPROPSET_CIFRMWRKCORE_EXT; PropSet.rgProperties = &Prop; PropSet.cProperties = 1; PropSet.guidPropertySet = guidQueryCorePropset; Prop.dwPropertyID = DBPROP_MACHINE; Prop.colid = DB_NULLID; Prop.vValue.vt = VT_BSTR; Prop.vValue.bstrVal = SysAllocString( awcMachine ); if ( NULL == Prop.vValue.bstrVal ) THROW( CException( E_OUTOFMEMORY ) ); sc = xCommandProperties->SetProperties ( 1, &PropSet ); VariantClear( &Prop.vValue ); if ( FAILED (sc) ) THROW( CException( sc ) ); // try to get the restriction DBPROPIDSET PropIDSet; DBPROPID PropID = MSIDXSPROP_QUERY_RESTRICTION; PropIDSet.rgPropertyIDs = &PropID; PropIDSet.cPropertyIDs = 1; const GUID guidMSIDXS_ROWSETEXT = DBPROPSET_MSIDXS_ROWSETEXT; PropIDSet.guidPropertySet = guidMSIDXS_ROWSETEXT; ULONG cPropSets; DBPROPSET *pPropSet; sc = xCommandProperties->GetProperties ( 1, &PropIDSet, &cPropSets, &pPropSet ); if ( FAILED (sc) ) THROW( CException( sc ) ); Win4Assert( 1 == cPropSets ); XCoMem xPropSet( pPropSet ); XCoMem xProp( pPropSet->rgProperties ); srchDebugOut((DEB_TRACE, "SQL query as tripolish: %ws\n", pPropSet->rgProperties->vValue.bstrVal )); // MSIDXSPROP_QUERY_RESTRICTION returns the restriction in in Triplish1 syntax. // It is sometimes bogus. Just set a zero and ignore the error for now. DBCOMMANDTREE *pQuery = 0; ParseQuery( pPropSet->rgProperties->vValue.bstrVal, ISQLANG_V1, lcid, &pQuery ); _prstQuery = pQuery; sc = VariantClear( &pPropSet->rgProperties->vValue ); if ( FAILED (sc) ) THROW( CException( sc ) ); break; } } // Set the property that says we want to use asynch. queries { ICommandProperties * pCmdProp = 0; sc = pICommand->QueryInterface( IID_ICommandProperties, (void **)&pCmdProp ); if (FAILED (sc) ) THROW( CException( sc ) ); XInterface< ICommandProperties > xComdProp( pCmdProp ); const unsigned MAX_PROPS = 8; DBPROPSET aPropSet[MAX_PROPS]; DBPROP aProp[MAX_PROPS]; ULONG cProps = 0; // asynchronous, watchable query aProp[cProps].dwPropertyID = DBPROP_IDBAsynchStatus; aProp[cProps].dwOptions = DBPROPOPTIONS_REQUIRED; aProp[cProps].dwStatus = 0; aProp[cProps].colid = dbcolNull; aProp[cProps].vValue.vt = VT_BOOL; aProp[cProps].vValue.boolVal = VARIANT_TRUE; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = guidRowsetProps; cProps++; aProp[cProps].dwPropertyID = DBPROP_IRowsetWatchRegion; aProp[cProps].dwOptions = DBPROPOPTIONS_REQUIRED; aProp[cProps].dwStatus = 0; aProp[cProps].colid = dbcolNull; aProp[cProps].vValue.vt = VT_BOOL; aProp[cProps].vValue.boolVal = VARIANT_TRUE; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = guidRowsetProps; cProps++; // don't timeout queries aProp[cProps].dwPropertyID = DBPROP_COMMANDTIMEOUT; aProp[cProps].dwOptions = DBPROPOPTIONS_OPTIONAL; aProp[cProps].dwStatus = 0; aProp[cProps].colid = dbcolNull; aProp[cProps].vValue.vt = VT_I4; aProp[cProps].vValue.lVal = 0; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = guidRowsetProps; cProps++; // We can handle PROPVARIANTs aProp[cProps].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES; aProp[cProps].dwOptions = DBPROPOPTIONS_OPTIONAL; aProp[cProps].dwStatus = 0; aProp[cProps].colid = dbcolNull; aProp[cProps].vValue.vt = VT_BOOL; aProp[cProps].vValue.boolVal = VARIANT_TRUE; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = guidQueryExt; cProps++; if ( App.ForceUseCI() ) { // Set the property that says we don't want to enumerate aProp[cProps].dwPropertyID = DBPROP_USECONTENTINDEX; aProp[cProps].dwOptions = DBPROPOPTIONS_OPTIONAL; aProp[cProps].dwStatus = 0; aProp[cProps].colid = dbcolNull; aProp[cProps].vValue.vt = VT_BOOL; aProp[cProps].vValue.boolVal = VARIANT_TRUE; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = guidQueryExt; cProps++; } Win4Assert( MAX_PROPS >= cProps ); sc = pCmdProp->SetProperties( cProps, aPropSet ); if (FAILED (sc) || DB_S_ERRORSOCCURRED == sc ) THROW( CException( sc ) ); } if ( 0 != ulLimit || 0 != ulFirstRows ) { static const DBID dbcolNull = { { 0,0,0, { 0,0,0,0,0,0,0,0 } }, DBKIND_GUID_PROPID, 0 }; DBPROP aRowsetProp[1]; aRowsetProp[0].dwOptions = DBPROPOPTIONS_OPTIONAL; aRowsetProp[0].dwStatus = 0; aRowsetProp[0].colid = dbcolNull; aRowsetProp[0].dwPropertyID = (0 != ulLimit) ? DBPROP_MAXROWS : DBPROP_FIRSTROWS; aRowsetProp[0].vValue.vt = VT_I4; aRowsetProp[0].vValue.lVal = (LONG) (0 != ulLimit) ? ulLimit : ulFirstRows; DBPROPSET aPropSet[1]; aPropSet[0].rgProperties = &aRowsetProp[0]; aPropSet[0].cProperties = 1; aPropSet[0].guidPropertySet = DBPROPSET_ROWSET; XInterface xICommandProperties; sc = pICommand->QueryInterface( IID_ICommandProperties, xICommandProperties.GetQIPointer() ); if ( FAILED( sc ) ) THROW( CException( sc ) ); sc = xICommandProperties->SetProperties( 1, aPropSet ); // the properties if (FAILED (sc) || DB_S_ERRORSOCCURRED == sc ) THROW( CException( sc ) ); } IRowsetScroll * pRowset; sc = pICommand->Execute( 0, // no aggr. IUnknown IID_IRowsetScroll, // IID for i/f to return 0, // dbparams 0, // chapter (IUnknown **) & pRowset ); // Returned interface if ( FAILED(sc) ) { // get the real error here ERRORINFO ErrorInfo; XInterface xErrorInfo; SCODE sc2 = GetOleDBErrorInfo( pICommand, IID_ICommand, lcid, &ErrorInfo, xErrorInfo.GetPPointer() ); // Post IErrorInfo only if we have a valid ptr to it. if (SUCCEEDED(sc2) && 0 != xErrorInfo.GetPointer()) sc = ErrorInfo.hrError; THROW( CException(sc) ); } _xRowset.Set( pRowset ); IRowsetQueryStatus *pRowsetStatus; sc = _xRowset->QueryInterface( IID_IRowsetQueryStatus, (void**) &pRowsetStatus ); if (SUCCEEDED( sc ) ) _xRowsetStatus.Set( pRowsetStatus ); IColumnsInfo *pColInfo = 0; sc = _xRowset->QueryInterface( IID_IColumnsInfo, (void **)&pColInfo ); if (FAILED(sc)) THROW( CException( sc ) ); XInterface< IColumnsInfo > xColInfo( pColInfo ); IAccessor *pIAccessor; sc = _xRowset->QueryInterface( IID_IAccessor, (void **)&pIAccessor ); if (FAILED(sc)) THROW (CException (sc)); _xIAccessor.Set( pIAccessor ); // // set up an accessor for bookmarks. // DBID acols[1]; acols[0] = dbcolBookMark; DBORDINAL lcol; sc = pColInfo->MapColumnIDs(1, acols, &lcol); if (FAILED(sc)) THROW (CException (sc)); Win4Assert( 0 == lcol ); aBmkColumn[0].iOrdinal = lcol; sc = _xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 1, aBmkColumn, 0, &_hBmkAccessor, 0 ); if ( FAILED(sc) ) THROW (CException (sc)); SetupColumnMappingsAndAccessors(); // Get a pointer to the IDBAsynchStatus for checking completion state IDBAsynchStatus *pDBAsynchStatus; sc = _xRowset->QueryInterface( IID_IDBAsynchStatus, (void**) &pDBAsynchStatus ); if ( FAILED(sc) ) THROW (CException (sc)); _xDBAsynchStatus.Set( pDBAsynchStatus ); } //CSearchQuery void CSearchQuery::InitNotifications( HWND hwndList) { _hwndList = hwndList; _xWatch.Set( new CWatchQuery( this, _xRowset.GetPointer() ) ); if (!_xWatch->Ok()) _xWatch.Free(); else _hRegion = _xWatch->Handle(); } //InitNotifications CSearchQuery::~CSearchQuery() { srchDebugOut((DEB_TRACE,"top of ~CSearchQuery()\n")); _xRowsetStatus.Free(); // // make sure notification thread isn't stuck sleeping in our code // when we shut down notifications. this is ok, but will cause // an unnecessary delay in shutting down the query. // if ( !_xWatch.IsNull() ) _xWatch->IgnoreNotifications( TRUE ); _xDBAsynchStatus.Free(); _xWatch.Free(); if ( !_xIAccessor.IsNull() ) { if (_hAccessor) _xIAccessor->ReleaseAccessor( _hAccessor, 0 ); if ( _hBmkAccessor ) _xIAccessor->ReleaseAccessor( _hBmkAccessor, 0 ); if ( _hBrowseAccessor ) _xIAccessor->ReleaseAccessor( _hBrowseAccessor, 0 ); _xIAccessor.Free(); } if ( !_xRowset.IsNull() ) { srchDebugOut((DEB_TRACE,"~ Releasing %d rows, first %d\n",_cHRows,_aHRows[0])); _xRowset->ReleaseRows(_cHRows, _aHRows, 0, 0, 0); _xRowset.Free(); } srchDebugOut(( DEB_TRACE, "bottom of ~CSearchQuery()\n" )); } //~CSearchQuery BOOL CSearchQuery::ListNotify( HWND hwnd, WPARAM action, long * pDist) { BOOL fRet = TRUE; CWaitCursor wait; switch (action) { case listScrollLineUp: ScrollLineUp (pDist); break; case listScrollLineDn: ScrollLineDn (pDist); break; case listScrollPageUp: ScrollPageUp (pDist); break; case listScrollPageDn: ScrollPageDn (pDist); break; case listScrollTop: ScrollTop (pDist); break; case listScrollBottom: ScrollBottom (pDist); break; case listScrollPos: ScrollPos (pDist); break; case listSize: fRet = WindowResized (*(ULONG*)pDist); break; case listSelect: return Select (pDist); break; case listSelectUp: fRet = SelectUp( pDist ); break; case listSelectDown: fRet = SelectDown( pDist ); break; default: Win4Assert (!"Unknown action in CSearchQuery::Scroll"); } return fRet; } //ListNotify long CSearchQuery::FindSelection() { long iSel; if (IsSelected()) { // Find out what is selected for (ULONG i = 0; i < _cHRows; i++) if (IsSelected(i)) break; if (i == _cHRows) iSel = -1; else iSel = i; } else { iSel = -1; } return iSel; } //FindSelection BOOL CSearchQuery::SelectUp( long * piNew ) { *piNew = FindSelection(); if ( -1 == *piNew || 0 == *piNew ) return FALSE; (*piNew)--; GetBookMark( _aHRows[ *piNew ], _bmkSelect ); return TRUE; } //SelectUp BOOL CSearchQuery::SelectDown( long * piNew ) { *piNew = FindSelection(); if ( ( -1 == *piNew ) || ( *piNew == (long) ( _cHRows - 1) ) ) return FALSE; (*piNew)++; GetBookMark( _aHRows[ *piNew ], _bmkSelect ); return TRUE; } //SelectDown BOOL CSearchQuery::Select( long* piRow ) { ULONG newRow = *piRow; if (newRow >= _cHRows) { return FALSE; } *piRow = FindSelection(); if (*piRow != (long)newRow) GetBookMark (_aHRows[newRow], _bmkSelect); #if 0 else _bmkSelect.Invalidate(); #endif return TRUE; } //Select BOOL CSearchQuery::IsSelected( UINT iRow ) { if (iRow >= _cHRows) return FALSE; CBookMark bmk; GetBookMark (_aHRows[iRow], bmk); return bmk.IsEqual (_bmkSelect); } //IsSelected BOOL CSearchQuery::WindowResized( ULONG & cRows) { BOOL fInvalidate = FALSE; if (cRows < _cHRows) { if ( !_xWatch.IsNull() ) _xWatch->Shrink (_hRegion, _bmkTop, cRows); _xRowset->ReleaseRows(_cHRows - cRows, _aHRows + cRows, 0, 0, 0); for (ULONG i = cRows; i < _cHRows; i++) _aHRows[i] = 0; _cHRows = cRows; } else if (cRows > _cHRows) { if (cRows > cMaxRowCache) cRows = cMaxRowCache; CBookMark bmk; long cSkip; if (_cHRows == 0) { cSkip = 0; bmk.MakeFirst(); } else { cSkip = 1; GetBookMark (_aHRows[_cHRows-1], bmk); } DBCOUNTITEM cRowsFetched = 0; if ( !_xWatch.IsNull() ) _xWatch->Extend (_hRegion); ULONG cRowsNeeded = cRows - _cHRows; FetchResult res = Fetch ( bmk, cSkip, cRowsNeeded, cRowsFetched, _aHRows + _cHRows, _hRegion); if ( cRowsFetched ) { _cHRows += (ULONG) cRowsFetched; cRowsNeeded -= (ULONG) cRowsFetched; } if ( cRowsNeeded && _cHRows < _cRowsTotal ) { // looks like we are at the ned // try to get rows from the top to fill up new space fInvalidate = TRUE; Win4Assert( fetchBoundary == res ); if ( cRowsNeeded > _cRowsTotal - _cHRows ) { cRowsNeeded = (ULONG) ( _cRowsTotal - _cHRows ); } _cRowsDisp = cRows; // need to set this here before PageUp ScrollPageUp( (long*) &cRowsNeeded ); } } _cRowsDisp = cRows; cRows = _cHRows; // return TRUE if an invalidate is needed or FALSE if just updating // the scroll bars is sufficient. return fInvalidate; } //WindowResized void CSearchQuery::ScrollLineUp( long * pDist) { if (_cHRows == 0) { *pDist = 0; return; } // Try to fetch new first row HROW hrow; CBookMark bmk; GetBookMark (_aHRows[0], bmk); DBCOUNTITEM cRowsFetched = 0; if ( !_xWatch.IsNull() ) _xWatch->Move (_hRegion); FetchResult res = Fetch ( bmk, -1, 1, cRowsFetched, &hrow, _hRegion); if ( isFetchOK( res ) && cRowsFetched == 1) { if (_cHRows == _cRowsDisp) { // Release last row _xRowset->ReleaseRows (1, _aHRows + _cHRows - 1, 0, 0, 0); } else { _cHRows++; } // shift all rows down for (ULONG i = _cHRows - 1; i > 0; i--) _aHRows [i] = _aHRows [i-1]; _aHRows [0] = hrow; GetBookMark (hrow, _bmkTop); } else { *pDist = 0; _bmkTop.MakeFirst(); } } //ScrollLineUp void CSearchQuery::ScrollLineDn( long* pDist) { if (_cHRows < _cRowsDisp || _cHRows == 0) { // can't scroll *pDist = 0; } else { // Try to fetch new last row HROW hrow; CBookMark bmk; GetBookMark (_aHRows[_cHRows-1], bmk); DBCOUNTITEM cRowsFetched = 0; if ( !_xWatch.IsNull() ) _xWatch->Move( _hRegion ); FetchResult res = Fetch ( bmk, 1, 1, cRowsFetched, &hrow, _hRegion); if ( ( isFetchOK( res ) ) && ( 1 == cRowsFetched ) ) { // release zeroth row _xRowset->ReleaseRows (1, _aHRows, 0, 0, 0); // shift all rows up for (ULONG i = 0; i < _cHRows - 1; i++) _aHRows [i] = _aHRows [i+1]; // if we fetched the row if (cRowsFetched == 1) _aHRows [_cHRows - 1] = hrow; GetBookMark (_aHRows[0], _bmkTop); } else *pDist = 0; } } //ScrollLineDn void CSearchQuery::ScrollPageUp( long* pDist) { if (_cHRows == 0) { *pDist = 0; return; } // Try to fetch new first rows CBookMark bmk; GetBookMark (_aHRows[0], bmk); DBCOUNTITEM cRowsFetched = 0; long count = *pDist; FetchResult res; if ( !_xWatch.IsNull() ) _xWatch->Move( _hRegion ); res = Fetch ( bmk, -count, count, cRowsFetched, _aHRowsTmp, _hRegion); *pDist = (long) cRowsFetched; if ( isFetchOK( res ) && cRowsFetched != 0) { // release rows at the end int cRowsToRelease = (int) ( _cHRows + cRowsFetched - _cRowsDisp ); int cRowsToShift = (int) ( _cRowsDisp - cRowsFetched ); _cHRows += (ULONG) cRowsFetched; if (cRowsToRelease > 0) { Win4Assert ( cRowsToShift >= 0); _xRowset->ReleaseRows (cRowsToRelease, _aHRows + cRowsToShift, 0, 0, 0); _cHRows -= cRowsToRelease; } if (cRowsToShift > 0) { for (int i = 0; i < cRowsToShift; i++) _aHRowsTmp[cRowsFetched + i] = _aHRows[i]; } for (ULONG i = 0; i < _cRowsDisp; i++) _aHRows[i] = _aHRowsTmp[i]; if (res == fetchBoundary) _bmkTop.MakeFirst(); else GetBookMark (_aHRows[0], _bmkTop); } } //ScrollPageUp void CSearchQuery::ScrollPageDn( long* pDist) { if (_cHRows < _cRowsDisp || _cHRows == 0) { // can't scroll *pDist = 0; } else { // Try to fetch new bottom rows CBookMark bmk; GetBookMark (_aHRows[_cHRows-1], bmk); DBCOUNTITEM cRowsFetched = 0; if ( !_xWatch.IsNull() ) _xWatch->Move( _hRegion ); FetchResult res = Fetch ( bmk, 1, *pDist, cRowsFetched, _aHRowsTmp, _hRegion); *pDist = (long) cRowsFetched; if ( isFetchOK( res ) && ( cRowsFetched > 0 ) ) { int cRowsToRelease = (int) ( cRowsFetched + _cHRows - _cRowsDisp ); int cRowsToShift = (int) ( _cRowsDisp - cRowsFetched ); // release top rows _xRowset->ReleaseRows (cRowsToRelease, _aHRows, 0, 0, 0); // shift all rows up for (int i = 0; i < cRowsToShift; i++) _aHRows [i] = _aHRows [i+cRowsToRelease]; for (ULONG j = 0; j < cRowsFetched; j++) _aHRows [cRowsToShift + j] = _aHRowsTmp[j]; _cHRows = (ULONG) ( cRowsToShift + cRowsFetched ); GetBookMark (_aHRows[0], _bmkTop); } } } //ScrollPageDn void CSearchQuery::ScrollBottom( long* pDist) { CBookMark bmk(DBBMK_LAST); DBCOUNTITEM cRowsFetched = 0; if ( !_xWatch.IsNull() ) _xWatch->Move (_hRegion); FetchResult res = Fetch ( bmk, 1 - *pDist, *pDist, cRowsFetched, _aHRowsTmp, _hRegion); if ( isFetchOK( res ) ) { _xRowset->ReleaseRows(_cHRows, _aHRows, 0, 0, 0); for (ULONG i = 0; i < cRowsFetched; i++) _aHRows[i] = _aHRowsTmp[i]; for (; i < _cHRows; i++) _aHRows[i] = 0; _cHRows = (ULONG) cRowsFetched; *pDist = (long) cRowsFetched; GetBookMark (_aHRows[0], _bmkTop); } } //ScrollBottom void CSearchQuery::ScrollTop( long* pDist) { CBookMark bmk(DBBMK_FIRST); DBCOUNTITEM cRowsFetched = 0; if ( !_xWatch.IsNull() ) _xWatch->Move (_hRegion); FetchResult res = Fetch ( bmk, 0, *pDist, cRowsFetched, _aHRowsTmp, _hRegion); if ( isFetchOK( res ) ) { _xRowset->ReleaseRows(_cHRows, _aHRows, 0, 0, 0); for (ULONG i = 0; i < cRowsFetched; i++) _aHRows[i] = _aHRowsTmp[i]; for (; i < _cHRows; i++) _aHRows[i] = 0; _cHRows = (ULONG) cRowsFetched; *pDist = (long) cRowsFetched; } _bmkTop.MakeFirst(); } //ScrollTop void CSearchQuery::ScrollPos( long* pDist) { DBCOUNTITEM cRowsFetched = 0; if ( !_xWatch.IsNull() ) _xWatch->Move (_hRegion); if (FetchApprox (*pDist, _cRowsDisp, cRowsFetched, _aHRowsTmp, _hRegion)) { _xRowset->ReleaseRows(_cHRows, _aHRows, 0, 0, 0); for (ULONG i = (ULONG) cRowsFetched; i < _cHRows; i++) _aHRows[i] = 0; for (i = 0; i < cRowsFetched; i++) _aHRows [i] = _aHRowsTmp [i]; _cHRows = (ULONG) cRowsFetched; CBookMark bmk; GetBookMark (_aHRowsTmp[0], bmk); _xRowset->GetApproximatePosition(0, bmk.cbBmk, bmk.abBmk, &_iRowCurrent, &_cRowsTotal); if (_iRowCurrent > 0) _iRowCurrent--; *pDist = (long) _iRowCurrent; GetBookMark (_aHRows[0], _bmkTop); } else *pDist = -1; } //ScrollPos void CSearchQuery::InvalidateCache() { ULONG cRows = _cRowsDisp; ULONG zero = 0; WindowResized (zero); WindowResized (cRows); } //InvalidateCache void CSearchQuery::UpdateProgress( BOOL& fMore) { if ( _xRowset.IsNull() ) { _pctDone = 0; return; } #if 0 if ( !_xRowsetStatus.IsNull() ) { ULONG ulNumerator,ulDenominator; DWORD cFilteredDocs,cDocsToFilter; SCODE sc; if ( 0 != _cHRows ) { sc = _xRowsetStatus->GetStatusEx( &_dwQueryStatus, &cFilteredDocs, &cDocsToFilter, &ulDenominator, &ulNumerator, _bmkTop.cbBmk, _bmkTop.abBmk, &_iRowCurrent, &_cRowsTotal ); _iRowCurrent--; // zero base } else { DWORD current; sc = _xRowsetStatus->GetStatusEx( &_dwQueryStatus, &cFilteredDocs, &cDocsToFilter, &ulDenominator, &ulNumerator, 0, 0, ¤t, &_cRowsTotal ); } if ( FAILED( sc ) ) { // query failed when we weren't looking _pctDone = 100; _iRowCurrent = 0; _cRowsTotal = 0; _fDone = TRUE; _dwQueryStatus = STAT_ERROR; _scLastError = sc; } else { Win4Assert( ulNumerator <= ulDenominator ); _pctDone = 100 * ulNumerator; if ( 0 == ulDenominator ) // Prevent division by 0 ulDenominator = 1; _pctDone /= ulDenominator; } } else #endif // 0 { DBCOUNTITEM ulNumerator, ulDenominator; DBASYNCHPHASE ulAsynchPhase; SCODE sc = _xDBAsynchStatus->GetStatus( DB_NULL_HCHAPTER, DBASYNCHOP_OPEN, &ulNumerator, &ulDenominator, &ulAsynchPhase, 0 ); if ( FAILED( sc ) ) { // query failed when we weren't looking _pctDone = 100; _iRowCurrent = 0; _cRowsTotal = 0; _fDone = TRUE; } else { if ( !_xRowsetStatus.IsNull() ) _xRowsetStatus->GetStatus( &_dwQueryStatus ); Win4Assert( (ulAsynchPhase == DBASYNCHPHASE_COMPLETE) ? (ulNumerator == ulDenominator) : (ulNumerator < ulDenominator) ); _pctDone = 100 * (ULONG) ulNumerator; if ( 0 == ulDenominator ) // Prevent division by 0 ulDenominator = 1; _pctDone /= (ULONG) ulDenominator; if (_cHRows != 0) { _xRowset->GetApproximatePosition( 0, _bmkTop.cbBmk, _bmkTop.abBmk, &_iRowCurrent, &_cRowsTotal ); _iRowCurrent--; // zero base } else { _xRowset->GetApproximatePosition(0, 0, 0, 0, &_cRowsTotal); } } } } //UpdateProgress void CSearchQuery::ProcessNotification( HWND hwndList, DBWATCHNOTIFY changeType, IRowset * pRowset) { if ( _xRowset.GetPointer() == pRowset && !_xWatch.IsNull() ) _xWatch->Notify( hwndList, changeType ); _dwEndTime = GetTickCount(); } //ProcessNotification void CSearchQuery::ProcessNotificationComplete() { if ( !_xWatch.IsNull() ) _xWatch->NotifyComplete(); } //ProcessNotificationComplete void CSearchQuery::CreateScript( DBCOUNTITEM * pcChanges, DBROWWATCHCHANGE ** paScript) { if (_cRowsDisp == 0) { *pcChanges = 0; return; } Win4Assert (_cHRows == 0 || _aHRows[_cHRows-1] != 0); *paScript = (DBROWWATCHCHANGE*) CoTaskMemAlloc (2 * _cRowsDisp * sizeof DBROWWATCHCHANGE); DBCOUNTITEM cRowsFetched = 0; if ( !_xWatch.IsNull() ) _xWatch->Move (_hRegion); //srchDebugOut((DEB_TRACE,"CreateScript fetch\n")); Fetch ( _bmkTop, 0, _cRowsDisp, cRowsFetched, _aHRowsTmp, _hRegion); if (cRowsFetched > 0) { //srchDebugOut((DEB_TRACE," CreateScript fetched %d rows\n",cRowsFetched)); ULONG iSrc = 0; ULONG iDst = 0; do { if ( iDst == _cHRows || _aHRows[iDst] != _aHRowsTmp [iSrc]) { // maybe the current iDst row was deleted? // Find out it the iSrc row appears // somewhere after the current row for (ULONG i = iDst + 1; i < _cHRows; i++) { if (_aHRows[i] == _aHRowsTmp[iSrc]) break; } if (i < _cHRows && iSrc < _cRowsDisp ) { // everything from iDst up to i - 1 // has been deleted while ( iDst < i ) { DBROWWATCHCHANGE& change = (*paScript)[*pcChanges]; change.hRegion = _hRegion; change.eChangeKind = DBROWCHANGEKIND_DELETE; change.iRow = iSrc; change.hRow = 0; (*pcChanges)++; iDst++; } } else { // insertion DBROWWATCHCHANGE& change = (*paScript)[*pcChanges]; change.hRegion = _hRegion; change.eChangeKind = DBROWCHANGEKIND_INSERT; change.iRow = iSrc - 1; change.hRow = _aHRowsTmp[iSrc]; (*pcChanges)++; iSrc++; } } else { //srchDebugOut((DEB_TRACE," CreateScript ignoring row %d\n",_aHRowsTmp[iSrc])); _xRowset->ReleaseRows ( 1, _aHRowsTmp + iSrc, 0, 0, 0); iDst++; iSrc++; } } while (iDst < _cRowsDisp && iSrc < cRowsFetched); if ( iSrc < cRowsFetched ) { //srchDebugOut((DEB_TRACE," CreateScript freeing rows left behind\n")); _xRowset->ReleaseRows ( cRowsFetched - iSrc, _aHRowsTmp + iSrc, 0, 0, 0); } else if (iSrc == cRowsFetched && iSrc < _cRowsDisp) { // the rest of the rows were deleted while (iDst < _cHRows) { DBROWWATCHCHANGE& change = (*paScript)[*pcChanges]; change.hRegion = _hRegion; change.eChangeKind = DBROWCHANGEKIND_DELETE; change.iRow = iSrc; change.hRow = 0; (*pcChanges)++; iDst++; } } } if (*pcChanges == 0) { CoTaskMemFree (*paScript); *paScript = 0; } } //CreateScript void CSearchQuery::InsertRowAfter( int iRow, HROW hrow) { Win4Assert (iRow >= -1 && iRow < (int) _cHRows && iRow < (int)_cRowsDisp - 1); int iLastRow = _cHRows - 1; // release last row if (_cHRows == _cRowsDisp) { //srchDebugOut((DEB_TRACE,"InsertRowAfter releasing 1 row %d\n",_aHRows[iLastRow])); _xRowset->ReleaseRows(1, _aHRows + iLastRow, 0, 0, 0); // shift rows down for (int i = iLastRow; i > iRow + 1; i--) { _aHRows [i] = _aHRows [i-1]; } } else { // shift rows down for (int i = iLastRow + 1; i > iRow + 1; i--) { _aHRows [i] = _aHRows [i-1]; } _cHRows++; } _aHRows [iRow + 1] = hrow; if (iRow == -1 && !_bmkTop.IsFirst()) { GetBookMark (_aHRows[0], _bmkTop); } } //InsertRowAfter void CSearchQuery::DeleteRow( int iRow) { // with limited rows, rows to be deleted may exceed the number of // rows in the result //Win4Assert (iRow >= 0 && iRow < (int)_cHRows); if ( _aHRows[iRow] > 0 ) { _xRowset->ReleaseRows (1, _aHRows + iRow, 0, 0, 0); _aHRows[iRow] = 0; } // shift rows up for (ULONG i = iRow; i < _cHRows - 1; i++) _aHRows [i] = _aHRows [i+1]; #if 0 if (_cHRows < _cRowsDisp) { _aHRows[_cHRows-1] = 0; _cHRows--; } else { // Try to fetch new last row HROW hrow; CBookMark bmk; GetBookMark (_aHRows[_cHRows-1], bmk); DBCOUNTITEM cRowsFetched = 0; // no need when running script if ( !_xWatch.IsNull() ) _xWatch->Move (_hRegion); FetchResult res = Fetch ( bmk, 1, 1, cRowsFetched, &hrow, _hRegion); if ( isFetchOK( res ) && ( cRowsFetched > 0 ) ) _xRowset->ReleaseRows(1, &hrow, 0, 0, 0); } #else if ( (ULONG)iRow < _cHRows ) { _aHRows[_cHRows-1] = 0; _cHRows--; } #endif if (iRow == 0 && !_bmkTop.IsFirst() && _aHRows[0] != 0) GetBookMark (_aHRows[0], _bmkTop); } //DeleteRow void CSearchQuery::UpdateRow( int iRow, HROW hrow) { Win4Assert (iRow >= 0 && iRow < (int)_cHRows); //srchDebugOut((DEB_TRACE,"UpdateRowAfter releasing 1 row %d\n",_aHRows[iRow])); _xRowset->ReleaseRows (1, _aHRows + iRow, 0, 0, 0); _aHRows[iRow] = hrow; } //UpdateRow BOOL CSearchQuery::GetSelectedRowData( WCHAR *&rpPath, HROW &hrow ) { if (!_bmkSelect.IsValid()) return FALSE; DBCOUNTITEM cRowsFetched = 0; FetchResult res = Fetch ( _bmkSelect, 0, 1, cRowsFetched, &hrow, 0); if ( ( !isFetchOK( res ) ) || ( cRowsFetched != 1 ) ) return FALSE; return SUCCEEDED( _xRowset->GetData( hrow, _hBrowseAccessor, &rpPath ) ); } //GetSelectedRowData void CSearchQuery::FreeSelectedRowData( HROW hrow ) { _xRowset->ReleaseRows( 1, &hrow, 0, 0, 0 ); } //FreeSelectedRowData BOOL CSearchQuery::Browse( enumViewFile eViewType ) { BOOL fOK = TRUE; if (!_bmkSelect.IsValid()) return TRUE; HROW hrow; DBCOUNTITEM cRowsFetched = 0; FetchResult res = Fetch( _bmkSelect, 0, 1, cRowsFetched, &hrow, 0 ); if ( ( !isFetchOK( res ) ) || ( cRowsFetched != 1 ) ) return fOK; WCHAR *pwcPathFound; SCODE sc = _xRowset->GetData( hrow, _hBrowseAccessor, &pwcPathFound ); if (SUCCEEDED(sc) && ( 0 != _prstQuery ) ) { fOK = ViewFile( pwcPathFound, eViewType, 1, _prstQuery ); _xRowset->ReleaseRows(1, &hrow, 0, 0, 0); } else fOK = FALSE; return fOK; } //Browse // // Helper method(s) // void CSearchQuery::ParseCatList( WCHAR * * aScopes, WCHAR * * aCatalogs, WCHAR * * aMachines, DWORD * aDepths, ULONG & cScopes ) { BOOL fDeep; WCHAR aScope[MAX_PATH]; WCHAR aCatalog[MAX_PATH]; WCHAR aMachine[MAX_PATH]; for(cScopes = 0; ; cScopes++) { if ( !GetCatListItem( _xCatList, cScopes, aMachine, aCatalog, aScope, fDeep ) ) { break; } aMachines[cScopes] = new WCHAR[ wcslen( aMachine ) + 1 ]; aCatalogs[cScopes] = new WCHAR[ wcslen( aCatalog ) + 1 ]; aScopes[cScopes] = new WCHAR[ wcslen( aScope ) + 1 ]; wcscpy( aMachines[cScopes], aMachine ); wcscpy( aCatalogs[cScopes], aCatalog ); wcscpy( aScopes[cScopes], aScope ); aDepths[cScopes] = ( fDeep ? QUERY_DEEP : QUERY_SHALLOW ); // If the scope is virtual, set the flag and flip the slashes if ( L'/' == aScope[0] ) { aDepths[ cScopes ] |= QUERY_VIRTUAL_PATH; for ( WCHAR *p = aScopes[cScopes]; *p; p++ ) if ( L'/' == *p ) *p = L'\\'; } } } SCODE CSearchQuery::InstantiateICommand( ICommand ** ppICommand ) { DWORD aDepths[ 20 ]; // hope 20 is big enough WCHAR * aScopes[ 20 ]; WCHAR * aCatalogs[ 20 ]; WCHAR * aMachines[ 20 ]; ULONG cScopes = 0; SCODE sc = S_FALSE; ParseCatList( aScopes, aCatalogs, aMachines, aDepths, cScopes ); *ppICommand = 0; sc = CIMakeICommand( ppICommand, cScopes, aDepths, (WCHAR const * const *)aScopes, (WCHAR const * const *)aCatalogs, (WCHAR const * const *)aMachines ); unsigned ii; for( ii = 0; ii < cScopes; ii ++) { delete [] aMachines[ii]; // This mem may not get deleted if we throw delete [] aCatalogs[ii]; // in this func ? delete [] aScopes[ii]; } return sc; } //InstantiateICommand BOOL CSearchQuery::FetchApprox( LONG iFirstRow, LONG cToFetch, DBCOUNTITEM &rcFetched, HROW *pHRows, HWATCHREGION hRegion) { SCODE sc; if ( 0 != _cRowsTotal ) { sc = _xRowset->GetRowsAtRatio( hRegion, 0, // no chapters iFirstRow, _cRowsTotal, cToFetch, &rcFetched, &pHRows ); if ( FAILED( sc ) ) _scLastError = sc; } else { rcFetched = 0; sc = S_OK; } return (SUCCEEDED(sc) && rcFetched); } //FetchApprox FetchResult CSearchQuery::Fetch( CBookMark & bmkStart, LONG iFirstRow, LONG cToFetch, DBCOUNTITEM & rcFetched, HROW * pHRows, HWATCHREGION hRegion) { //srchDebugOut((DEB_TRACE," Fetch fetch\n")); Win4Assert (bmkStart.IsValid()); SCODE scTmp; SCODE sc = _xRowset->GetRowsAt( hRegion, 0, // no chapters bmkStart.cbBmk, bmkStart.abBmk, iFirstRow, cToFetch, &rcFetched, &pHRows ); WCHAR* szError = 0; WCHAR buf[100]; if ( FAILED( sc ) ) _scLastError = sc; switch (sc) { case S_OK: //srchDebugOut((DEB_TRACE," ::fetch ok got %d rows, first: %d\n",rcFetched,pHRows[0])); if (cToFetch == (long)rcFetched) return fetchOk; else szError = L"Incomplete Fetch returned S_OK"; break; case DB_S_ENDOFROWSET: //srchDebugOut((DEB_TRACE," ::fetch EOR got %d rows, first: %d\n",rcFetched,pHRows[0])); // Debugging // Turn it back on when we have frozen state! // if ( FALSE && rcFetched != 0) { HROW* pHRowsTmp = new HROW [cToFetch]; DBCOUNTITEM cFetchedTmp = 0; CBookMark bmk; GetBookMark (pHRows[0], bmk); scTmp = _xRowset->GetRowsAt( hRegion, 0, // no chapters bmk.cbBmk, bmk.abBmk, 0, cToFetch, &cFetchedTmp, &pHRowsTmp ); if (FAILED(scTmp)) { szError = buf; swprintf (buf, L"Repeated call returned error 0x%04x", scTmp); } else if (cFetchedTmp < rcFetched) { szError = L"Repeated call returned fewer rows"; } else { for (ULONG i = 0; i < rcFetched; i++) { if (pHRows[i] != pHRowsTmp[i]) { szError = L"Repeated call returned different HROWs"; break; } } } _xRowset->ReleaseRows(cFetchedTmp, pHRowsTmp, 0, 0, 0); delete pHRowsTmp; if (szError != 0) break; } return fetchBoundary; case DB_E_BADSTARTPOSITION: //srchDebugOut((DEB_TRACE," ::fetch %d returned DB_E_BADSTARTPOSITION \n",cToFetch)); //Win4Assert(sc != DB_E_BADSTARTPOSITION); szError = L"DB_E_BADSTARTPOSITION"; break; case DB_S_BOOKMARKSKIPPED: szError = L"DB_S_BOOKMARKSKIPPED"; break; case DB_S_ROWLIMITEXCEEDED: szError = L"DB_S_ROWLIMITEXCEEDED"; break; case DB_E_BADBOOKMARK: szError = L"DB_E_BADBOOKMARK"; break; case DB_E_BADCHAPTER: szError = L"DB_E_BADCHAPTER"; break; case DB_E_NOTREENTRANT: szError = L"DB_E_NOTREENTRANT"; break; case E_FAIL: szError = L"E_FAIL"; break; case E_INVALIDARG: szError = L"E_INVALIDARG"; break; case E_OUTOFMEMORY: szError = L"E_OUTOFMEMORY"; break; case E_UNEXPECTED: szError = L"E_UNEXPECTED"; break; default: szError = buf; swprintf (buf, L"Unexpected error 0x%04x", sc); } //MessageBox ( 0, szError, L"GetRowsAt", MB_OK ); return fetchError; } //Fetch const unsigned cAtATime = 20; void CSearchQuery::WriteResults() { CBookMark bmk(DBBMK_FIRST); XArray xRows( cAtATime ); ULONG cRowsToGo = (ULONG) _cRowsTotal; CDynArrayInPlace awcBuf( 4096 ); int cwc = 0; while ( 0 != cRowsToGo ) { DBCOUNTITEM cFetched = 0; FetchResult res = Fetch( bmk, (LONG) ( _cRowsTotal - cRowsToGo ), __min( cAtATime, cRowsToGo ), cFetched, xRows.GetPointer(), 0 ); if ( ( fetchError == res ) || ( 0 == cFetched ) ) return; cRowsToGo -= (ULONG) cFetched; for ( ULONG row = 0; row < cFetched; row++ ) { WCHAR *pwcPath; _xRowset->GetData( xRows[ row ], _hBrowseAccessor, &pwcPath ); while ( *pwcPath ) awcBuf[ cwc++ ] = *pwcPath++; awcBuf[ cwc++ ] = L'\r'; awcBuf[ cwc++ ] = L'\n'; } _xRowset->ReleaseRows( cFetched, xRows.GetPointer(), 0, 0, 0 ); } awcBuf[ cwc++ ] = 0; PutInClipboard( awcBuf.Get() ); } //WriteResults const unsigned COUNT_HIDDEN_COLUMNS = 1; void CSearchQuery::SetupColumnMappingsAndAccessors() { // allocate the necessary resources unsigned cColumns = _columns.NumberOfColumns(); DBID aDbCols[maxBoundCols]; for ( unsigned iCol = 0; iCol < cColumns; iCol++ ) { // Get the next desired property DBTYPE propType; unsigned int uiWidth; DBID *pdbid; SCODE sc = _columnMapper.GetPropInfoFromName( _columns.GetColumn( iCol ), &pdbid, &propType, &uiWidth ); memcpy( &aDbCols[ iCol ], pdbid, sizeof DBID ); if (FAILED(sc)) THROW( CException( sc ) ); } IColumnsInfo *pColInfo = 0; SCODE sc = _xRowset->QueryInterface ( IID_IColumnsInfo, ( void ** )&pColInfo ); if (FAILED(sc)) THROW( CException( sc ) ); XInterface< IColumnsInfo > xColInfo( pColInfo ); DBORDINAL aColumnIds[ maxBoundCols ]; // Map the columns sc = pColInfo->MapColumnIDs( cColumns, aDbCols, aColumnIds ); if (FAILED(sc)) THROW (CException (sc)); // Map hidden columns DBID apsHidden[COUNT_HIDDEN_COLUMNS]; apsHidden[0] = dbcolPath; DBORDINAL aColumnIdHidden[COUNT_HIDDEN_COLUMNS]; sc = pColInfo->MapColumnIDs( 1, apsHidden, aColumnIdHidden ); if (FAILED(sc)) THROW (CException (sc)); // allocate the necessary resources and cache in the // object DBBINDING aGenBindings[maxBoundCols]; ULONG oCurrentOffset = 0; // Add each requested property to the binding for ( unsigned iBind = 0; iBind < cColumns; iBind++ ) { // Set up the binding array aGenBindings[ iBind ].iOrdinal = aColumnIds[ iBind ]; // Ordinal of column aGenBindings[ iBind ].obValue = oCurrentOffset; // Offset of data aGenBindings[ iBind ].obLength = 0, // Offset where length data is stored aGenBindings[ iBind ].obStatus = 0, // Status info for column written aGenBindings[ iBind ].pTypeInfo = 0, // Reserved aGenBindings[ iBind ].pObject = 0, // DBOBJECT structure aGenBindings[ iBind ].pBindExt = 0, // Ignored aGenBindings[ iBind ].dwPart = DBPART_VALUE; // Return data aGenBindings[ iBind ].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // Memory owne aGenBindings[ iBind ].eParamIO = 0; // eParamIo aGenBindings[ iBind ].cbMaxLen = sizeof(PROPVARIANT *); // Size of data to return aGenBindings[ iBind ].dwFlags = 0; // Reserved aGenBindings[ iBind ].wType = DBTYPE_VARIANT | DBTYPE_BYREF; // Type of return data aGenBindings[ iBind ].bPrecision = 0; // Precision to use aGenBindings[ iBind ].bScale = 0; // Scale to us oCurrentOffset += sizeof( PROPVARIANT *); } sc = _xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, cColumns, aGenBindings, 0, // cbRowSize &_hAccessor, 0 ); if (FAILED(sc)) THROW ( CException( sc ) ); DBBINDING aBrowBindings[1]; aBrowBindings[0] = dbbindingPath; aBrowBindings[0].iOrdinal = aColumnIdHidden[0]; sc = _xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 1, aBrowBindings, 0, // cbRowSize &_hBrowseAccessor, 0 ); if (FAILED(sc)) THROW ( CException( sc ) ); } //SetupColumnMappingsAndAccessors