1981 lines
57 KiB
C++
1981 lines
57 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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<ISupportErrorInfo> 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<IErrorInfo> xErrorInfo;
|
|
hr = GetErrorInfo( 0, xErrorInfo.GetPPointer() );
|
|
if ( xErrorInfo.IsNull() )
|
|
return hr;
|
|
|
|
// Get the IErrorRecord interface and get the count of errors.
|
|
|
|
XInterface<IErrorRecords> 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<IErrorInfo> xErrorInfoRec;
|
|
ERRORINFO ErrorInfo;
|
|
for ( unsigned i=0; i<cErrRecords; i++ )
|
|
{
|
|
// Get basic error information.
|
|
|
|
xErrorRecords->GetBasicErrorInfo( 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<WCHAR> & 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<WCHAR> 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<WCHAR> xColumns;
|
|
columns.MakeList( xColumns );
|
|
|
|
XArray<WCHAR> 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<ICommandTree> 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<ICommandText> 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<ICommandPrepare> 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<ICommandProperties> 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<DBPROPSET> xPropSet( pPropSet );
|
|
XCoMem<DBPROP> 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<ICommandProperties> 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<IErrorInfo> 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<HROW> xRows( cAtATime );
|
|
ULONG cRowsToGo = (ULONG) _cRowsTotal;
|
|
CDynArrayInPlace<WCHAR> 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
|
|
|