|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 2002.
//
// File: docstore.cxx
//
// Contents: Deals with the client side document store implementation.
//
// Classes: CClientDocStore
//
// History: 12-03-96 srikants Created
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
// for definition of CRequestQueue
#include <query.hxx>
#include <srequest.hxx>
#include <regacc.hxx>
#include <docstore.hxx>
#include <docname.hxx>
#include <qsession.hxx>
#include <cicat.hxx>
#include <cinulcat.hxx>
#include <seccache.hxx>
#include <dmnstart.hxx>
#include <propmap.hxx>
#include <notifyev.hxx>
#include <catalog.hxx>
#include <catarray.hxx>
#include <glbconst.hxx>
#include <cisvcex.hxx>
#include <cisvcfrm.hxx>
#include <cistore.hxx>
#include <enumstr.hxx>
#include <filterob.hxx>
#include <catadmin.hxx>
#include <fsci.hxx>
extern CCatArray Catalogs;
CRequestQueue * g_pFSCIRequestQueue = 0;
extern "C" const GUID clsidStorageFilterObject = { 0xaa205a4d, 0x681f, 0x11d0, { 0xa2, 0x43, 0x8, 0x0, 0x2b, 0x36, 0xfc, 0xa4 } };
extern "C" const GUID guidStorageDocStoreLocatorObject;
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::QueryInterface
//
// Synopsis: Supports IID_IUnknown
// IID_ICiCDocStore
// IID_ICiCDocStoreEx
// IID_ICiCPropertyStorage
// IID_ICiCDocNameToWorkidTranslator
// IID_ICiCDocNameToWorkidTranslatorEx
// IID_ICiCAdviseStatus
// IID_IFsCiAdmin
// IID_ICiCLangRes
//
// History: 12-03-96 srikants Created
// 2-14-97 mohamedn ICiCLangRes
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::QueryInterface( REFIID riid, void **ppvObject ) { Win4Assert( 0 != ppvObject );
if ( IID_ICiCDocStore == riid ) *ppvObject = (void *)((ICiCDocStore *)this); else if ( IID_ICiCPropertyStorage == riid ) *ppvObject = (void *)((ICiCPropertyStorage *)this); else if ( IID_ICiCDocNameToWorkidTranslator == riid ) *ppvObject = (void *)((ICiCDocNameToWorkidTranslator *)this); else if ( IID_ICiCDocNameToWorkidTranslatorEx == riid ) *ppvObject = (void *)((ICiCDocNameToWorkidTranslatorEx *)this); else if ( IID_ICiCAdviseStatus == riid ) *ppvObject = (void *)((ICiCAdviseStatus *)this); else if ( IID_IFsCiAdmin == riid ) *ppvObject = (void *)((IFsCiAdmin *)this); else if ( IID_ICiCLangRes == riid ) *ppvObject = (void *) ((ICiCLangRes *)this); else if ( IID_ICiCDocStoreEx == riid ) *ppvObject = (void *)((IUnknown *) (ICiCDocStoreEx *)this); else if ( IID_ICiCResourceMonitor == riid ) *ppvObject = (void *)((IUnknown *) (ICiCResourceMonitor *)this); else if ( IID_IUnknown == riid ) *ppvObject = (void *)((IUnknown *) (ICiCDocStore *)this); else { *ppvObject = 0; return E_NOINTERFACE; }
AddRef(); return S_OK; } //QueryInterface
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::AddRef
//
// History: 12-03-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CClientDocStore::AddRef() { return InterlockedIncrement(&_refCount); } //AddRef
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::Release
//
// History: 12-03-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CClientDocStore::Release() { Win4Assert( _refCount > 0 );
LONG refCount = InterlockedDecrement(&_refCount);
if ( refCount <= 0 ) delete this;
return (ULONG) refCount; } //Release
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::CClientDocStore
//
// Synopsis: Constructor of the NULL CiCDocStore object.
//
// Arguments: None
//
// History: Jul-09-97 KrishnaN Created
// 01-Nov-98 KLam Need to instantiate a CDiskFreeStatus
//
//----------------------------------------------------------------------------
CClientDocStore::CClientDocStore() : _sigClientDocStore(eSigClientDocStore), _refCount(1), _fNoQuery( FALSE ), _state(eUpdatesDisabled), _pCiCat(0), _pCiNullCat(0) { // Create the CiNullCat object.
_pCiNullCat = new CiNullCat( *this ); XPtr<CiNullCat> xCat( _pCiNullCat );
// Map std props only (second param)
_xPropMapper.Set( new CFwPropertyMapper( _pCiNullCat->GetPidLookupTable(), TRUE ) );
// Create CI Manager object.
_CreateCiManager();
_pCiNullCat->StartupCiFrameWork( _xCiManager.GetPointer() );
//
// Startup content index.
//
ICiStartup * pCiStartup; SCODE sc = _xCiManager->QueryInterface( IID_ICiStartup, (void **) &pCiStartup ); XInterface<ICiStartup> xStartup( pCiStartup ); if ( S_OK != sc ) { THROW( CException(sc) ); }
CI_STARTUP_INFO startupInfo; RtlZeroMemory( &startupInfo, sizeof(startupInfo) );
startupInfo.clsidDaemonClientMgr = clsidStorageFilterObject; startupInfo.startupFlags = CI_CONFIG_ENABLE_QUERYING ;
BOOL fAbort = FALSE;
sc = pCiStartup->StartupNullContentIndex( &startupInfo,0, &fAbort );
if ( FAILED(sc) ) { THROW( CException(sc) ); }
xCat->SetAdviseStatus(); xCat.Acquire(); }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::CClientDocStore
//
// Synopsis: Constructor of the CiCDocStore object.
//
// Arguments: [pwszPath] - Path of the directory where the files must be
// created.
// [pwszName] - Name of the Content Index.
//
// History: 12-03-96 srikants Created
// 02-17-98 kitmanh Added code to deal with read-only
// catalogs (the readOnly flag is
// passed down from CiCat to startupInfo)
// 07-Jan-99 klam Logged and event that initialization failed
//
//----------------------------------------------------------------------------
CClientDocStore::CClientDocStore( WCHAR const * pwszPath, BOOL fOpenForReadOnly, CDrvNotifArray & DrvNotifArray, WCHAR const * pwszName ) : _sigClientDocStore(eSigClientDocStore), _refCount(1), _state(eUpdatesDisabled), _pCiCat(0), _pCiNullCat(0), _fNoQuery( FALSE ), _pDrvNotifArray( &DrvNotifArray ) { ciDebugOut(( DEB_ITRACE, "CClientDocStore::CClinetDocStore.. fOpenForReadOnly == %d\n", fOpenForReadOnly )); ciDebugOut(( DEB_ITRACE, "CClientDocStore::CClinetDocStore.. pwszPath == %ws\n", pwszPath )); ciDebugOut(( DEB_ITRACE, "CClientDocStore::CClinetDocStore.. pwszName == %ws\n", pwszName ));
// Check if the catalog directory exists
if ( ( wcslen( pwszPath ) + wcslen( CAT_DIR ) ) >= MAX_PATH ) THROW( CException( STATUS_INVALID_PARAMETER ) );
WCHAR awcCatDir[ MAX_PATH ]; wcscpy( awcCatDir, pwszPath ); wcscat( awcCatDir, CAT_DIR );
WIN32_FILE_ATTRIBUTE_DATA fData; if ( GetFileAttributesEx( awcCatDir, GetFileExInfoStandard, &fData ) ) { // Is the catalog path a file or a directory?
if ( 0 == ( fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) THROW( CException( HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) ) ); } else { //
// You can get back both errors depending on what parts of the
// directory tree currently exist.
//
if ( ( ERROR_FILE_NOT_FOUND == GetLastError() ) || ( ERROR_PATH_NOT_FOUND == GetLastError() ) ) { // create the catalog directory with proper acls
CMachineAdmin admin; admin.CreateSubdirs( pwszPath ); } else THROW( CException() ); }
BOOL fLeaveCorruptCatalog; { // By default, delete corrupt catalogs.
CRegAccess reg( RTL_REGISTRY_CONTROL, wcsRegAdmin ); fLeaveCorruptCatalog = reg.Read( wcsLeaveCorruptCatalog, (ULONG) FALSE ); }
//
// Create the CiCat object.
//
BOOL fVersionChange = FALSE;
_pCiCat = new CiCat( *this, _workMan, pwszPath, fVersionChange, fOpenForReadOnly, *_pDrvNotifArray, pwszName, fLeaveCorruptCatalog );
XPtr<CiCat> xCat( _pCiCat );
// Create a CDiskFreeStatus object
_xDiskStatus.Set( new CDiskFreeStatus( pwszPath, xCat->GetRegParams()->GetMinDiskSpaceToLeave() ) );
// Map std props AND others (second param set to false)
_xPropMapper.Set( new CFwPropertyMapper( xCat->GetPidLookupTable(), FALSE ) );
//
// Create the perform counters object. This must be done before
// starting up rest of the content index.
//
Win4Assert( 0 != xCat->GetCatalogName() ); CPerfMon * pPerfMon = new CPerfMon( xCat->GetCatalogName() );
_xPerfMon.Set( pPerfMon );
//
// Create CI Manager object.
//
_CreateCiManager();
xCat->StartupCiFrameWork( _xCiManager.GetPointer() );
//
// Startup content index.
//
ICiStartup * pCiStartup; SCODE sc = _xCiManager->QueryInterface( IID_ICiStartup, (void **) &pCiStartup ); XInterface<ICiStartup> xStartup( pCiStartup ); if ( S_OK != sc ) THROW( CException(sc) );
BOOL fFullScanNeeded = FALSE;
CI_STARTUP_INFO startupInfo; RtlZeroMemory( &startupInfo, sizeof(startupInfo) );
startupInfo.clsidDaemonClientMgr = clsidStorageFilterObject; startupInfo.startupFlags = CI_CONFIG_ENABLE_INDEXING | CI_CONFIG_ENABLE_QUERYING ;
if ( xCat->IsReadOnly() ) startupInfo.startupFlags |= CI_CONFIG_READONLY;
if ( xCat->IsCiDataCorrupt() || xCat->IsFsCiDataCorrupt() || fVersionChange ) { if ( fLeaveCorruptCatalog ) { Win4Assert( !"leaving corrupt catalog" ); THROW( CException( CI_CORRUPT_CATALOG ) ); }
if ( xCat->IsCiDataCorrupt() || xCat->IsFsCiDataCorrupt() ) { ciDebugOut(( DEB_ERROR, "Persistent CI/FSCI Data Corruption. It will be emptied \n" )); //Win4Assert( !"Persistent CI/FSCI Data Corruption" );
}
startupInfo.startupFlags |= (ULONG) CI_CONFIG_EMPTY_DATA; xCat->LogEvent( CCiStatusMonitor::eCiRemoved ); fFullScanNeeded = TRUE; }
BOOL fAbort = FALSE;
#if CIDBG==1
#if 0
_FillLoadFilesInfo( startupInfo ); #endif // 0
#endif // CIDBG==1
sc = pCiStartup->StartupContentIndex( xCat->GetCatDir(), &startupInfo,0, &fAbort );
#if CIDBG==1
#if 0
if ( SUCCEEDED(sc) ) { WCHAR wszBackupPath[MAX_PATH]; wcscpy( wszBackupPath, xCat->GetCatDir() ); wcscat( wszBackupPath, L"\\backup"); ICiPersistIncrFile * pIPersist = 0; sc = _xCiManager->QueryInterface( IID_ICiPersistIncrFile, (void **) &pIPersist ); Win4Assert( SUCCEEDED(sc) ); _xSaveTest.Set( new CCiSaveTest( wszBackupPath, pIPersist, xCat.GetReference() ) ); pIPersist->Release(); } _ClearLoadFilesInfo( startupInfo ); #endif // 0
#endif // CIDBG==1
if ( FAILED(sc) ) { if ( !IsCiCorruptStatus( sc ) && sc != CI_INCORRECT_VERSION ) { ciDebugOut(( DEB_ERROR, "Failed to startupci. Error 0x%X\n", sc )); xCat->LogEvent ( CCiStatusMonitor::eCiError, sc ); THROW( CException(sc) ); }
if ( fLeaveCorruptCatalog ) { Win4Assert( !"leaving corrupt catalog" ); THROW( CException( sc ) ); }
ciDebugOut(( DEB_ERROR, "ContentIndex is corrupt. It will be emptied\n" )); //Win4Assert( !"Startup CI Data Corruption" );
xCat->LogEvent( CCiStatusMonitor::eCiRemoved );
//
// Content Index is corrupt. Ask CI to delete the contentIndex and
// start afresh.
//
startupInfo.startupFlags |= CI_CONFIG_EMPTY_DATA; sc = pCiStartup->StartupContentIndex( xCat->GetCatDir(), &startupInfo, 0, &fAbort );
if ( FAILED(sc) ) { THROW( CException(sc) ); }
fFullScanNeeded = TRUE; }
xCat->ClearCiDataCorrupt();
xCat->InitIf( fLeaveCorruptCatalog );
//
// Optimization - we may want to just force an "add" of the documents in the
// property store rather than a full scan of the corpus.
//
if ( fFullScanNeeded ) xCat->MarkFullScanNeeded();
xCat.Acquire(); }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::_CreateCiManager
//
// Synopsis: Creates the CI manager by doing a CoCreateInstance of the
// ICiControl.
//
// History: 1-31-97 srikants Created
//
//----------------------------------------------------------------------------
void CClientDocStore::_CreateCiManager() { //
// We have to create the ICiManager also now.
//
ICiControl * pICiControl = 0; GUID clsIdCiControl = CLSID_CiControl; SCODE sc = CoCreateInstance( clsIdCiControl, NULL, CLSCTX_INPROC_SERVER, IID_ICiControl, (void **) &pICiControl );
if ( 0 == pICiControl ) { ciDebugOut(( DEB_ERROR, "Cannot CoCreateInstance IID_ICiControl. Error (0x%X)\n", sc )); THROW( CException(sc) ); }
XInterface<ICiControl> xCiControl( pICiControl );
ICiManager * pICiManager = 0;
sc = xCiControl->CreateContentIndex( this, &pICiManager ); if ( 0 == pICiManager ) { ciDebugOut(( DEB_ERROR, "Cannot Get ContentIndex. Error 0x%X\n", sc ));
THROW( CException(sc) ); }
_xCiManager.Set( pICiManager ); } //_CreateCiManager
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::~CClientDocStore
//
// Synopsis: Destructor of the client document store.
//
// History: 12-03-96 srikants Created
//
//----------------------------------------------------------------------------
CClientDocStore::~CClientDocStore() { delete _pCiCat; delete _pCiNullCat; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::IsPropertyCached
//
// Synopsis: Tests if the given property is cached in the property
// store or not.
//
// Arguments: [pPropSpec] - Property to test.
// [pfValue] - Set to TRUE if cached; FALSE o/w
//
// Returns: S_OK if successful;
// Some other error code if not in a valid state.
//
// History: 12-03-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::IsPropertyCached( const FULLPROPSPEC * pPropSpec, BOOL * pfValue ) { Win4Assert( 0 != pfValue ); Win4Assert( 0 != pPropSpec );
Win4Assert( 0 != _pCiCat );
CFullPropSpec const & ps = *((CFullPropSpec const *) pPropSpec);
SCODE sc = S_OK;
TRY { *pfValue = _pCiCat->IsPropertyCached( ps ); } CATCH( CException, e ) { sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Function: StoreProperty
//
// Synopsis: Stores the given property for the workid.
//
// Arguments: [workid] - WorkId of the document
// [pPropSpec] - Property to be stored
// [pPropVariant] - Value of the property
//
// Returns: S_OK if successful
// CI_E_PROPERTY_NOT_CACHED if it is not a cached property.
// Other error code
//
// History: 12-03-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::StoreProperty( WORKID workid, const FULLPROPSPEC * pPropSpec, const PROPVARIANT * pPropVariant ) { Win4Assert( 0 != pPropSpec ); Win4Assert( 0 != pPropVariant );
Win4Assert( 0 != _pCiCat );
CFullPropSpec const & ps = *((CFullPropSpec const *) pPropSpec); CStorageVariant const & var = *(ConvertToStgVariant( pPropVariant ));
SCODE sc = S_OK;
TRY { BOOL fStored = _pCiCat->StoreValue( workid, ps, var ); if ( !fStored ) sc = CI_E_PROPERTY_NOT_CACHED; } CATCH( CException, e ) { sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::FlushPropertyStore
//
// Synopsis: Causes the property store to flush.
//
// History: 06-30-97 KrishnaN Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::FlushPropertyStore (void) { SCODE sc = S_OK;
Win4Assert(_pCiCat);
TRY { //
// Flushing the property store at this point (just before a shadow
// merge) is necessary for Push model filtering, but not for clients
// like fsci that do Pull model filtering. The changelog,
// scopetable, and property store are already tightly linked and
// flushed appropriately.
//
// _pCiCat->FlushPropertyStore();
} CATCH( CException, e) { sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::GetClientStatus
//
// Synopsis: Retrieves the client status information
//
// Arguments: [pStatus] - Will have the status information on output.
//
// History: 12-05-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::GetClientStatus( CI_CLIENT_STATUS * pStatus ) { Win4Assert( 0 != pStatus );
SCODE sc = S_OK;
TRY { ULONG cPendingScans, state; if (_pCiNullCat) _pCiNullCat->CatalogState( pStatus->cDocuments, cPendingScans, state ); else _pCiCat->CatalogState( pStatus->cDocuments, cPendingScans, state ); } CATCH( CException,e ) { sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::GetContentIndex
//
// Synopsis: Returns the ICiManager if there is one and we are not
// in a shutdown sequence.
//
// Arguments: [ppICiManager] - ICiManager pointer
//
//
// Returns: S_OK if successful
// CI_E_SHUTDOWN if shutdown
// CI_E_NOT_INITIALIZED if not yet initialized
//
// History: 12-10-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::GetContentIndex( ICiManager ** ppICiManager) { Win4Assert( 0 != ppICiManager );
SCODE sc = S_OK;
CLock lock(_mutex);
if ( !_IsShutdown() && _xCiManager.GetPointer() ) { _xCiManager->AddRef(); *ppICiManager = _xCiManager.GetPointer(); } else { *ppICiManager = 0; if ( _IsShutdown() ) sc = CI_E_SHUTDOWN; else sc = CI_E_NOT_INITIALIZED; }
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::EnableUpdates
//
// Synopsis: Enables updates from document store.
//
// History: 12-09-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::EnableUpdates() { SCODE sc = S_OK;
Win4Assert(_pCiCat);
BOOL fEnableUpdateNotifies = FALSE;
TRY { // =============================================================
{ CLock lock(_mutex); if ( _IsShutdown() ) { ciDebugOut(( DEB_ERROR, "CClientDocStore::EnableUpdates called after shutdown\n" )); THROW( CException( CI_E_SHUTDOWN ) ); }
fEnableUpdateNotifies = _AreUpdatesDisabled(); _state = eUpdatesEnabled; } // =============================================================
//
// Notifications are not disabled on DisableUpdates and so only scans/usns
// need to be scheduled, which is done by NoLokClearDiskFull
//
if ( fEnableUpdateNotifies ) _pCiCat->NoLokClearDiskFull(); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CClientDocStore::EnableUpdates caught error (0x%X)\n", e.GetErrorCode() ));
sc = e.GetErrorCode(); } END_CATCH
if ( S_OK != sc && fEnableUpdateNotifies ) { //
// There was a failure while enabling udpates. We must set
// the state back to indicate that updates are not enabled.
//
CLock lock(_mutex); _LokDisableUpdates(); }
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::DisableUpdates
//
// Synopsis: Disables further updates and prevents update notifications
// until enabled via the "EnableUpdates" call.
//
// History: 12-31-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::DisableUpdates( BOOL fIncremental, CI_DISABLE_UPDATE_REASON dwReason ) { Win4Assert(_pCiCat);
if ( _IsShutdown() ) return CI_E_SHUTDOWN;
SCODE sc = S_OK;
TRY { { CLock lock(_mutex); _LokDisableUpdates(); }
if ( fIncremental ) { _pCiCat->MarkIncrScanNeeded(); _pCiCat->NoLokProcessDiskFull(); } else { if ( dwReason == CI_CORRUPT_INDEX ) _pCiCat->HandleError( CI_CORRUPT_DATABASE ); else { _pCiCat->MarkFullScanNeeded(); _pCiCat->NoLokProcessDiskFull(); } } } CATCH( CException, e ) { sc = e.GetErrorCode(); ciDebugOut(( DEB_ERROR, "Error (0x%X) while disabling updates\n", sc )); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::ProcessCiDaemonTermination
//
// Synopsis: Processes the death of CiDaemon. Creates a work item to
// restart the filter daemon.
//
// History: 12-23-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::ProcessCiDaemonTermination( DWORD dwStatus ) { //
// The CiDaemon process is dead; Just start filtering again.
//
SCODE sc = S_OK;
TRY { CStartFilterDaemon * pWorkItem = new CStartFilterDaemon( *this, _workMan ); _workMan.AddToWorkList( pWorkItem );
pWorkItem->AddToWorkQueue(); pWorkItem->Release(); } CATCH( CException, e) { ciDebugOut(( DEB_ERROR, "Failed to create a work item for start filter daemon. Error 0x%X\n", e.GetErrorCode() ));
sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::CheckPointChangesFlushed
//
// Synopsis: Processes a changelog flush.
//
// History: 12-23-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::CheckPointChangesFlushed( FILETIME ftFlushed, ULONG cEntries, USN_FLUSH_INFO const * const *ppUsnEntries) { SCODE sc = S_OK;
Win4Assert(_pCiCat);
TRY { _pCiCat->ProcessChangesFlush( ftFlushed, cEntries, ppUsnEntries ); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "CheckPointChangesFlushed failed. Error 0x%X\n", e.GetErrorCode() ));
sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::MarkDocUnReachable
//
// Synopsis: Marks that the document was not reachable when an attempt
// was made to filter it.
//
// Arguments: [wid] - The WORKID which could not be reached.
//
// History: 12-10-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::MarkDocUnReachable( WORKID wid ) { SCODE sc = S_OK;
Win4Assert(_pCiCat);
TRY { _pCiCat->MarkUnReachable( wid ); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CClientDocStore::MarkDocUnReachable caught exception (0x%X)\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::StoreSecurity
//
// Synopsis: Stores the given security data for the workid.
//
// Arguments: [wid] - WorkId of the document
// [pbData] - Security data buffer
// [cbData] - NUmber of bytes in the security data buffer
//
// History: 1-15-97 srikants Created
//
// Notes: We may want to eliminate this call and instead have the
// security be stored as a special property.
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::StoreSecurity( WORKID wid, BYTE const * pbData, ULONG cbData ) { SCODE sc = S_OK;
Win4Assert(_pCiCat);
TRY { _pCiCat->StoreSecurity( wid, (PSECURITY_DESCRIPTOR) pbData, cbData ); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CClientDocStore::MarkDocUnReachable caught exception (0x%X)\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::IsNoQuery
//
// Synopsis: Check if the docstore is set to NoQuery
//
// Arguments: [fNoQuery] - Output
//
// History: 05-26-98 kitmanh Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::IsNoQuery( BOOL * fNoQuery ) { SCODE sc = S_OK;
if ( !fNoQuery ) sc = STATUS_INVALID_PARAMETER; else *fNoQuery = _fNoQuery; return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::GetPropertyMapper
//
// Synopsis: Retrieves the interface to IPropertyMapper.
//
// Arguments: [ppIPropertyMapper] - On output, will have the IPropertyMapper.
//
// Returns: S_OK if successful;
// E_NOTIMPL if property mapper is not supported by doc store.
// Other error code if there is a failure.
// Modifies:
//
// History: 12-31-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::GetPropertyMapper( IPropertyMapper ** ppIPropertyMapper) { SCODE sc = S_OK;
TRY { Win4Assert( 0 != _xPropMapper.GetPointer() );
*ppIPropertyMapper = _xPropMapper.GetPointer(); _xPropMapper->AddRef(); } CATCH( CException, e ) { ciDebugOut(( DEB_ERROR, "CClientDocStore::MarkDocUnReachable caught exception (0x%X)\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ICiCDocNameToWorkidTranslatorEx methods.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::QueryDocName
//
// Synopsis: Creates a new doc name object and returns its interface.
//
// Arguments: [ppICiCDocName] - [out] Will have a pointer to the
// ICiCDocName object filled in.
//
// History: 12-05-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::QueryDocName( ICiCDocName ** ppICiCDocName ) { Win4Assert( 0 != ppICiCDocName );
SCODE sc = S_OK;
TRY { *ppICiCDocName = new CCiCDocName; } CATCH( CException,e ) { sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::WorkIdToDocName
//
// Synopsis: Translates a WorkId to a document name.
//
// Arguments: [workid] - [in] WorkId to translate
// [pICiCDocName] - [out] Will be filled in with the document
// name on output.
//
// Returns: S_OK if successfully converted.
// CI_E_BUFFERTOOSMALL if the buffer is not big enough
// Other error code.
//
// History: 12-05-96 srikants Created
//
// Notes: This method may be one of the most frequently called methods.
// Look for optimizations (esp. can the TRY/CATCH be avoided?)
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::WorkIdToDocName( WORKID workid, ICiCDocName * pICiCDocName ) { return InternalWorkIdToDocName( workid, pICiCDocName, FALSE ); }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::DocNameToWorkId
//
// Synopsis: Converts a document name to a WorkId.
//
// Arguments: [pICiCDocName] - Document Name
// [pWorkid] - [out] Will have the workid on output.
//
// Returns: S_OK if successful; Error code otherwise.
//
// History: 12-05-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::DocNameToWorkId( ICiCDocName const * pICiCDocName, WORKID * pWorkid ) { SCODE sc = S_OK;
Win4Assert( 0 != pICiCDocName ); Win4Assert( 0 != pWorkid );
TRY { CCiCDocName const * pDocName = (CCiCDocName const *) pICiCDocName; *pWorkid = _pCiCat->PathToWorkId( pDocName->GetPath(), TRUE ); } CATCH( CException, e ) { sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::WorkIdToAccurateDocName
//
// Synopsis: Translates a WorkId 'accurately' to a document name.
//
// Arguments: [workid] - [in] WorkId to translate
// [pICiCDocName] - [out] Will be filled in with the document
// name on output.
//
// Returns: S_OK if successfully converted.
// CI_E_BUFFERTOOSMALL if the buffer is not big enough
// Other error code.
//
// History: 31-Dec-1998 KyleP Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::WorkIdToAccurateDocName( WORKID workid, ICiCDocName * pICiCDocName ) { return InternalWorkIdToDocName( workid, pICiCDocName, TRUE ); }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::_GetPerfIndex
//
// Synopsis: An internal helper function to get the offset of the perfmon
// counter in the perfmon shared memory.
//
// Arguments: [name] - Name of the counter.
// [index] - Index of the name
//
// Returns: TRUE if successfully looked up; FALSE on failure.
//
// History: 12-06-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CClientDocStore::_GetPerfIndex( CI_PERF_COUNTER_NAME name, ULONG & index ) { BOOL fOk= TRUE;
ULONG offset = 0;
switch ( name ) { case CI_PERF_NUM_WORDLIST:
offset = NUM_WORDLIST; break;
case CI_PERF_NUM_PERSISTENT_INDEXES:
offset = NUM_PERSISTENT_INDEX; break;
case CI_PERF_INDEX_SIZE:
offset = INDEX_SIZE; break;
case CI_PERF_FILES_TO_BE_FILTERED:
offset = FILES_TO_BE_FILTERED; break;
case CI_PERF_NUM_UNIQUE_KEY:
offset = NUM_UNIQUE_KEY; break;
case CI_PERF_RUNNING_QUERIES:
offset = RUNNING_QUERIES; break;
case CI_PERF_MERGE_PROGRESS:
offset = MERGE_PROGRESS; break;
case CI_PERF_DOCUMENTS_FILTERED:
offset = DOCUMENTS_FILTERED; break;
case CI_PERF_NUM_DOCUMENTS:
offset = NUM_DOCUMENTS; break;
case CI_PERF_TOTAL_QUERIES:
offset = TOTAL_QUERIES; break;
case CI_PERF_DEFERRED_FILTER_FILES:
offset = DEFERRED_FILTER_FILES; break;
default:
fOk = FALSE; }
if ( fOk ) index = offset + KERNEL_USER_INDEX;
return fOk; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::SetPerfCounterValue
//
// Synopsis: Sets the value of the perfmon counter.
//
// Arguments: [name] - Name of the counter
// [value] - Value to be set.
//
// Returns: S_OK if a valid perfmon name; E_INVALIDARG if the perfmon
// name is not correct.
//
// History: 12-06-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::SetPerfCounterValue( CI_PERF_COUNTER_NAME name, long value ) { // true for the NULL catalog
if ( _xPerfMon.IsNull() ) return S_OK; SCODE sc = S_OK; ULONG index;
//
// CPerfMon::Update must not throw.
//
if ( _GetPerfIndex( name, index ) ) _xPerfMon->Update( index, value ); else sc = E_INVALIDARG;
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::IncrementPerfCounterValue
//
// Synopsis: Increments the value of the perfmon counter.
//
// Arguments: [name] - Name of the counter
//
// Returns: S_OK if a valid perfmon name; E_INVALIDARG if the perfmon
// name is not correct.
//
// History: 1-15-97 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::IncrementPerfCounterValue( CI_PERF_COUNTER_NAME name ) { // true for the NULL catalog
if ( _xPerfMon.IsNull() ) return S_OK;
SCODE sc = S_OK; ULONG index;
//
// CPerfMon::Update must not throw.
//
if ( _GetPerfIndex( name, index ) ) _xPerfMon->Increment( index ); else sc = E_INVALIDARG;
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::DecrementPerfCounterValue
//
// Synopsis: Decrements the value of the perfmon counter.
//
// Arguments: [name] - Name of the counter
//
// Returns: S_OK if a valid perfmon name; E_INVALIDARG if the perfmon
// name is not correct.
//
// History: 1-15-97 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::DecrementPerfCounterValue( CI_PERF_COUNTER_NAME name ) { // true for the NULL catalog
if ( _xPerfMon.IsNull() ) return S_OK;
SCODE sc = S_OK; ULONG index;
//
// CPerfMon::Update must not throw.
//
if ( _GetPerfIndex( name, index ) ) _xPerfMon->Decrement( index ); else sc = E_INVALIDARG;
return sc; } //DecrementPerfCounterValue
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::GetPerfCounterValue
//
// Synopsis: Retrieves the value of the perfmon counter.
//
// Arguments: [name] - [see SetPerfCounterValue]
// [pValue] - "
//
// History: 12-06-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::GetPerfCounterValue( CI_PERF_COUNTER_NAME name, long * pValue ) { // true for the NULL catalog
if ( _xPerfMon.IsNull() ) return S_OK;
Win4Assert( pValue );
ULONG index; SCODE sc = S_OK;
if ( _GetPerfIndex( name, index ) ) *pValue = _xPerfMon->GetCurrValue( index ); else sc = E_INVALIDARG;
return sc; }
//+------------------------------------------------------
//
// Member: CClientDocStore::NotifyEvent
//
// Synopsis: Reports the passed in event and arguments to eventlog
//
// Arguments: [fType ] - Type of event
// [eventId] - Message file event identifier
// [nParams] - Number of substitution arguments being passed
// [aParams] - pointer to PROPVARIANT array of substitution args.
// [cbData ] - number of bytes in supplemental raw data.
// [data ] - pointer to block of supplemental data.
//
// Returns: S_OK upon success, value of the exception if an exception
// is thrown.
//
// History: 12-30-96 mohamedn Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::NotifyEvent( WORD fType, DWORD eventId, ULONG nParams, const PROPVARIANT *aParams, ULONG cbData, void* data)
{
SCODE sc = S_OK;
TRY { CClientNotifyEvent notifyEvent(fType,eventId,nParams,aParams,cbData,data); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Exception 0x%X in CClientDocStore::NotifyEvent()\n", e.GetErrorCode() ));
sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::NotifyStatus
//
// Synopsis: When a special status is being notified.
//
// Returns: S_OK always.
//
// History: 12-05-96 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::NotifyStatus( CI_NOTIFY_STATUS_VALUE status, ULONG nParams, const PROPVARIANT * aParams ) {
SCODE sc = S_OK;
switch ( status ) { case CI_NOTIFY_FILTERING_FAILURE:
Win4Assert( nParams == 1 ); Win4Assert( aParams[0].vt == VT_I4 || aParams[0].vt == VT_UI4 );
if ( 1 == nParams ) _ReportFilteringFailure( aParams[0].ulVal ); break;
case CI_NOTIFY_FILTER_TOO_MANY_BLOCKS: case CI_NOTIFY_FILTER_EMBEDDING_FAILURE: //
// This is possible in the in-proc filtering because
// the advise status is provided by docstore for filtering
// also.
//
{ CStorageFilterObjNotifyStatus notify( this ); sc = notify.NotifyStatus( status, nParams, aParams ); } break;
default:
Win4Assert( !"Invalid Case Stmt" ); break; };
return sc; }
//
// IFsCiAdmin methods.
//
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::ForceMerge
//
// Synopsis: Forces a master merge on the given partition id.
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::ForceMerge( PARTITIONID partId ) {
SCODE sc = S_OK;
TRY { if ( 0 == _pCiCat ) THROW( CException( E_INVALIDARG ) );
sc = _pCiCat->ForceMerge( partId ); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::ForceMerge\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::AbortMerge
//
// Synopsis: Aborts any in-progress merge on the given partition id.
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::AbortMerge( PARTITIONID partId ) {
SCODE sc = S_OK;
TRY { if ( 0 == _pCiCat ) THROW( CException( E_INVALIDARG ) );
sc = _pCiCat->AbortMerge( partId ); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::AbortMerge\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::CiState
//
// Synopsis: Retrieves the CI_STATE information.
//
// Arguments: [pCiState] - On output, will contain the cistate data.
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::CiState( CI_STATE * pCiState ) {
SCODE sc = S_OK;
TRY { if (_pCiNullCat) sc = _pCiNullCat->CiState( *pCiState ); else sc = _pCiCat->CiState( *pCiState ); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::CiState\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::UpdateDocuments
//
// Synopsis: Forces a scan on the given root.
//
// Arguments: [rootPath] - Root to force a scan on.
// [flag] - Indicating if this is a full scan or a partial
// scan.
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::UpdateDocuments( const WCHAR *rootPath, ULONG flag ) {
SCODE sc = S_OK;
TRY { if ( 0 == _pCiCat ) THROW( CException( E_INVALIDARG ) );
_pCiCat->UpdateDocuments( rootPath, flag); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::UpdateDocuments\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::AddScopeToCI
//
// Synopsis: Adds the given scope to ContentIndex for indexing.
//
// Arguments: [rootPath] - Path to add
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::AddScopeToCI( const WCHAR *rootPath ) {
SCODE sc = S_OK;
TRY { if ( 0 == _pCiCat ) THROW( CException( E_INVALIDARG ) );
_pCiCat->AddScopeToCI( rootPath ); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::AddScopeToCI\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::RemoveScopeFromCI
//
// Synopsis: Removes the given scope from CI.
//
// Arguments: [rootPath] - Scope to remove.
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::RemoveScopeFromCI( const WCHAR *rootPath ) {
SCODE sc = S_OK;
TRY { if ( 0 == _pCiCat ) THROW( CException( E_INVALIDARG ) );
_pCiCat->RemoveScopeFromCI( rootPath, FALSE ); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::RemoveScopeFromCI\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::BeginCacheTransaction
//
// Synopsis: Begins a property cache transaction.
//
// Arguments: [pulToken] - Output - Transaction "cookie".
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::BeginCacheTransaction( ULONG_PTR * pulToken ) {
SCODE sc = S_OK;
TRY { if ( 0 == _pCiCat ) THROW( CException( E_INVALIDARG ) );
*pulToken = _pCiCat->BeginCacheTransaction(); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::BeginCacheTransaction\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::SetupCache
//
// Synopsis: Sets up the property for storing the property cache.
//
// Arguments: [ps] -
// [vt] -
// [cbMaxLen] -
// [ulToken] -
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::SetupCache( const FULLPROPSPEC *ps, ULONG vt, ULONG cbMaxLen, ULONG_PTR ulToken, BOOL fCanBeModified, DWORD dwStoreLevel) { Win4Assert(PRIMARY_STORE == dwStoreLevel || SECONDARY_STORE == dwStoreLevel);
SCODE sc = S_OK;
TRY { if ( 0 == _pCiCat ) THROW( CException( E_INVALIDARG ) );
CFullPropSpec * pFullPropSpec = (CFullPropSpec *)ps; _pCiCat->SetupCache( *pFullPropSpec, vt, cbMaxLen, ulToken, fCanBeModified, dwStoreLevel ); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::RemoveVirtualScope\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::EndCacheTransaction
//
// Synopsis: Ends the property cache transaction.
//
// Arguments: [ulToken] -
// [fCommit] -
//
// History: 2-12-97 srikants Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::EndCacheTransaction( ULONG_PTR ulToken, BOOL fCommit ) { SCODE sc = S_OK;
TRY { if ( 0 == _pCiCat ) THROW( CException( E_INVALIDARG ) );
_pCiCat->EndCacheTransaction( ulToken, fCommit ); } CATCH( CException,e ) { ciDebugOut(( DEB_ERROR, "Error 0x%X in CClientDocStore::RemoveVirtualScope\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+-------------------------------------------------------------------------
//
// Method: CClientDocStore::IsIoHigh, public
//
// Returns: S_OK if system is in a high i/o state, S_FALSE if it is not.
//
// History: 21-Jul-1998 KyleP Created
//
//--------------------------------------------------------------------------
SCODE CClientDocStore::IsIoHigh( BOOL * pfAbort ) { // Windows XP removed support for the IOCTL we used to measure disk
// usage.
return S_FALSE;
} //IsIoHigh
//
// Non-Interface methods.
//
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::_StartFiltering
//
// Synopsis: Asks the CiManager to start filter daemon and resume
// filtering.
//
// History: 12-09-96 srikants Created
//
//----------------------------------------------------------------------------
void CClientDocStore::_StartFiltering() { XInterface<ICiManager> xManager;
// =============================================
{ CLock lock(_mutex); if ( !_IsShutdown() && _xCiManager.GetPointer() ) { _xCiManager->AddRef(); xManager.Set( _xCiManager.GetPointer() ); } else { ciDebugOut(( DEB_WARN, "Already Shutdown or CI not started. Cannot start filtering\n" )); return; } } // =============================================
//
// Get the startup data and serialize it.
//
WCHAR const * pwszName = _pCiCat->GetName(); WCHAR const * pwszCatDir = _pCiCat->GetDriveName();
CDaemonStartupData startupData( pwszCatDir, pwszName );
ULONG cbData; BYTE * pbData = startupData.Serialize( cbData ); XArray<BYTE> xBuf; xBuf.Set(cbData,pbData);
xManager->StartFiltering( cbData, pbData );
//
// Note: Should we be worried about StartFiltering failing ??
//
}
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::Shutdown
//
// Synopsis: Intiates a shutdown of the document store and the
// CiManager associated with the document store.
//
// History: 12-05-96 srikants Created
//
//----------------------------------------------------------------------------
void CClientDocStore::Shutdown() { TRY { Win4Assert( !_IsShutdown() );
// =============================================
{ CLock lock(_mutex); _LokMarkShutdown(); } // =============================================
#if CIDBG==1
if ( !_xSaveTest.IsNull() ) _xSaveTest->InitiateShutdown(); #endif // CIDBG==1
if (_pCiNullCat) _pCiNullCat->ShutdownPhase1(); else _pCiCat->ShutdownPhase1();
if ( _xCiManager.GetPointer() ) { _xCiManager->Shutdown(); _xCiManager.Free(); }
#if CIDBG==1
if ( !_xSaveTest.IsNull() ) _xSaveTest->WaitForDeath(); #endif //CIDBG==1
if (_pCiNullCat) _pCiNullCat->ShutdownPhase2(); else _pCiCat->ShutdownPhase2(); } CATCH( CException, e) { ciDebugOut(( DEB_ERROR, "Error (0x%X) in CClientDocStore::Shutdown\n", e.GetErrorCode() )); } END_CATCH
}
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::GetName
//
// Synopsis: Gets the catalog name (if any)
//
// History: 12-05-96 srikants Created
//
//----------------------------------------------------------------------------
WCHAR const * CClientDocStore::GetName() { if (_pCiNullCat) return _pCiNullCat->GetName(); else return _pCiCat->GetName(); }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::_SetCiCatRecovered
//
// Synopsis: Sets that the CiCat is recovered and if updates were
// enabled by Content Index, it asks CiCat to start scans
// and notifications.
//
// History: 12-09-96 srikants Created
// 02-25-98 kitmanh if catalog is read-only, don't filter
//
// Notes: This method MUST be called in SYSTEM context only. This
// starts filtering which in turn creates the filter daemon
// proxy thread and that must be in SYSTEM context.
//
//----------------------------------------------------------------------------
void CClientDocStore::_SetCiCatRecovered() { Win4Assert( 0 != _pCiCat );
Win4Assert( !CImpersonateSystem::IsImpersonated() );
BOOL fEnableFilering = FALSE;
//don't start filtering if catalog is read-only
if ( !_pCiCat->IsReadOnly() ) {
// ==================================================
{ CLock lock(_mutex);
fEnableFilering = _AreUpdatesEnabled(); } // ==================================================
//
// Since CiCat has been recovered fully, filtering can be started
// if we are running with "indexing" enabled.
//
_StartFiltering();
if ( fEnableFilering ) _pCiCat->EnableUpdateNotifies(); }
}
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::IsLowOnDiskSpace
//
// Synopsis: Returns the status of free space on the disk used by
// docstore (client) based on the last check.
//
// Returns: TRUE if low on free disk space; FALSE o/w
//
// History: 12-10-96 srikants Created
//
// Notes: This does not actually make an I/O. Just returns an in-memory
// status.
//
//----------------------------------------------------------------------------
BOOL CClientDocStore::IsLowOnDiskSpace() const { Win4Assert ( !_xDiskStatus.IsNull() ); return _xDiskStatus->IsLow(); }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::VerifyIfLowOnDiskSpace
//
// Synopsis: Verifies the free disk space situation by making an I/O call
// to the O/S.
//
// Returns: TRUE if disk is getting to be full; FALSE o/w
//
// History: 12-10-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CClientDocStore::VerifyIfLowOnDiskSpace() { Win4Assert ( !_xDiskStatus.IsNull() ); _xDiskStatus->UpdateDiskLowInfo(); return _xDiskStatus->IsLow(); }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::_ReportFilteringFailure
//
// Synopsis: Reports that there was filtering failure on the given wid.
//
// Arguments: [wid] - Workid which failed to filter.
//
// History: 1-24-97 srikants Created
//
//----------------------------------------------------------------------------
void CClientDocStore::_ReportFilteringFailure( WORKID wid ) { ciDebugOut(( DEB_IWARN, "Warning: unsuccessful attempts to filter workid 0x%X; " "cancelling\n", wid )); }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::GetQuerySession
//
// Synopsis: Returns a query session object
//
// Returns: [ppICiCQuerySession] -- Session object is returned here
//
// History: 22-Jan-97 SitaramR Created
//
//----------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CClientDocStore::GetQuerySession( ICiCQuerySession **ppICiCQuerySession) { SCODE sc = S_OK;
TRY { CQuerySession *pQuerySession = _pCiNullCat ? new CQuerySession(*_pCiNullCat) : new CQuerySession(*_pCiCat ); XInterface<CQuerySession> xSession( pQuerySession );
sc = pQuerySession->QueryInterface( IID_ICiCQuerySession, (void **) ppICiCQuerySession );
if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "GetQuerySession - QI failed 0x%x\n", sc )); } } CATCH( CException,e ) { sc = e.GetErrorCode();
vqDebugOut(( DEB_ERROR, "CClientDocStore::GetQuerySession - Exception caught 0x%x\n", sc )); } END_CATCH
return sc;
} //GetQuerySession
//+-------------------------------------------------------------------------
//
// Function: RegisterDLL
//
// Synopsis: Calls DllRegisterServer on the fully qualified path
//
// History: 19-Jun-97
//
//--------------------------------------------------------------------------
DWORD RegisterDLL( WCHAR const * pwcDLL ) { // All Index Server dlls are currently in system32
DWORD dwErr = NO_ERROR;
HINSTANCE hDll = LoadLibraryEx( pwcDLL, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
if( 0 != hDll ) { SCODE (STDAPICALLTYPE *pfnDllRegisterServer)(); pfnDllRegisterServer = (HRESULT (STDAPICALLTYPE *)()) GetProcAddress(hDll, "DllRegisterServer");
if ( 0 != pfnDllRegisterServer ) { TRY { SCODE sc = (*pfnDllRegisterServer)(); if ( S_OK != sc ) { // no way to map a scode to a win32 error
dwErr = ERROR_INVALID_FUNCTION; } } CATCH( CException, e ) { Win4Assert( !"DllRegisterServer threw an exception" ); ciDebugOut(( DEB_ERROR, "caught 0x%x registering '%ws'\n", e.GetErrorCode(), pwcDLL )); } END_CATCH; } else dwErr = GetLastError();
FreeLibrary( hDll ); } else { dwErr = GetLastError(); }
return dwErr; } //RegisterDll
//+-------------------------------------------------------------------------
//
// Function: RegisterKnownDlls
//
// Synopsis: Gets the value of the DLL's from the registry and then
// registers all of them if not done so already.
//
// History: 19-Jun-97 t-elainc Created
//
//--------------------------------------------------------------------------
void RegisterKnownDlls() { static fKnownDllsRegistered = FALSE;
if ( fKnownDllsRegistered ) return;
fKnownDllsRegistered = TRUE;
TRY { // get registry value
HKEY hkey; int errorcode = RegOpenKeyEx( HKEY_LOCAL_MACHINE, wcsRegAdminSubKey, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkey );
if (NO_ERROR == errorcode) { SRegKey xKey( hkey );
WCHAR awcPath[4000]; DWORD cwcPath = sizeof awcPath / sizeof WCHAR;
int anothererrorcode = RegQueryValueEx( hkey, L"DLLsToRegister", 0, 0, (BYTE *)awcPath, &cwcPath ); if (NO_ERROR == anothererrorcode) { // parse the string
WCHAR* pwcCurrFile = awcPath;
//for each file call RegisterDLL
while ( 0 != *pwcCurrFile ) { RegisterDLL(pwcCurrFile); pwcCurrFile += wcslen( pwcCurrFile) + 1; } } } } CATCH( CException, ex ) { ciDebugOut(( DEB_FORCE, "exception %#x registering dlls\n", ex.GetErrorCode() )); } END_CATCH; } //RegisterKnownDlls
//+-------------------------------------------------------------------------
//
// Function: OpenCatalogsOnRemovableDrives
//
// Synopsis: Opens the catalogs found on the roots of removable drives
// that are not yet opened.
//
// History: 28-Apr-99 dlee Created.
//
//--------------------------------------------------------------------------
void OpenCatalogsOnRemovableDrives() { CRegAccess reg( RTL_REGISTRY_CONTROL, wcsRegAdmin );
if ( 0 == reg.Read( wcsMountRemovableCatalogs, CI_AUTO_MOUNT_CATALOGS_DEFAULT ) ) return;
// Determine which drives exist in this bitmask
DWORD dwDriveMask = GetLogicalDrives(); dwDriveMask >>= 2;
WCHAR awcPath[5]; wcscpy( awcPath, L"c:\\" );
// loop through all the drives c-z
for ( WCHAR wc = L'C'; wc <= L'Z'; wc++ ) { DWORD dwTemp = ( dwDriveMask & 1 ); dwDriveMask >>= 1;
if ( 0 != dwTemp ) { if ( IsRemovableDrive( wc ) ) { awcPath[0] = wc;
if ( 0 == Catalogs.GetDocStore( awcPath ) ) Catalogs.TryToStartCatalogOnRemovableVol( wc, g_pFSCIRequestQueue ); } } } } //OpenCatalogsOnRemovableDrives
//+-------------------------------------------------------------------------
//
// Function: OpenCatalogsInRegistry
//
// Synopsis: Opens the catalogs listed in the registry. If there are
// problems opening a catalog, it is skipped -- no sense
// bringing down the service over a messed-up registry entry.
//
// History: 10-Oct-96 dlee Created.
//
//--------------------------------------------------------------------------
void OpenCatalogsInRegistry( BOOL fOpenForReadOnly = FALSE ) { //
// Side effect of starting up -- register dlls
//
RegisterKnownDlls();
ciDebugOut(( DEB_ITRACE, "OpenCatalogsInRegistry: fOpenForReadOnly == %d\n", fOpenForReadOnly ));
HKEY hKey; if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, wcsRegCatalogsSubKey, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey ) ) { SRegKey xKey( hKey ); DWORD iSubKey = 0;
do { FILETIME ft; WCHAR awcName[MAX_PATH]; DWORD cwcName = sizeof awcName / sizeof WCHAR; LONG err = RegEnumKeyEx( hKey, iSubKey, awcName, &cwcName, 0, 0, 0, &ft );
// either error or end of enumeration
if ( ERROR_SUCCESS != err ) break;
iSubKey++;
HKEY hCatName; if ( ERROR_SUCCESS == RegOpenKeyEx( hKey, awcName, 0, KEY_QUERY_VALUE, &hCatName ) ) { SRegKey xCatNameKey( hCatName ); WCHAR awcPath[MAX_PATH]; DWORD cbPath = sizeof awcPath; if ( ERROR_SUCCESS == RegQueryValueEx( hCatName, wcsCatalogLocation, 0, 0, (BYTE *)awcPath, &cbPath ) ) { Catalogs.GetNamedOne( awcPath, awcName, fOpenForReadOnly ); } } } while ( TRUE ); }
OpenCatalogsOnRemovableDrives(); } //OpenCatalogsInRegistry
//+-------------------------------------------------------------------------
//
// Function: OpenOneCatalog
//
// Synopsis: Opens the catalog specified for R/W or R/O.
// set the fNoQuery flag in the docstore if fNoQuery (passed in)
// is TRUE
//
// Arguments: [wcCatName] -- name of the catalog
// [fReadOnly] -- opening for readOnly
// [fNoQuery] -- opening the catalog as NoQuery
//
// History: 27-Apr-98 kitmanh Created.
// 12-May-98 kitmanh Added fNoQuery
//
//--------------------------------------------------------------------------
void OpenOneCatalog( WCHAR const * wcCatName, BOOL fReadOnly, BOOL fNoQuery = FALSE ) { ciDebugOut(( DEB_ITRACE, "OpenOneCatalog\n" )); ciDebugOut(( DEB_ITRACE, "fNoQuery is %d\n", fNoQuery ));
HKEY hKey; WCHAR wcsKey[MAX_PATH];
if ( ( wcslen( wcsRegCatalogsSubKey ) + wcslen( wcCatName ) + 1 ) >= MAX_PATH ) THROW( CException( STATUS_INVALID_PARAMETER ) );
wcscpy( wcsKey, wcsRegCatalogsSubKey ); wcscat( wcsKey, L"\\" ); wcscat( wcsKey, wcCatName );
if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, wcsKey, 0, KEY_QUERY_VALUE, &hKey ) ) { SRegKey xKey( hKey );
WCHAR awcPath[MAX_PATH]; DWORD cwcPath = sizeof awcPath / sizeof WCHAR; if ( ERROR_SUCCESS == RegQueryValueEx( hKey, wcsCatalogLocation, 0, 0, (BYTE *)awcPath, &cwcPath ) ) { Catalogs.GetNamedOne( awcPath, wcCatName, fReadOnly, fNoQuery ); } } } //OpenOneCatalog
// Mutex and Event to synchronize start, stop, pause and continue
CStaticMutexSem g_mtxStartStop; CEventSem * g_pevtPauseContinue = 0;
//+-------------------------------------------------------------------------
//
// Function: StartCiSvcWork
//
// Synopsis: Entry point for doing the CI service work. The thread does
// not exit until StopCiSvcWork is called.
//
// Argument: [DrvNotifArray] -- reference to the only one
// DriveNotificationArray
//
// History: 16-Sep-96 dlee Created.
// 16-Sep-98 kitmanh Added parameter DrvNotifArray
//
//--------------------------------------------------------------------------
void StartCiSvcWork( CDrvNotifArray & DrvNotifArray ) { TRY { //
// This lock is released when cisvc is initialized enough that
// StopCiSvcWork can safely be called.
//
CReleasableLock lock( g_mtxStartStop ); ciDebugOut(( DEB_ITRACE, "StartCiSvcWork got the lock\n" ));
Catalogs.Init(); TheFrameworkClientWorkQueue.Init();
XCom xcom;
// Create the CRequestQueue here, so it'll stay around for
// the whole time
ULONG cMaxCachedServerItems, cMaxSimultaneousRequests, cmsDefaultClientTimeout, cMinClientIdleTime, cmsStartupDelay; BOOL fMinimizeWorkingSet;
{ CRegAccess reg( RTL_REGISTRY_CONTROL, wcsRegAdmin ); cMaxCachedServerItems = reg.Read( wcsMaxCachedPipes, CI_MAX_CACHED_PIPES_DEFAULT, CI_MAX_CACHED_PIPES_MIN, CI_MAX_CACHED_PIPES_MAX ); cMaxSimultaneousRequests = reg.Read( wcsMaxSimultaneousRequests, CI_MAX_SIMULTANEOUS_REQUESTS_DEFAULT, CI_MAX_SIMULTANEOUS_REQUESTS_MIN, CI_MAX_SIMULTANEOUS_REQUESTS_MAX ); cmsDefaultClientTimeout = reg.Read( wcsRequestTimeout, CI_REQUEST_TIMEOUT_DEFAULT, CI_REQUEST_TIMEOUT_MIN, CI_REQUEST_TIMEOUT_MAX ); fMinimizeWorkingSet = reg.Read( wcsMinimizeWorkingSet, (DWORD)CI_MINIMIZE_WORKINGSET_DEFAULT );
// convert seconds to milliseconds
cMinClientIdleTime = 1000 * reg.Read( wcsMinClientIdleTime, CI_MIN_CLIENT_IDLE_TIME ); cmsStartupDelay = reg.Read( wcsStartupDelay, CI_STARTUP_DELAY_DEFAULT, CI_STARTUP_DELAY_MIN, CI_STARTUP_DELAY_MAX ); }
Catalogs.SetDrvNotifArray( &DrvNotifArray );
// Create the CRequestQueue here, so it'll stay around for the
// whole time.
CRequestQueue queue( cMaxCachedServerItems, cMaxSimultaneousRequests, cmsDefaultClientTimeout, fMinimizeWorkingSet, cMinClientIdleTime, cmsStartupDelay, guidStorageDocStoreLocatorObject );
g_pFSCIRequestQueue = &queue;
while ( TRUE ) { // request lock to prevent problems of sequence of
// net pause, net stop and net start (in any order
// and combo.)
if ( !lock.IsHeld() ) lock.Request();
ciDebugOut(( DEB_ITRACE, "About to call StartFWCiSvcWork\n" ));
StartFWCiSvcWork( lock, &queue, *g_pevtPauseContinue);
ciDebugOut(( DEB_ITRACE, "StartCiSvcWork:: fell out from StartFWCiSvcWork\n" ));
// In the case where a net pause happened
// instead of checking IsNetPause and IsNetcontinue, loop thru the
// dynarray to reopen docstores. and clear the SCarry when done
// note if array.count == 0, work as normal.
SCWorkItem * WorkItem; WCHAR wcCatName[MAX_PATH];
if ( !queue.IsShutdown() ) { // something is on the _stateChangeArray to be handled
for ( unsigned i = 0; i < queue.SCArrayCount(); i++ ) { WorkItem = queue.GetSCItem(i); if ( WorkItem->StoppedCat ) { wcsncpy( wcCatName, WorkItem->StoppedCat, sizeof wcCatName / sizeof WCHAR ); wcCatName[ (sizeof wcCatName / sizeof WCHAR) - 1 ] = 0; } else if ( eNoCatWork != WorkItem->type ) { wcsncpy( wcCatName, ( (CClientDocStore *)(WorkItem->pDocStore) )->GetName(), sizeof wcCatName / sizeof WCHAR ); wcCatName[ (sizeof wcCatName / sizeof WCHAR) - 1 ] = 0; } switch ( WorkItem->type ) { case eCatRO: //flush cat
if ( !WorkItem->StoppedCat ) Catalogs.FlushOne( WorkItem->pDocStore ); else Catalogs.RmFromStopArray( wcCatName );
OpenOneCatalog( wcCatName, TRUE ); break; case eCatW: if ( !WorkItem->StoppedCat ) Catalogs.FlushOne( WorkItem->pDocStore ); else Catalogs.RmFromStopArray( wcCatName );
OpenOneCatalog( wcCatName, FALSE ); break; case eStopCat: if ( !WorkItem->StoppedCat ) { Catalogs.FlushOne( WorkItem->pDocStore ); ciDebugOut(( DEB_ITRACE, "Catalogs.IsCatStopped( %ws ) == %d\n", wcCatName, Catalogs.IsCatStopped( wcCatName ) )); } break; case eNoQuery: if ( !WorkItem->StoppedCat ) Catalogs.FlushOne( WorkItem->pDocStore ); else Catalogs.RmFromStopArray( wcCatName );
OpenOneCatalog( wcCatName, !(WorkItem->fNoQueryRW), TRUE ); break; case eNetPause: case eNetContinue: case eNetStop: case eNoCatWork: // These cases are handled later
break; default: Win4Assert( !"The eCisvcActionType specified is unknown" ); } } }
if ( queue.IsNetPause() ) { Win4Assert( !queue.IsNetContinue() );
Catalogs.ClearStopArray(); // reset the queue
queue.ReStart();
//closecatalog
Catalogs.Flush();
OpenCatalogsInRegistry( TRUE ); //reopen catalogs for r/o
//Win4Assert( !"Done Pausing" );
} // a net continue happened
else if ( queue.IsNetContinue() ) { Win4Assert( !queue.IsNetPause() );
Catalogs.ClearStopArray(); // reset the queue
queue.ReStart();
ciDebugOut(( DEB_ITRACE, "YES, net continued*********\n" )); ciDebugOut(( DEB_ITRACE, "About to flush catalogs.\n" )); //closecatalog
Catalogs.Flush(); ciDebugOut(( DEB_ITRACE, "About to OpenCatalogsInRegistry for r/w \n" ));
OpenCatalogsInRegistry( FALSE ); //reopen catalogs for r/w
ciDebugOut(( DEB_ITRACE, "Done opening catalogs in registry\n" ));
//Win4Assert( !"Done Continuing" );
} else { if ( !( queue.IsShutdown() || queue.IsNetStop() ) ) queue.ReStart(); else { ciDebugOut(( DEB_ITRACE, "StartCisvcWork: Breaking out of the loop and ready to shutdown the service\n" )); Catalogs.ClearStopArray(); break; } } } } CATCH( CException, ex ) { ciDebugOut(( DEB_WARN, "StartCiSvcWork2 exception error 0x%x\n", ex.GetErrorCode() )); } END_CATCH; } //StartCiSvcWork
//+-------------------------------------------------------------------------
//
// Function: StopCiSvcWork
//
// Synopsis: Entry point for stopping the CI service work.
//
// Arguments: [type] -- type of work to do
// [wcVol] -- volume letter (for eLockVol only)
//
// History: 16-Sep-96 dlee Created.
// 12-Aug-98 kitmanh Returned an SCODE
//
//--------------------------------------------------------------------------
SCODE StopCiSvcWork( ECiSvcActionType type, WCHAR wcVol ) { ciDebugOut(( DEB_ITRACE, "StopCiSvcWork is trying to get the lock\n" )); CReleasableLock lock( g_mtxStartStop ); ciDebugOut(( DEB_ITRACE, "StopCiSvcWork got the lock\n" ));
// If the main SCM thread didn't get very far, this will be the case.
if ( 0 == g_pevtPauseContinue ) return S_OK;
g_pevtPauseContinue->Reset();
SCODE sc = StopFWCiSvcWork( type, &lock, g_pevtPauseContinue, wcVol );
lock.Release();
ciDebugOut(( DEB_ITRACE, "StopCiSvcWork.. After StopFwCiSvcWork has returned. type == %d\n", type ));
// Do not block if shutdown has intialized
if ( STATUS_TOO_LATE == sc ) return S_OK;
// block if pausing or continuing
if ( eNetStop != type ) { ciDebugOut(( DEB_ITRACE, "StopCiSvcWork.. After StopFwCiSvcWork has returned. Block on g_pevtPauseContinue\n" )); g_pevtPauseContinue->Wait(); lock.Request(); g_pevtPauseContinue->Reset(); lock.Release(); ciDebugOut(( DEB_ITRACE, "StopCiSvcWork.. After StopFwCiSvcWork has returned. Reusme on g_pevtPauseContinue\n" )); }
return sc; } //StopCiSvcWork
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::InternalWorkIdToDocName
//
// Synopsis: Translates a WorkId to a document name.
//
// Arguments: [workid] - [in] WorkId to translate
// [pICiCDocName] - [out] Will be filled in with the document
// name on output.
// [fAccurate] - Use the slow and accurate call
//
// Returns: S_OK if successfully converted.
// CI_E_BUFFERTOOSMALL if the buffer is not big enough
// Other error code.
//
// History: 31-Dec-1998 KyleP Created
//
//----------------------------------------------------------------------------
SCODE CClientDocStore::InternalWorkIdToDocName( WORKID workid, ICiCDocName * pICiCDocName, BOOL fAccurate ) { Win4Assert( 0 != pICiCDocName ); Win4Assert(_pCiCat);
CCiCDocName * pDocName = (CCiCDocName *) pICiCDocName;
CLowerFunnyPath funnyPath;
SCODE sc = S_OK;
TRY { //
// We should avoid a memory copy here. Copy into the CCiCDocName
// directly. If this turns out to be a performance problem fix
// it. It isn't now.
//
unsigned cwc;
if ( fAccurate ) cwc = _pCiCat->WorkIdToAccuratePath( workid, funnyPath ); else cwc = _pCiCat->WorkIdToPath( workid, funnyPath ); //
// CiCat::WorkIdToPath makes an EMPTY string when a doc
// is deleted.
//
if ( cwc > 1 ) { // this is not a deleted file.
pDocName->SetPath( funnyPath.GetActualPath(), cwc ); } else { // this is a deleted file.
sc = CI_S_WORKID_DELETED; } } CATCH( CException, e ) { sc = e.GetErrorCode(); } END_CATCH
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CClientDocStore::ClearNonStoragePropertiesForWid
//
// Synopsis: Clear non-storage properties from the property storage for wid
//
// Arguments: [wid] - workid
//
// Returns: S_OK if successful;
//
// History: 10-06-2000 kitmanh Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CClientDocStore::ClearNonStoragePropertiesForWid( WORKID wid ) { SCODE sc = S_OK; TRY { _pCiCat->ClearNonStoragePropertiesForWid( wid ); } CATCH( CException, e ) { sc = e.GetErrorCode(); } END_CATCH
return sc; }
|