//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997 - 2000. // // File: lgplist.cxx // // Contents: Index server wide local/global property list class. // // History: 05 May 1997 Alanw Created // 27 Aug 1997 KrishnaN Moved from ixsso to querylib // //---------------------------------------------------------------------------- #include #pragma hdrstop //----------------------------------------------------------------------------- // Include Files //----------------------------------------------------------------------------- #include #include #include #include #include // class declaration CStaticPropertyList GlobalStaticList; CPropListFile * CLocalGlobalPropertyList::_pGlobalPropListFile = 0; CStaticMutexSem g_mtxFilePropList; // regulates access to the global file prop list. CRegChangeEvent CGlobalPropFileRefresher::_regChangeEvent( wcsRegCommonAdminTree, TRUE ); WCHAR CGlobalPropFileRefresher::_wcsFileName[]; FILETIME CGlobalPropFileRefresher::_ftFile; DWORD CGlobalPropFileRefresher::_dwLastCheckMoment; BOOL CGlobalPropFileRefresher::_fInited = FALSE; HKEY CGlobalPropFileRefresher::_hKey; LONG CGlobalPropFileRefresher::_lRegReturn; CGlobalPropFileRefresher gRefresher; //+------------------------------------------------------------------------- // // Member: CDefColumnRegEntry::CDefColumnRegEntry, public // // Synopsis: Constructor for registry param object // // Arguments: [pwcName] - 0 or name of the catalog from the registry // // History: 12-Oct-96 dlee Created // //-------------------------------------------------------------------------- CDefColumnRegEntry::CDefColumnRegEntry() { // set default wcscpy( _awcDefaultColumnFile, L"" ); } //CDefColumnRegEntry //+------------------------------------------------------------------------- // // Member: CDefColumnRegEntry::Refresh, public // // Synopsis: Reads the values from the registry // // History: 12-Oct-96 dlee Added header, reorganized // //-------------------------------------------------------------------------- void CDefColumnRegEntry::Refresh( BOOL fUseDefaultsOnFailure ) { TRY { // Query the registry. CRegAccess regAdmin( RTL_REGISTRY_CONTROL, wcsRegCommonAdmin ); XPtrST xRegValue(regAdmin.Read(wcsDefaultColumnFile, L"")); wcsncpy( _awcDefaultColumnFile, xRegValue.GetPointer(), MAX_PATH ); } CATCH (CException, e) { // Only store defaults when told to do so -- the params // are still in good shape at this point and are more // accurate than the default settings. if ( fUseDefaultsOnFailure ) wcscpy( _awcDefaultColumnFile, L"" ); } END_CATCH } //Refresh //----------------------------------------------------------------------------- // // Member: CPropListFile::CPropListFile // // Synopsis: Constructor of a property list from a file // // Arguments: [pDefaultList] -- The default property list // [fDynamicRefresh] -- True, if list should be dynamically // refreshed when file changes. // [pwcPropFile] -- The property file. If this is null, // use the registry. // [ulCodePage] -- Codepage to interpret the property list. // // Notes: // // History: 08 Sep 1997 KrishnaN Created // //----------------------------------------------------------------------------- CPropListFile::CPropListFile( CEmptyPropertyList *pDefaultList, BOOL fDynamicRefresh, WCHAR const * pwcPropFile, ULONG ulCodePage ) : CCombinedPropertyList(pDefaultList, ulCodePage), _scError( S_OK ), _iErrorLine( 0 ), _xErrorFile( 0 ), _ulCodePage( ulCodePage ), _fDynamicRefresh( fDynamicRefresh ), _dwLastCheckMoment( GetTickCount() ) { if (pwcPropFile) Load(pwcPropFile); else { WCHAR wszFile[MAX_PATH+1]; _RegParams.GetDefaultColumnFile( wszFile, MAX_PATH ); Load(wszFile); } } CPropListFile::~CPropListFile() { ClearList(); } //+--------------------------------------------------------------------------- // // Member: CPropListFile::Load - public // // Synopsis: Loads the file into the property list. // // Arguments: [pwszFile] -- File name for property definitions // // History: 06 May 1997 AlanW Created // //---------------------------------------------------------------------------- void CPropListFile::Load(WCHAR const * const pwszFile ) { if (0 == pwszFile || 0 == *pwszFile) return; CImpersonateSystem impersonateSystem; // prevent multiple loads at the same time CLock lock(_mtx); // Erase any previous error settings. _scError = S_OK; _iErrorLine = 0; _xErrorFile.Free(); SCODE sc = GetLastWriteTime(pwszFile, _ftFile); if (S_OK == sc) { // ParseNameFile should not throw exceptions. sc = ParseNameFile( pwszFile ); } else sc = QPLIST_E_CANT_OPEN_FILE; if (FAILED(sc)) { qutilDebugOut(( DEB_WARN, "Can't open column file named %ws\n", pwszFile )); _scError = sc; _iErrorLine = 0; if (0 == _xErrorFile.GetPointer()) { WCHAR * pwcErrorFile = new WCHAR[wcslen(pwszFile)+1]; wcscpy(pwcErrorFile, pwszFile); _xErrorFile.Set( pwcErrorFile ); } } } //+--------------------------------------------------------------------------- // // Member: CPropListFile::IsMapUpToDate - public // // Synopsis: Determines if the file is still vaid, or if it has // changed since it was last read. // // History: 06 May 1997 AlanW Created // //---------------------------------------------------------------------------- SCODE CPropListFile::IsMapUpToDate() { // // Has the file been modified since last loaded? // FILETIME ft; SCODE sc = GetLastWriteTime(_xFileName.GetPointer(), ft); if (FAILED(sc)) return E_HANDLE; if ( (_ftFile.dwLowDateTime == ft.dwLowDateTime) && (_ftFile.dwHighDateTime == ft.dwHighDateTime) ) return S_OK; return S_FALSE; } //+--------------------------------------------------------------------------- // // Method: CPropListFile::GetLastWriteTime, static // // Purpose: Gets the last change time of the file specified // // Arguments: [wcsFileName] - name of file to get last write time of // [ftLastWrite] - on return, last mod. time of file // // Returns: SCODE - S_OK if successful. // // History: 96/Jan/23 DwightKr Created // 96/Mar/13 DwightKr Changed to use GetFileAttributesEx() // //---------------------------------------------------------------------------- SCODE CPropListFile::GetLastWriteTime( WCHAR const * wcsFileName, FILETIME & ftLastWrite ) { if ( 0 == wcsFileName ) return E_INVALIDARG; WIN32_FIND_DATA ffData; if ( !GetFileAttributesEx( wcsFileName, GetFileExInfoStandard, &ffData ) ) { ULONG error = GetLastError(); qutilDebugOut(( DEB_IERROR, "Unable to GetFileAttributesEx(%ws) GetLastError=0x%x\n", wcsFileName, error )); return HRESULT_FROM_WIN32(error); } ftLastWrite = ffData.ftLastWriteTime; return S_OK; } //+--------------------------------------------------------------------------- // // Member: CPropListfile::ParseNameFile, public // // Synopsis: Parses the given file name and creates a list of 'friendly // name' to CDbColId equivalences. // // Arguments: szFileName -- name of the file to parse // // History: 17-May-94 t-jeffc Created. // //---------------------------------------------------------------------------- SCODE CPropListFile::ParseNameFile( WCHAR const * wcsFileName ) { int iLength; BOOL fRememberFileName = FALSE; if( wcsFileName == 0 ) { // use the last specified property file if( !_xFileName.IsNull() ) { wcsFileName = _xFileName.GetPointer(); iLength = wcslen( wcsFileName ) + 1; } else return QPLIST_E_CANT_OPEN_FILE; } else { // make a copy of the file name _xFileName.Free(); iLength = wcslen( wcsFileName ) + 1; _xFileName.Set( new WCHAR[ iLength ] ); memcpy( _xFileName.GetPointer(), wcsFileName, iLength * sizeof WCHAR ); } CSFile pfile( OpenFileFromPath( wcsFileName ) ); if( pfile == 0 ) return QPLIST_E_CANT_OPEN_FILE; // // Process a line at a time, skip ahead until we find the [Names] // or [Query] section and process lines within that section only. // BOOL fNameSection = FALSE; SCODE sc = S_OK; int iLine = 0; for( ;; ) { TRY { iLine++; // line buffers char szLine[ MAX_LINE_LENGTH ]; WCHAR wcsLine[ MAX_LINE_LENGTH ]; if( !fgets( szLine, MAX_LINE_LENGTH, pfile ) ) { if( feof( pfile ) ) break; THROW( CPListException( QPLIST_E_READ_ERROR, iLine ) ); } // // Skip ahead until we find a [Names] section // if ( *szLine == '[' ) { if ( _strnicmp(szLine, "[Names]", 7) == 0 ) { fNameSection = TRUE; continue; } else { fNameSection = FALSE; continue; } } else if ( *szLine == '#' ) { continue; } if ( fNameSection ) { if( MultiByteToWideChar( _ulCodePage, MB_COMPOSITE, szLine, -1, wcsLine, MAX_LINE_LENGTH ) == 0 ) { THROW( CException() ); } CQueryScanner scanner( wcsLine, FALSE ); XPtr propentry; CPropertyList::ParseOneLine( scanner, iLine, propentry ); if (propentry.GetPointer()) { AddEntry( propentry.GetPointer(), iLine ); propentry.Acquire(); } } } CATCH( CPListException, e ) { qutilDebugOut(( DEB_WARN, "Plist exception %08x caught parsing default column file at line %d. Line ignored.\n", e.GetErrorCode(), e.GetLine() )); sc = _scError = e.GetErrorCode(); _iErrorLine = e.GetLine(); fRememberFileName = TRUE; } AND_CATCH ( CException, e ) { qutilDebugOut(( DEB_WARN, "Exception caught parsing default column file %08x\n", e.GetErrorCode() )); sc = _scError = e.GetErrorCode(); fRememberFileName = TRUE; } END_CATCH if (fRememberFileName && 0 == _xErrorFile.GetPointer()) { fRememberFileName = FALSE; WCHAR * pwcErrorFile = new WCHAR[wcslen(wcsFileName)+1]; wcscpy(pwcErrorFile, wcsFileName); _xErrorFile.Set( pwcErrorFile ); } } return sc; } //+--------------------------------------------------------------------------- // // Member: CPropListfile::CheckError, public // // Synopsis: Checks if there was an error in the parsing of the file. // // Arguments: iLine -- error line number returned here. // ppFile -- file name returned here. can be 0 if not needed. // // History: 17-Sep-97 KrishnaN Created. // //---------------------------------------------------------------------------- SCODE CPropListFile::CheckError( ULONG & iLine, WCHAR ** ppFile ) { iLine = _iErrorLine; if (ppFile) *ppFile = _xErrorFile.GetPointer(); return _scError; } //+--------------------------------------------------------------------------- // // Member: CPropListfile::Refresh, private // // Synopsis: Reloads the property list if prop. file has been modified. // // History: 15-Sep-97 KrishnaN Created. // //---------------------------------------------------------------------------- void CPropListFile::Refresh() { if (!_fDynamicRefresh) return; // Don't check more than once in a few seconds if (abs(GetTickCount() - _dwLastCheckMoment) < REFRESH_INTERVAL) return; _dwLastCheckMoment = GetTickCount(); if (S_OK != IsMapUpToDate()) { // // Reload. // ClearList(); Load(_xFileName.GetPointer()); } } //+--------------------------------------------------------------------------- // // Member: CCombinedPropertyList::Find, public // // Synopsis: Attempt to find an entry in the list. // // Arguments: wcsName -- friendly property name to find // // Returns a pointer to the CPropEntry if found, 0 otherwise. // // History: 17-May-94 t-jeffc Created. // 28-Aug-97 KrishnaN modified. // //---------------------------------------------------------------------------- CPropEntry const * CCombinedPropertyList::Find( WCHAR const * wcsName ) { if( 0 == wcsName ) return 0; // // First look in the default list, and if not found, look // in the overrides. // CPropEntry const * ppentry = _xDefaultList->Find(wcsName); if (ppentry) return ppentry; return (_xOverrideList.GetPointer() ? _xOverrideList->Find(wcsName) : 0); } //+--------------------------------------------------------------------------- // // Member: CCombinedPropertyList::Next, public // // Synopsis: Gets the next property during an enumeration // // Returns: The next property entry or 0 for end of enumeration // // History: 21-Jul-97 dlee Moved from .hxx and added header // //---------------------------------------------------------------------------- CPropEntry const * CCombinedPropertyList::Next() { // // First look in the static list, and if not found, look in the overrides. // CPropEntry const *pEntry = 0; if (!_fOverrides) pEntry = _xDefaultList->Next(); if (pEntry) return pEntry; _fOverrides = TRUE; return (_xOverrideList.GetPointer() ? _xOverrideList->Next() : 0); } //Next //+--------------------------------------------------------------------------- // // Member: CCombinedPropertyList::InitIterator, public // // Synopsis: Initialize the iterator // // History: 29-Aug-97 KrishnaN Created // //---------------------------------------------------------------------------- void CCombinedPropertyList::InitIterator() { // causes the default list to be iterated before the overrides _fOverrides = FALSE; // Initialize the iterators of the two lists _xDefaultList->InitIterator(); if (_xOverrideList.GetPointer()) _xOverrideList->InitIterator(); } //InitIterator //+--------------------------------------------------------------------------- // // Member: CCombinedPropertyList::AddEntry, private // // Synopsis: Adds a CPropEntry to the overriding list. Verifies that the name // isn't already in the default list or the overriding list. // // Arguments: ppentryNew -- pointer to the CPropEntry to add // iLine -- line number we're parsing // // History: 11-Sep-97 KrishnaN Created. // //---------------------------------------------------------------------------- void CCombinedPropertyList::AddEntry( CPropEntry * ppentryNew, int iLine ) { // protect _xOverrideList CLock lock(_mtxAdd); if (0 == _xOverrideList.GetPointer()) { _xOverrideList.Set(new CPropertyList(_ulCodePage)); } // // We do not allow entries in the override list that have the same name // as the default list. // if( _xDefaultList->Find( ppentryNew->GetName() ) || _xOverrideList->Find( ppentryNew->GetName() ) ) THROW( CPListException( QPLIST_E_DUPLICATE, iLine ) ); _xOverrideList->AddEntry(ppentryNew, iLine); } //+--------------------------------------------------------------------------- // // Member: CCombinedPropertyList::ClearList, public // // Synopsis: Frees the memory used by the list. // // History: 11-Sep-97 KrishnaN Created. // //---------------------------------------------------------------------------- void CCombinedPropertyList::ClearList() { // protect _xOverrideList CLock lock(_mtxAdd); // // Just free it now. It will be created, if necessary, // on AddEntry(). // _xOverrideList.Free(); } //+--------------------------------------------------------------------------- // // Member: CCombinedPropertyList::SetDefaultList, public // // Synopsis: Sets the default list. // // Arguments: pDefaultList -- The list to set as default list. // // History: 11-Sep-97 KrishnaN Created. // //---------------------------------------------------------------------------- void CCombinedPropertyList::SetDefaultList(CEmptyPropertyList *pDefaultList) { Win4Assert(pDefaultList); // Jettison any existing property list _xDefaultList.Free(); _xDefaultList.Set(pDefaultList); pDefaultList->AddRef(); } //+--------------------------------------------------------------------------- // // Member: CCombinedPropertyList::GetCount, public // // Synopsis: Returns cardinality of list. // // History: 11-Sep-97 KrishnaN Created. // //---------------------------------------------------------------------------- ULONG CCombinedPropertyList::GetCount() { ULONG ulTotal = _xDefaultList->GetCount(); if (_xOverrideList.GetPointer()) { ulTotal += _xOverrideList->GetCount(); } return ulTotal; } //+--------------------------------------------------------------------------- // // Member: CCombinedPropertyList::GetAllEntries, public // // Synopsis: Returns cardinality of list. // // History: 11-Sep-97 KrishnaN Created. // //---------------------------------------------------------------------------- SCODE CCombinedPropertyList::GetAllEntries(CPropEntry **ppPropEntries, ULONG ulMaxCount) { ULONG ulSize = _xDefaultList->GetCount(); // get the first set of entries from the default list SCODE sc = _xDefaultList->GetAllEntries(ppPropEntries, min(ulSize, ulMaxCount)); // get the remaining entries from the override list if (S_OK == sc && ulMaxCount > ulSize && _xOverrideList.GetPointer()) { sc = _xOverrideList->GetAllEntries(ppPropEntries+ulSize, ulMaxCount-ulSize); } return sc; } //----------------------------------------------------------------------------- // // Member: CLocalGlobalPropertyList::CLocalGlobalPropertyList // // Synopsis: Constructor of a overridable file based property list. // The file name will always be obtained through the registry. // // Arguments: [ulCodePage] -- Codepage to interpret the property list. // // Notes: // // History: 08 Sep 1997 KrishnaN Created // //----------------------------------------------------------------------------- CLocalGlobalPropertyList::CLocalGlobalPropertyList( ULONG ulCodePage ) : CCombinedPropertyList(ulCodePage), _ulCodePage( ulCodePage ), _dwLastCheckMoment( GetTickCount() ), _mtx() { XInterface xPropListFile(GetGlobalPropListFile()); SetDefaultList(xPropListFile.GetPointer()); } //----------------------------------------------------------------------------- // // Member: CLocalGlobalPropertyList::CLocalGlobalPropertyList // // Synopsis: Constructor of a overridable file based property list. // The file name will always be obtained through the registry. // // Arguments: [pDefaultList] -- The default property list // [fDynamicRefresh] -- True, if list should be dynamically // refreshed when file changes. // [pwcPropFile] -- The property file. If this is null, // use the registry. // [ulCodePage] -- Codepage to interpret the property list. // // Notes: This constructor is used by clients who use their own file based // list instead of using the registry based file. // // History: 08 Sep 1997 KrishnaN Created // //----------------------------------------------------------------------------- CLocalGlobalPropertyList::CLocalGlobalPropertyList (CEmptyPropertyList *pDefaultList, BOOL fDynamicRefresh, WCHAR const * pwcsPropFile, ULONG ulCodePage) : CCombinedPropertyList(ulCodePage), _ulCodePage( ulCodePage ), _dwLastCheckMoment( GetTickCount() ), _mtx() { XInterface xPropListFile( new CPropListFile(pDefaultList, fDynamicRefresh, pwcsPropFile, ulCodePage)); SetDefaultList(xPropListFile.GetPointer()); } //+--------------------------------------------------------------------------- // // Member: CLocalGlobalPropertyList::IsMapUpToDate - public // // Synopsis: Determines if the file is still valid, or if it has // changed since it was last read. // // History: 06 May 1997 AlanW Created // //---------------------------------------------------------------------------- SCODE CLocalGlobalPropertyList::IsMapUpToDate() { ULONG cchRequired = _RegParams.GetDefaultColumnFile( 0, 0 ); WCHAR wszFile[MAX_PATH + 1]; wszFile[0] = 0; if ( 0 != cchRequired ) { _RegParams.GetDefaultColumnFile( wszFile, MAX_PATH ); } // // If the current file and the one in the registry are not the same, // we are outdated. // if (0 != _wcsicmp(_xFileName.GetPointer(), wszFile)) return S_FALSE; // // The filelist takes care of file modifications, so ask it // directly if it is up to date. // return GetDefaultList().IsMapUpToDate(); } //+--------------------------------------------------------------------------- // // Member: CLocalGlobalPropertyList::CheckError, public // // Synopsis: Checks if there was an error in the parsing of the file. // // Arguments: iLine -- error line number returned here. // ppFile -- file name returned here. can be 0 if not needed. // // History: 17-Sep-97 KrishnaN Created. // //---------------------------------------------------------------------------- SCODE CLocalGlobalPropertyList::CheckError( ULONG & iLine, WCHAR ** ppFile ) { return ((CPropListFile &)GetDefaultList()).CheckError(iLine, ppFile); } //+--------------------------------------------------------------------------- // // Member: CLocalGlobalPropertyList::Load - public // // Synopsis: Loads the file into the property list. // // Arguments: [pwszFile] -- File name for property definitions // // History: 19 Sep 1997 KrishnaN Created // //---------------------------------------------------------------------------- void CLocalGlobalPropertyList::Load(WCHAR const * const pwszFile ) { ((CPropListFile &)GetDefaultList()).Load(pwszFile); } // Miscellaneous CStaticPropertyList * GetGlobalStaticPropertyList() { GlobalStaticList.AddRef(); return &GlobalStaticList; } // // Return an AddRef'd global prop file list. The caller will release // when done using the global prop file list. // CPropListFile * GetGlobalPropListFile() { CImpersonateSystem impersonateSystem; CLock lock(g_mtxFilePropList); if (!CLocalGlobalPropertyList::_pGlobalPropListFile) { // The global property list will be controlled by CLocalGlobalPropertyList, so // we disable its ability to refresh dynamically. CLocalGlobalPropertyList::_pGlobalPropListFile = new CPropListFile(GetGlobalStaticPropertyList(), FALSE); } else { // // If the refresher replaces the global property list, that would // AddRef the newly created global proplist, so we should not AddREf // again. If DoIt fails, no need to AddRef. // if (!gRefresher.DoIt()) CLocalGlobalPropertyList::_pGlobalPropListFile->AddRef(); } Win4Assert(CLocalGlobalPropertyList::_pGlobalPropListFile); return CLocalGlobalPropertyList::_pGlobalPropListFile; } //+--------------------------------------------------------------------------- // // Member: CreateNewGlobalPropFileList - public // // Synopsis: Creates a new global file based property list. // // History: 15-Sep-1997 KrishnaN Created // // Notes: This function replaces the global property file list so that newer // clients asking for the list will get an updated list (if the file has // been modified or replaced.) // //---------------------------------------------------------------------------- void CreateNewGlobalPropFileList(WCHAR CONST *wcsFileName) { CLock lock(g_mtxFilePropList); Win4Assert(CLocalGlobalPropertyList::_pGlobalPropListFile); // The global property list will be controlled by CLocalGlobalPropertyList, so // we disable its ability to refresh dynamically. CLocalGlobalPropertyList::_pGlobalPropListFile = new CPropListFile(GetGlobalStaticPropertyList(), FALSE, wcsFileName); } //+--------------------------------------------------------------------------- // // Member: CGlobalPropFileRefresher::DoIt - public // // Synopsis: Creates a new global file based property list if necessary. // // Returns: True if refresh happened. False otherwise. // // History: 15-Sep-1997 KrishnaN Created // // Notes: This function monitors the registry and the file so the list // can be updated if the underlying property file changes. // //---------------------------------------------------------------------------- BOOL CGlobalPropFileRefresher::DoIt() { // // We don't need to lock this because currently it is only // being called after obtaining a lock elsewhere. If that // changes, then a lock may be needed here. // // Don't check more than once in a few seconds if (abs(GetTickCount() - _dwLastCheckMoment) < REFRESH_INTERVAL) return FALSE; CImpersonateSystem impersonateSystem; if (!_fInited) Init(); BOOL fRefresh = FALSE; _dwLastCheckMoment = GetTickCount(); // First check the registry, then check the file itself ULONG res = WaitForSingleObject( _regChangeEvent.GetEventHandle(), 0 ); if (WAIT_OBJECT_0 == res) { _regChangeEvent.Reset(); // the registry changed, but the value may not have. check that. WCHAR wszFile[MAX_PATH]; GetDefaultColumnFile(wszFile); // Are the file names the same? if (0 != _wcsicmp(wszFile, _wcsFileName)) { wcscpy(_wcsFileName, wszFile); fRefresh = TRUE; GetLastWriteTime(_ftFile); } } if (! fRefresh) { FILETIME ft; GetLastWriteTime(ft); if ((_ftFile.dwLowDateTime != ft.dwLowDateTime) || (_ftFile.dwHighDateTime != ft.dwHighDateTime) ) { _ftFile = ft; fRefresh = TRUE; } } if (fRefresh) { // refresh CreateNewGlobalPropFileList(_wcsFileName); } return fRefresh; } //+--------------------------------------------------------------------------- // // Member: CGlobalPropFileRefresher::Init - public // // Synopsis: Initializes this class. // // Returns: Nothing. Can throw. // // History: 15-Sep-1997 KrishnaN Created // //---------------------------------------------------------------------------- void CGlobalPropFileRefresher::Init() { _regChangeEvent.Init(); _lRegReturn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Root wcsRegCommonAdminSubKey, 0, // Reserved KEY_READ, // Access &_hKey); // Handle GetDefaultColumnFile(_wcsFileName); GetLastWriteTime(_ftFile); _fInited = TRUE; }