//============================================================================= // This file contains code to implement the CMSInfoCategory derived class for // showing live WMI data. //============================================================================= #include "stdafx.h" #include "resource.h" #include "category.h" #include "datasource.h" #include "dataset.h" #include "refreshthread.h" #include "refreshdialog.h" #include "wbemcli.h" #include "version5extension.h" #include "filestuff.h" #include "historyparser.h" //============================================================================= // CLiveDataSource // // TBD - need methods to look at deltas. How will this work? //============================================================================= CLiveDataSource::CLiveDataSource() : m_hwnd(NULL), m_pThread(NULL), m_strMachine(_T("")), m_pHistoryRoot(NULL), m_iDeltaIndex(-1) { } //----------------------------------------------------------------------------- // The default constructor will take care of deleting the tree. //----------------------------------------------------------------------------- CLiveDataSource::~CLiveDataSource() { if (m_pThread) delete m_pThread; } //----------------------------------------------------------------------------- // Creating a live data source consists of making the WMI connection to the // appropriate machine (most likely this one). We'll also need to load the // tree with default categories. // // TBD - also load extensions //----------------------------------------------------------------------------- extern CMSInfoLiveCategory catSystemSummary; extern CMSInfoHistoryCategory catHistorySystemSummary; HRESULT CLiveDataSource::Create(LPCTSTR szMachine, HWND hwnd, LPCTSTR szFilter) { // Build the tree. The default categories are stored in a static // set of structures - the base of which is catSystemSummary. m_pHistoryRoot = &catHistorySystemSummary; m_pRoot = &catSystemSummary; m_fStaticTree = TRUE; // Load any extensions to the live data. AddExtensions(); // If there is a string containing a filter of what categories // to show, apply that filter. if (szFilter && szFilter[0]) ApplyCategoryFilter(szFilter); // Save the machine name we are remoting to. m_strMachine = szMachine; SetMachineForCategories((CMSInfoLiveCategory *) m_pRoot); // Create the refresh thread for the live data source. m_pThread = new CRefreshThread(hwnd); if (m_pThread) m_pThread->m_strMachine = szMachine; m_hwnd = hwnd; return S_OK; } //----------------------------------------------------------------------------- // Apply the set of filters to the categories. If the filter string is not // empty, we should start out showing none of the categories, and only add in // the ones specified by the filter (this is to match a feature in 5.0). //----------------------------------------------------------------------------- void CLiveDataSource::ApplyCategoryFilter(LPCTSTR szFilter) { m_pRoot->SetShowCategory(FALSE); CString strNode, strFilter(szFilter); strFilter.TrimLeft(_T(" \"=:")); strFilter.TrimRight(_T(" \"=:")); while (!strFilter.IsEmpty()) { BOOL fAdd = (strFilter[0] == _T('+')); strFilter = strFilter.Mid(1); if (!strFilter.IsEmpty()) { strNode = strFilter.SpanExcluding(_T("+-")); strFilter = strFilter.Mid(strNode.GetLength()); if (!strNode.IsEmpty()) { CMSInfoLiveCategory * pNode; if (strNode.CompareNoCase(_T("all")) == 0) pNode = (CMSInfoLiveCategory *) m_pRoot; else pNode = GetNodeByName(strNode, (CMSInfoLiveCategory *) m_pRoot); if (pNode) pNode->SetShowCategory(fAdd); } } } } //----------------------------------------------------------------------------- // Add version 5.0 extensions to the m_pRoot category tree. // // Note - we only want to do this once. And we only want to delete the nodes // we added once (when we're unloading). So we'll create a simple class to // manage this lifetime. // // THIS CLASS IS DANGEROUS (and this should probably be redesigned). It should // only be used to add extensions to a static tree, that won't be deleted // any time before the app exits. This class assumes the responsibility for // deleting the dynamic nodes inserted into the tree. //----------------------------------------------------------------------------- class CManageExtensionCategories { public: CManageExtensionCategories() : m_pRoot(NULL) {}; ~CManageExtensionCategories() { DeleteTree(m_pRoot); }; BOOL ExtensionsAdded(CMSInfoLiveCategory * pRoot) { if (m_pRoot) return TRUE; m_pRoot = pRoot; return FALSE; } private: CMSInfoLiveCategory * m_pRoot; void DeleteTree(CMSInfoLiveCategory * pRoot) { if (pRoot == NULL) return; for (CMSInfoLiveCategory * pChild = (CMSInfoLiveCategory *) pRoot->GetFirstChild(); pChild;) { CMSInfoLiveCategory * pNext = (CMSInfoLiveCategory *) pChild->GetNextSibling(); DeleteTree(pChild); pChild = pNext; } // If the tree is static, then don't actually delete, just reset // some state variables (possibly). if (pRoot->m_fDynamicColumns) delete pRoot; } }; CManageExtensionCategories gManageExtensionCategories; extern BOOL FileExists(const CString & strFile); void CLiveDataSource::AddExtensions() { if (gManageExtensionCategories.ExtensionsAdded((CMSInfoLiveCategory *) m_pRoot)) return; CStringList strlistExtensions; GetExtensionSet(strlistExtensions); while (!strlistExtensions.IsEmpty()) { CString strExtension = strlistExtensions.RemoveHead(); if (FileExists(strExtension)) { CMapWordToPtr mapVersion5Categories; DWORD dwRootID = CTemplateFileFunctions::ParseTemplateIntoVersion5Categories(strExtension, mapVersion5Categories); ConvertVersion5Categories(mapVersion5Categories, dwRootID, (CMSInfoLiveCategory *) m_pRoot); } } } //----------------------------------------------------------------------------- // Look for all of the version 5.0 style extension. These will be located as // values under the msinfo\templates registry key. //----------------------------------------------------------------------------- void CLiveDataSource::GetExtensionSet(CStringList & strlistExtensions) { strlistExtensions.RemoveAll(); TCHAR szBaseKey[] = _T("SOFTWARE\\Microsoft\\Shared Tools\\MSInfo\\templates"); HKEY hkeyBase; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBaseKey, 0, KEY_READ, &hkeyBase)) { TCHAR szName[64], szValue[MAX_PATH]; DWORD dwIndex = 0; DWORD dwLength = sizeof(szName) / sizeof(TCHAR); while (ERROR_SUCCESS == RegEnumKeyEx(hkeyBase, dwIndex++, szName, &dwLength, NULL, NULL, NULL, NULL)) { dwLength = sizeof(szValue) / sizeof(TCHAR); if (ERROR_SUCCESS == RegQueryValue(hkeyBase, szName, szValue, (long *)&dwLength)) if (*szValue) strlistExtensions.AddTail(szValue); dwLength = sizeof(szName) / sizeof(TCHAR); } RegCloseKey(hkeyBase); } } //----------------------------------------------------------------------------- // Convert the categories from version 5.0 format (in the map) to our // format in the tree structure. //----------------------------------------------------------------------------- extern CMSInfoLiveCategory catSystemSummary; void CLiveDataSource::ConvertVersion5Categories(CMapWordToPtr & mapVersion5Categories, DWORD dwRootID, CMSInfoLiveCategory * m_pRoot) { WORD wMapID; INTERNAL_CATEGORY * pCategory; POSITION pos; DWORD dwID = dwRootID; CMSInfoLiveCategory * pInsertCat; while ((pCategory = CTemplateFileFunctions::GetInternalRep(mapVersion5Categories, dwID)) != NULL) { INTERNAL_CATEGORY * pPrev = CTemplateFileFunctions::GetInternalRep(mapVersion5Categories, pCategory->m_dwPrevID); if (pPrev && (pInsertCat = GetNodeByName(pPrev->m_strIdentifier, m_pRoot))) { CMSInfoLiveCategory * pNewCat = MakeVersion6Category(pCategory); pNewCat->m_pPrevSibling = pInsertCat; pNewCat->m_pNextSibling = pInsertCat->m_pNextSibling; pNewCat->m_pParent = pInsertCat->m_pParent; pInsertCat->m_pNextSibling = pNewCat; if (pNewCat->m_pNextSibling) pNewCat->m_pNextSibling->m_pPrevSibling = pNewCat; } else { INTERNAL_CATEGORY * pParent = CTemplateFileFunctions::GetInternalRep(mapVersion5Categories, pCategory->m_dwParentID); CString strIdentifier; if (pParent) strIdentifier = pParent->m_strIdentifier; else catSystemSummary.GetNames(NULL, &strIdentifier); if ((pInsertCat = GetNodeByName(strIdentifier, m_pRoot)) != NULL) { CMSInfoLiveCategory * pNewCat = MakeVersion6Category(pCategory); if (pInsertCat->m_pFirstChild == NULL) { pNewCat->m_pPrevSibling = NULL; pNewCat->m_pNextSibling = NULL; pNewCat->m_pParent = pInsertCat; pInsertCat->m_pFirstChild = pNewCat; } else { CMSInfoLiveCategory * pInsertAfter = (CMSInfoLiveCategory *) pInsertCat->m_pFirstChild; while (pInsertAfter->m_pNextSibling) pInsertAfter = (CMSInfoLiveCategory *) pInsertAfter->m_pNextSibling; pNewCat->m_pPrevSibling = pInsertAfter; pNewCat->m_pNextSibling = NULL; pNewCat->m_pParent = pInsertAfter->m_pParent; pInsertAfter->m_pNextSibling = pNewCat; } } } dwID += 1; } for (pos = mapVersion5Categories.GetStartPosition(); pos != NULL;) { mapVersion5Categories.GetNextAssoc(pos, wMapID, (void * &) pCategory); if (pCategory) delete pCategory; } mapVersion5Categories.RemoveAll(); } //----------------------------------------------------------------------------- // Look for a node in the tree with the specified name. //----------------------------------------------------------------------------- CMSInfoLiveCategory * CLiveDataSource::GetNodeByName(const CString & strSearch, CMSInfoLiveCategory * pRoot) { if (pRoot == NULL) return NULL; CString strName; pRoot->GetNames(NULL, &strName); if (strName.CompareNoCase(strSearch) == 0) return pRoot; CMSInfoLiveCategory * pSearch = GetNodeByName(strSearch, (CMSInfoLiveCategory *) pRoot->m_pNextSibling); if (pSearch) return pSearch; pSearch = GetNodeByName(strSearch, (CMSInfoLiveCategory *) pRoot->m_pFirstChild); if (pSearch) return pSearch; return NULL; } //----------------------------------------------------------------------------- // Create a version 6.0 category structure out of a version 5.0 category // structure. //----------------------------------------------------------------------------- CMSInfoLiveCategory * CLiveDataSource::MakeVersion6Category(INTERNAL_CATEGORY * pCategory5) { CMSInfoLiveCategory * pCategory6 = new CMSInfoLiveCategory(pCategory5); return pCategory6; } //----------------------------------------------------------------------------- // Propagate the machine name through the entire category tree. //----------------------------------------------------------------------------- void CLiveDataSource::SetMachineForCategories(CMSInfoLiveCategory * pCategory) { if (pCategory) { for (CMSInfoLiveCategory * pChild = (CMSInfoLiveCategory *) pCategory->GetFirstChild(); pChild;) { CMSInfoLiveCategory * pNext = (CMSInfoLiveCategory *) pChild->GetNextSibling(); SetMachineForCategories(pChild); pChild = pNext; } pCategory->SetMachine(m_strMachine); } } //----------------------------------------------------------------------------- // Update the category tree to show delta information. Also changes which // tree should be returned. // // An index of -1 means show current system information. // // If the function returns TRUE, then the tree doesn't need to be rebuild // (although the selected category needs to be refreshed). //----------------------------------------------------------------------------- BOOL CLiveDataSource::ShowDeltas(int iDeltaIndex) { BOOL fUpdateTree = FALSE; if (m_iDeltaIndex != iDeltaIndex) { if (m_iDeltaIndex == -1 || iDeltaIndex == -1) fUpdateTree = TRUE; #ifdef A_STEPHL /*CString strMSG; strMSG.Format("iDeltaIndex= %d, m_iDeltaIndex =%d \n",iDeltaIndex,m_iDeltaIndex); ::MessageBox(NULL,strMSG,"",MB_OK);*/ #endif m_iDeltaIndex = iDeltaIndex; if (m_iDeltaIndex != -1) { // The user has selected a new delta period, and it's different // than the last one. We need to mark the categories in the tree // as not refreshed, and set the delta index. if (m_pHistoryRoot) m_pHistoryRoot->UpdateDeltaIndex(m_iDeltaIndex); } } else { #ifdef A_STEPHL2 ::MessageBox(NULL,"m_iDeltaIndex == iDeltaIndex","",MB_OK); #endif return TRUE; } return !fUpdateTree; } //----------------------------------------------------------------------------- // Populate the list of available deltas. //----------------------------------------------------------------------------- BOOL CLiveDataSource::GetDeltaList(CStringList * pstrlist) { ASSERT(pstrlist); if (pstrlist == NULL) return FALSE; pstrlist->RemoveAll(); if (m_pHistoryRoot == NULL) { ASSERT(0 && "Root node is not yet created"); } CComPtr pXMLDoc = GetXMLDoc(); CComPtr pDCNode; HRESULT hr = GetDataCollectionNode( pXMLDoc,pDCNode); if (FAILED(hr) || !pDCNode) { return FALSE; } CComPtr pList; hr = pDCNode->selectNodes(L"Delta",&pList); if (FAILED(hr) || !pList) { ASSERT(0 && "could not get list of delta nodes"); return FALSE; } long lListLen; hr = pList->get_length(&lListLen); if (lListLen == 0) { //we may have an incident file, which capitalizes "DELTA" pList.Release(); hr = pDCNode->selectNodes(L"DELTA",&pList); if (FAILED(hr) || !pList) { ASSERT(0 && "could not get list of delta nodes"); return FALSE; } hr = pList->get_length(&lListLen); } if (lListLen > 0) { CComPtr pDeltaNode; CString strDate(_T("")); TCHAR szBuffer[MAX_PATH]; // seems plenty big for(long i = 0 ;i < lListLen;i++) { hr = pList->nextNode(&pDeltaNode); if (FAILED(hr) || !pDeltaNode) { ASSERT(0 && "could not get next node from list"); break; } CComVariant varTS; CComPtr pTimestampElement; hr = pDeltaNode->QueryInterface(IID_IXMLDOMElement,(void**) &pTimestampElement); pDeltaNode.Release(); if (FAILED(hr) || !pTimestampElement) { ASSERT(0 && "could not get attribute element"); break; } hr = pTimestampElement->getAttribute(L"Timestamp_T0",&varTS); if (FAILED(hr) ) { ASSERT(0 && "could not get timestamp value from attribute"); } //now get time zone (number of seconds difference between local time and UTC) CComVariant varTzoneDeltaSeconds; hr = pTimestampElement->getAttribute(L"TimeZone",&varTzoneDeltaSeconds); if (FAILED(hr) ) //this will happen when loading WinME xml, which has no timezone info { varTzoneDeltaSeconds = 0; } //make sure we have an integer type hr = varTzoneDeltaSeconds.ChangeType(VT_INT); if (FAILED(hr) ) { varTzoneDeltaSeconds = 0; } USES_CONVERSION; pTimestampElement.Release(); CString strTimestamp = OLE2A(varTS.bstrVal); CTime tm1 = GetDateFromString(strTimestamp,varTzoneDeltaSeconds.intVal); COleDateTime olDate(tm1.GetTime()); // Try to get the date in the localized format. strDate.Empty(); SYSTEMTIME systime; if (olDate.GetAsSystemTime(systime)) { DWORD dwLayout = 0; ::GetProcessDefaultLayout(&dwLayout); // For some reason, in HEB we don't want to use the DATE_RTLREADING flag. Bug 434802. if (LANG_HEBREW == PRIMARYLANGID(::GetUserDefaultUILanguage())) dwLayout &= ~LAYOUT_RTL; // force the non-use of DATE_RTLREADING if (::GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE | (((dwLayout & LAYOUT_RTL) != 0) ? DATE_RTLREADING : 0), &systime, NULL, szBuffer, MAX_PATH)) { strDate = szBuffer; if (::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systime, NULL, szBuffer, MAX_PATH)) strDate += CString(_T(" ")) + CString(szBuffer); } } // Fall back on our old (partially incorrect) method. if (strDate.IsEmpty()) strDate = olDate.Format(0, LOCALE_USER_DEFAULT); pstrlist->AddTail(strDate); } } return TRUE; } //----------------------------------------------------------------------------- // Return an HRESULT indicating whether or not this is a valid data source. // This is primarily useful when we're remoting to a machine and we want to // determine if the network name is accessible. //----------------------------------------------------------------------------- HRESULT CLiveDataSource::ValidDataSource() { if (m_pThread == NULL) return E_FAIL; return (m_pThread->CheckWMIConnection()); } //============================================================================= // CMSInfoLiveCategory //============================================================================= //----------------------------------------------------------------------------- // The constructor needs to initialize some member variables, and make sure // that the category is inserted into the tree correctly. //----------------------------------------------------------------------------- CMSInfoLiveCategory::CMSInfoLiveCategory(UINT uiCaption, LPCTSTR szName, RefreshFunction pFunction, DWORD dwRefreshIndex, CMSInfoCategory * pParent, CMSInfoCategory * pPrevious, const CString & strHelpTopic, CMSInfoColumn * pColumns, BOOL fDynamicColumns, CategoryEnvironment environment) : CMSInfoCategory(uiCaption, szName, pParent, pPrevious, pColumns, fDynamicColumns, environment), m_pRefreshFunction(pFunction), m_dwLastRefresh(0), m_dwRefreshIndex(dwRefreshIndex), m_strMachine(_T("")), m_strHelpTopic(strHelpTopic) { // Insert ourselves into the category tree. This means making sure that // our parent and previous sibling point to us. if (m_pParent && m_pParent->m_pFirstChild == NULL) m_pParent->m_pFirstChild = this; if (m_pPrevSibling) { if (m_pPrevSibling->m_pNextSibling == NULL) m_pPrevSibling->m_pNextSibling = this; else { CMSInfoCategory * pScan = m_pPrevSibling->m_pNextSibling; while (pScan->m_pNextSibling) pScan = pScan->m_pNextSibling; pScan->m_pNextSibling = this; } } } CMSInfoLiveCategory::~CMSInfoLiveCategory() { } //----------------------------------------------------------------------------- // The copy constructor will copy the members, but not allocate a new sub-tree // (the new category has the same children and siblings as the original). //----------------------------------------------------------------------------- CMSInfoLiveCategory::CMSInfoLiveCategory(CMSInfoLiveCategory & copyfrom) : m_dwLastRefresh(0), m_dwRefreshIndex(copyfrom.m_dwRefreshIndex), m_pRefreshFunction(copyfrom.m_pRefreshFunction) { m_strMachine = copyfrom.m_strMachine; m_strHelpTopic = copyfrom.m_strHelpTopic; m_uiCaption = copyfrom.m_uiCaption; m_pParent = copyfrom.m_pParent; m_pPrevSibling = copyfrom.m_pPrevSibling; m_pFirstChild = copyfrom.m_pFirstChild; m_pNextSibling = copyfrom.m_pNextSibling; m_astrData = NULL; m_adwData = NULL; m_afRowAdvanced = NULL; m_strName = copyfrom.m_strName; m_hrError = S_OK; m_acolumns = copyfrom.m_acolumns; m_fDynamicColumns = FALSE; m_iRowCount = 0; m_iColCount = copyfrom.m_iColCount; m_iSortColumn = copyfrom.m_iSortColumn; m_hti = NULL; m_fSkipCategory = copyfrom.m_fSkipCategory; } //----------------------------------------------------------------------------- // Constructs a category from an old (version 5.0) category structure. //----------------------------------------------------------------------------- extern HRESULT RefreshExtensions(CWMIHelper * pWMI, DWORD dwIndex, volatile BOOL * pfCancel, CPtrList * aColValues, int iColCount, void ** ppCache); CMSInfoLiveCategory::CMSInfoLiveCategory(INTERNAL_CATEGORY * pinternalcat) { ASSERT(pinternalcat); if (pinternalcat == NULL) return; m_dwLastRefresh = 0; m_strMachine = CString(_T("")); m_strHelpTopic = CString(_T("")); // Graft on a help topic for the IE extension (bug 479839). if (pinternalcat->m_strIdentifier.CompareNoCase(_T("InternetSettings")) == 0) m_strHelpTopic = _T("msinfo_internet_settings.htm"); m_strCaption = pinternalcat->m_fieldName.m_strFormat; m_strName = pinternalcat->m_strIdentifier; m_iRowCount = 0; m_iColCount = 0; GATH_FIELD * pField = pinternalcat->m_pColSpec; while (pField) { m_iColCount += 1; pField = pField->m_pNext; } if (m_iColCount) { m_acolumns = new CMSInfoColumn[m_iColCount]; if (m_acolumns && pinternalcat->m_pColSpec) { m_fDynamicColumns = TRUE; pField = pinternalcat->m_pColSpec; for (int iCol = 0; iCol < m_iColCount && pField != NULL; iCol++) { m_acolumns[iCol].m_fAdvanced = (pField->m_datacomplexity == ADVANCED); m_acolumns[iCol].m_strCaption = pField->m_strFormat; m_acolumns[iCol].m_uiWidth = pField->m_usWidth; m_acolumns[iCol].m_fSorts = (pField->m_sort != NOSORT); m_acolumns[iCol].m_fLexical = (pField->m_sort == LEXICAL); pField = pField->m_pNext; } } } // Insert the information needed to refresh the extension category (such // as line specs) into a map, indexed by a DWORD. That DWORD will be saved // for the category, so we can look up the refresh data later. if (pinternalcat->m_pLineSpec) { m_dwRefreshIndex = gmapExtensionRefreshData.Insert(pinternalcat->m_pLineSpec); pinternalcat->m_pLineSpec = NULL; // keep this from being deleted m_pRefreshFunction = &RefreshExtensions; } else { m_dwRefreshIndex = 0; m_pRefreshFunction = NULL; } if (m_dwRefreshIndex) gmapExtensionRefreshData.InsertString(m_dwRefreshIndex, pinternalcat->m_strNoInstances); } //----------------------------------------------------------------------------- // Start a refresh (starts the thread, which will send a message when it's // done). //----------------------------------------------------------------------------- BOOL CMSInfoLiveCategory::Refresh(CLiveDataSource * pSource, BOOL fRecursive) { if (pSource && pSource->m_pThread) pSource->m_pThread->StartRefresh(this, fRecursive); return TRUE; } //----------------------------------------------------------------------------- // Start a synchronous refresh. This function won't return until the refresh // has been completed. //----------------------------------------------------------------------------- BOOL CMSInfoLiveCategory::RefreshSynchronous(CLiveDataSource * pSource, BOOL fRecursive) { if (pSource && pSource->m_pThread) { pSource->m_pThread->StartRefresh(this, fRecursive); pSource->m_pThread->WaitForRefresh(); } return TRUE; } //----------------------------------------------------------------------------- // Refreshes the current category (and possibly the children) while presenting // the user with a UI. A dialog box is presented to the user with the specified // mesage. If the user clicks cancel, the refresh is cancelled and this // function returns false. Otherwise, when the refresh is done the dialog box // will be removed, and this funcion returns true. //----------------------------------------------------------------------------- BOOL CMSInfoLiveCategory::RefreshSynchronousUI(CLiveDataSource * pSource, BOOL fRecursive, UINT uiMessage, HWND hwnd) { if (pSource && pSource->m_pThread) { pSource->m_pThread->StartRefresh(this, fRecursive); ::AfxSetResourceHandle(_Module.GetResourceInstance()); CWnd * pWnd = CWnd::FromHandle(hwnd); // CRefreshDialog refreshdialog(pWnd); // refreshdialog.DoModal(); if (pSource->m_pThread->IsRefreshing()) { pSource->m_pThread->CancelRefresh(); return FALSE; } } return TRUE; } //----------------------------------------------------------------------------- // Get the error strings for this category (subclasses should override this). //----------------------------------------------------------------------------- void CMSInfoLiveCategory::GetErrorText(CString * pstrTitle, CString * pstrMessage) { if (SUCCEEDED(m_hrError)) { ASSERT(0 && "why call GetErrorText for no error?"); CMSInfoCategory::GetErrorText(pstrTitle, pstrMessage); return; } if (pstrTitle) pstrTitle->LoadString(IDS_CANTCOLLECT); if (pstrMessage) { switch (m_hrError) { case WBEM_E_OUT_OF_MEMORY: pstrMessage->LoadString(IDS_OUTOFMEMERROR); break; case WBEM_E_ACCESS_DENIED: if (m_strMachine.IsEmpty()) pstrMessage->LoadString(IDS_GATHERACCESS_LOCAL); else pstrMessage->Format(IDS_GATHERACCESS, m_strMachine); break; case WBEM_E_INVALID_NAMESPACE: if (m_strMachine.IsEmpty()) pstrMessage->LoadString(IDS_BADSERVER_LOCAL); else pstrMessage->Format(IDS_BADSERVER, m_strMachine); break; case 0x800706BA: // RPC Server Unavailable case WBEM_E_TRANSPORT_FAILURE: if (m_strMachine.IsEmpty()) pstrMessage->LoadString(IDS_NETWORKERROR_LOCAL); else pstrMessage->Format(IDS_NETWORKERROR, m_strMachine); break; case WBEM_E_FAILED: case WBEM_E_INVALID_PARAMETER: default: pstrMessage->LoadString(IDS_UNEXPECTED); } #ifdef _DEBUG { CString strTemp; strTemp.Format(_T("\n\r\n\rDebug Version Only: [HRESULT = 0x%08X]"), m_hrError); *pstrMessage += strTemp; } #endif } } //============================================================================= // CMSInfoHistoryCategory //============================================================================= //----------------------------------------------------------------------------- // This refresh overrides the live category refresh (which starts a WMI refresh // using another thread). This version just fills in the variables from the // base classes (like m_astrData) based on which category we're view in // history mode. //----------------------------------------------------------------------------- extern CMSInfoHistoryCategory catHistorySystemSummary; extern CMSInfoHistoryCategory catHistoryResources; extern CMSInfoHistoryCategory catHistoryComponents; extern CMSInfoHistoryCategory catHistorySWEnv; BOOL CMSInfoHistoryCategory::Refresh(CLiveDataSource * pSource, BOOL fRecursive) { HCURSOR hc = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); if (pSource->GetXMLDoc()) { CHistoryParser HParser(pSource->GetXMLDoc()); HRESULT hr = HParser.Refresh(this, pSource->m_iDeltaIndex ); if (HParser.AreThereChangeLines() == TRUE) { //commitlines doesn't like it if there are no change lines this->CommitLines(); } if (pSource->m_hwnd) ::PostMessage(pSource->m_hwnd, WM_MSINFODATAREADY, 0, (LPARAM)this); m_dwLastRefresh = ::GetTickCount(); if (fRecursive) { for(CMSInfoCategory* pChildCat = (CMSInfoCategory*) this->GetFirstChild();pChildCat != NULL;pChildCat = (CMSInfoCategory*) pChildCat->GetNextSibling()) { if(pChildCat->GetDataSourceType() == LIVE_DATA) { if (!((CMSInfoHistoryCategory*)pChildCat)->Refresh(pSource,fRecursive)) { ::SetCursor(hc); return FALSE; } } } } } ::SetCursor(hc); return TRUE; } //----------------------------------------------------------------------------- // Call ClearLines before lines are inserted in the output. //----------------------------------------------------------------------------- void CMSInfoHistoryCategory::ClearLines() { DeleteContent(); for (int iCol = 0; iCol < 5; iCol++) while (!m_aValList[iCol].IsEmpty()) delete (CMSIValue *) m_aValList[iCol].RemoveHead(); } //----------------------------------------------------------------------------- // Call CommitLines after all of the Insert operations are completed. This will // transfer the values from the lists of CMSIValues to the data arrays. //----------------------------------------------------------------------------- void CMSInfoHistoryCategory::CommitLines() { int iRowCount = (int)m_aValList[0].GetCount(); #ifdef _DEBUG for (int i = 0; i < 5; i++) ASSERT(iRowCount == m_aValList[i].GetCount()); #endif if (iRowCount) AllocateContent(iRowCount); for (int j = 0; j < 5; j++) for (int i = 0; i < m_iRowCount; i++) { CMSIValue * pValue = (CMSIValue *) m_aValList[j].RemoveHead(); if (j < 4 || this != &catHistorySystemSummary) SetData(i, j, pValue->m_strValue, pValue->m_dwValue); // Set the advanced flag for either the first column, or // for any column which is advanced (any cell in a row // being advanced makes the whole row advanced). if (j == 0 || pValue->m_fAdvanced) SetAdvancedFlag(i, pValue->m_fAdvanced); delete pValue; } } //----------------------------------------------------------------------------- // Various functions to insert different types of events in the history. //----------------------------------------------------------------------------- void CMSInfoHistoryCategory::InsertChangeLine(CTime tm, LPCTSTR szType, LPCTSTR szName, LPCTSTR szProperty, LPCTSTR szFromVal, LPCTSTR szToVal) { CString strDetails; strDetails.Format(IDS_DELTACHANGE, szProperty, szFromVal, szToVal); CString strCaption; strCaption.LoadString(IDS_HISTORYCHANGE); InsertLine(tm, strCaption, szType, szName, strDetails); } void CMSInfoHistoryCategory::InsertAddLine(CTime tm, LPCTSTR szType, LPCTSTR szName) { CString strCaption; strCaption.LoadString(IDS_HISTORYADDED); InsertLine(tm, strCaption, szType, szName); } void CMSInfoHistoryCategory::InsertRemoveLine(CTime tm, LPCTSTR szType, LPCTSTR szName) { CString strCaption; strCaption.LoadString(IDS_HISTORYREMOVED); InsertLine(tm, strCaption, szType, szName); } void CMSInfoHistoryCategory::InsertLine(CTime tm, LPCTSTR szOperation, LPCTSTR szType, LPCTSTR szName, LPCTSTR szDetails) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); /*CString strTime; if (nDays >= 0) { strTime.Format(IDS_DAYSAGO, nDays + 1); } else { //-1 indicates no changes strTime = ""; }*/ COleDateTime olTime; CString strTime; if (-1 == (int) tm.GetTime()) { strTime.LoadString(IDS_NOHISTORY_AVAILABLE); } else { olTime = tm.GetTime(); strTime = olTime.Format(); } CMSIValue * pValue = new CMSIValue(strTime, (DWORD)olTime.m_dt); m_aValList[0].AddTail((void *) pValue); pValue = new CMSIValue(szOperation, 0); m_aValList[1].AddTail((void *) pValue); pValue = new CMSIValue(szName, 0); m_aValList[2].AddTail((void *) pValue); if (szDetails) pValue = new CMSIValue(szDetails, 0); else pValue = new CMSIValue(_T(""), 0); m_aValList[3].AddTail((void *) pValue); pValue = new CMSIValue(szType, 0); m_aValList[4].AddTail((void *) pValue); } /*void CMSInfoHistoryCategory::InsertChangeLine(int nDays, LPCTSTR szType, LPCTSTR szName, LPCTSTR szProperty, LPCTSTR szFromVal, LPCTSTR szToVal) { CString strDetails; strDetails.Format(IDS_DELTACHANGE, szProperty, szFromVal, szToVal); InsertLine(nDays, _T("CHANGED"), szType, szName, strDetails); } void CMSInfoHistoryCategory::InsertAddLine(int nDays, LPCTSTR szType, LPCTSTR szName) { InsertLine(nDays, _T("ADDED"), szType, szName); } void CMSInfoHistoryCategory::InsertRemoveLine(int nDays, LPCTSTR szType, LPCTSTR szName) { InsertLine(nDays, _T("REMOVED"), szType, szName); } void CMSInfoHistoryCategory::InsertLine(int nDays, LPCTSTR szOperation, LPCTSTR szType, LPCTSTR szName, LPCTSTR szDetails) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CString strTime; if (nDays >= 0) { strTime.Format(IDS_DAYSAGO, nDays + 1); } else { //-1 indicates no changes strTime = ""; } CMSIValue * pValue = new CMSIValue(strTime, (DWORD)nDays); m_aValList[0].AddTail((void *) pValue); pValue = new CMSIValue(szOperation, 0); m_aValList[1].AddTail((void *) pValue); pValue = new CMSIValue(szName, 0); m_aValList[2].AddTail((void *) pValue); if (szDetails) pValue = new CMSIValue(szDetails, 0); else pValue = new CMSIValue(_T(""), 0); m_aValList[3].AddTail((void *) pValue); pValue = new CMSIValue(szType, 0); m_aValList[4].AddTail((void *) pValue); } */