//============================================================================= // Code for loading and refreshing version 5.0 extensions. //============================================================================= #include "stdafx.h" #include "category.h" #include "dataset.h" #include "wmiabstraction.h" #include "version5extension.h" CMapExtensionRefreshData gmapExtensionRefreshData; extern HRESULT ChangeWBEMSecurity(IUnknown * pUnknown); //----------------------------------------------------------------------------- // Load the specified template from the named extension. This will involve // loading the DLL and using the entry point to retrieve the text for the // extension's template. // // Once the data is loaded, it's parsed into version 5 format structures. //----------------------------------------------------------------------------- typedef DWORD (__cdecl *pfuncGetTemplate)(void ** ppBuffer); DWORD CTemplateFileFunctions::ParseTemplateIntoVersion5Categories(const CString & strExtension, CMapWordToPtr & mapVersion5Categories) { DWORD dwRootID = 0; HINSTANCE hinst = LoadLibrary(strExtension); if (hinst == NULL) return dwRootID; pfuncGetTemplate pfunc = (pfuncGetTemplate) GetProcAddress(hinst, "GetTemplate"); if (pfunc == NULL) { FreeLibrary(hinst); return dwRootID; } // Call the DLL function with a NULL parameter to get the size of the buffer. void * pBuffer; CMemFile memfile; DWORD dwBufferSize = (*pfunc)((void **)&pBuffer); if (dwBufferSize && pBuffer) { memfile.Attach((BYTE *)pBuffer, dwBufferSize, 0); dwRootID = ReadTemplateFile(&memfile, mapVersion5Categories); memfile.Detach(); (void)(*pfunc)(NULL); // calling the exported DLL function with NULL frees its buffers } if (hinst != NULL) FreeLibrary(hinst); return dwRootID; } //----------------------------------------------------------------------------- // This function reads the contents of a template file (in this case, a memory // file) and produces a map of ID, INTERNAL_CATEGORY pointer pairs. It returns // the ID for the root node in the tree. //----------------------------------------------------------------------------- DWORD CTemplateFileFunctions::ReadTemplateFile(CFile * pFile, CMapWordToPtr & mapVersion5Categories) { ASSERT(pFile); if (pFile == NULL || !VerifyUNICODEFile(pFile) || !ReadHeaderInfo(pFile)) return 0; return (ReadNodeRecursive(pFile, mapVersion5Categories, 0, 0)); } //----------------------------------------------------------------------------- // Make sure this is an MSInfo template file. //----------------------------------------------------------------------------- BOOL CTemplateFileFunctions::ReadHeaderInfo(CFile * pFile) { return VerifyAndAdvanceFile(pFile, CString(_T(TEMPLATE_FILE_TAG))); } //----------------------------------------------------------------------------- // This method verifies that the passed file is a UNICODE file, by reading the // value 0xFEFF from the file. It also leaves the file pointer past this word. //----------------------------------------------------------------------------- BOOL CTemplateFileFunctions::VerifyUNICODEFile(CFile * pFile) { WORD verify; if (pFile->Read((void *) &verify, sizeof(WORD)) != sizeof(WORD)) return FALSE; return (verify == 0xFEFF); } //----------------------------------------------------------------------------- // This method verifies that the text in strVerify comes next in the file (not // including case or whitespace differences) and advances the file past that // text. If the text was the next content in the file, TRUE is returned, // otherwise FALSE. If FALSE is returned, the file is backed up to where it // was when this method was called. //----------------------------------------------------------------------------- BOOL CTemplateFileFunctions::VerifyAndAdvanceFile(CFile * pFile, const CString & strVerify) { DWORD dwPosition = pFile->GetPosition(); WCHAR cLastChar, cCurrentChar = L'\0'; BOOL fInComment = FALSE; int iCharIndex = 0, iStringLen = strVerify.GetLength(); while (iCharIndex < iStringLen) { // Save the last character read, since the comment token ("//") is // two characters long. cLastChar = cCurrentChar; // Read the next character in the file. if (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) != sizeof(WCHAR)) return FALSE; // If we're in a comment, and the character we just read isn't a new line, // we want to ignore it. if (fInComment) { if (cCurrentChar == L'\n') fInComment = FALSE; continue; } // Check to see if we've started into a comment. Note that we ignore // the first '/' also by continuing. if (cCurrentChar == L'/') { if (cLastChar == L'/') fInComment = TRUE; continue; } // Skip whitespace, and also leading commas. if (iswspace(cCurrentChar) || (cCurrentChar == L',' && iCharIndex == 0)) continue; if (cCurrentChar != (WCHAR)strVerify[iCharIndex]) { pFile->Seek((LONG)dwPosition, CFile::begin); return FALSE; } iCharIndex++; } return TRUE; } //----------------------------------------------------------------------------- // This is the recursive function to read a node. It reads the information // from the node parameters, creates the node, and processes the contents of // the block following the node (contained within "{}"'s). It's called // recursively if there are any nodes in that block. // // In this version (for 6.0), it returns the ID of the node it's read. //----------------------------------------------------------------------------- DWORD CTemplateFileFunctions::ReadNodeRecursive(CFile * pFile, CMapWordToPtr & mapCategories, DWORD dwParentID, DWORD dwPrevID) { // Determine if we need to create a new category for this node. Read the // information from the file to determine the identifier for the new category. CString strEnumerateClass, strIdentifier; if (!VerifyAndAdvanceFile(pFile, CString(NODE_KEYWORD) + CString("("))) return 0; if (!ReadArgument(pFile, strEnumerateClass)) return 0; if (!ReadArgument(pFile, strIdentifier)) return 0; // Generate the ID for this new node. This should be one greater than the max in the // map (or one, if the map is empty). DWORD dwID = 0; WORD wMapID; void * pDontCare; for (POSITION pos = mapCategories.GetStartPosition(); pos != NULL;) { mapCategories.GetNextAssoc(pos, wMapID, pDontCare); if ((DWORD) wMapID > dwID) dwID = (DWORD) wMapID; } dwID += 1; // Create the category for the node. INTERNAL_CATEGORY * pCategory = CreateCategory(mapCategories, dwID, dwParentID, dwPrevID); // Read the contents of the node argument list ("node(enum, identifier, field(source, formatstr, arg...))") // We've already read up to and including the identifier. pCategory->m_strEnumerateClass = strEnumerateClass; pCategory->m_strIdentifier = strIdentifier; if (!ReadField(pFile, pCategory->m_fieldName)) return 0; // Copy the field name to the name of the category (they are two different // member variables to allow for dynamically refreshed names, which turns // out to be unnecessary in this version). pCategory->m_categoryName.m_strText = pCategory->m_fieldName.m_strFormat; if (!ReadArgument(pFile, pCategory->m_strNoInstances)) return 0; if (!VerifyAndAdvanceFile(pFile, CString("){"))) return 0; // Process the contents of the block (enclosed in "{}") for this node. DWORD dwSubNodePrev = 0, dwNewNode = 0; CString strKeyword; // If this new category isn't actually new (i.e. it is being read from a // template and overlaps an existing category) see if there are any // existing children. // // Version 6.0: these are being read into distinct trees, so there should // be no overlap (it would be resolved later). while (GetKeyword(pFile, strKeyword)) { if (strKeyword.CompareNoCase(CString(NODE_KEYWORD)) == 0) { dwNewNode = ReadNodeRecursive(pFile, mapCategories, dwID, dwSubNodePrev); if (dwNewNode == 0) return 0; // If this is the first child node we've read, save its ID. if (pCategory->m_dwChildID == 0) pCategory->m_dwChildID = dwNewNode; // If we've read another child node, set its next field appropriately. if (dwSubNodePrev) { INTERNAL_CATEGORY * pPrevCategory = GetInternalRep(mapCategories, dwSubNodePrev); if (pPrevCategory) pPrevCategory->m_dwNextID = dwNewNode; } dwSubNodePrev = dwNewNode; } else if (strKeyword.CompareNoCase(CString(COLUMN_KEYWORD)) == 0) { if (!ReadColumnInfo(pFile, mapCategories, dwID)) return 0; } else if (strKeyword.CompareNoCase(CString(LINE_KEYWORD)) == 0) { GATH_LINESPEC * pNewLineSpec = ReadLineInfo(pFile); if (pNewLineSpec == NULL) return 0; // Add the line we just read in to the end of the list of line specs for this // internal category. if (pCategory->m_pLineSpec == NULL) pCategory->m_pLineSpec = pNewLineSpec; else { GATH_LINESPEC * pLineSpec = pCategory->m_pLineSpec; while (pLineSpec->m_pNext) pLineSpec = pLineSpec->m_pNext; pLineSpec->m_pNext = pNewLineSpec; } } else if (strKeyword.CompareNoCase(CString(ENUMLINE_KEYWORD)) == 0) { GATH_LINESPEC * pNewLineSpec = ReadLineEnumRecursive(pFile, mapCategories); if (pNewLineSpec == NULL) return 0; // Add the line we just read in to the end of the list of line specs for this // internal category. if (pCategory->m_pLineSpec == NULL) pCategory->m_pLineSpec = pNewLineSpec; else { GATH_LINESPEC * pLineSpec = pCategory->m_pLineSpec; while (pLineSpec->m_pNext) pLineSpec = pLineSpec->m_pNext; pLineSpec->m_pNext = pNewLineSpec; } } else { ASSERT(FALSE); VerifyAndAdvanceFile(pFile, strKeyword); } } if (!VerifyAndAdvanceFile(pFile, CString("}"))) return 0; return dwID; } //----------------------------------------------------------------------------- // Get the category structure, given a DWORD ID. //----------------------------------------------------------------------------- INTERNAL_CATEGORY * CTemplateFileFunctions::GetInternalRep(CMapWordToPtr & mapCategories, DWORD dwID) { INTERNAL_CATEGORY * pReturn; if (mapCategories.Lookup((WORD) dwID, (void * &) pReturn)) return pReturn; return NULL; } //----------------------------------------------------------------------------- // Create the internal category structure. // // Version 6.0: this doesn't set the category ID. //----------------------------------------------------------------------------- INTERNAL_CATEGORY * CTemplateFileFunctions::CreateCategory(CMapWordToPtr & mapCategories, DWORD dwNewID, DWORD dwParentID, DWORD dwPrevID) { INTERNAL_CATEGORY * pInternalCat; INTERNAL_CATEGORY * pPreviousCat; CString strName; pInternalCat = new INTERNAL_CATEGORY; if (!pInternalCat) return NULL; pInternalCat->m_dwID = dwNewID; pInternalCat->m_fListView = TRUE; pInternalCat->m_dwParentID = dwParentID; pInternalCat->m_dwPrevID = dwPrevID; if (dwPrevID) { pPreviousCat = GetInternalRep(mapCategories, dwPrevID); if (pPreviousCat) pPreviousCat->m_dwNextID = dwNewID; } mapCategories.SetAt((WORD)dwNewID, (void *) pInternalCat); return pInternalCat; } //----------------------------------------------------------------------------- // This method simply reads an argument (as string) from the file, until a // punctuation or whitespace character is found. If a quote mark is found, // all characters are included in the string until another quote is found. // NOTE: currently no way to have a quote mark in the string. //----------------------------------------------------------------------------- BOOL CTemplateFileFunctions::ReadArgument(CFile * pFile, CString & strSource) { BOOL fInQuote = FALSE, fInComment = FALSE; CString strTemp; WCHAR cLastChar, cCurrentChar = L'\0'; // Skip over characters until we reach an alphanumeric char. If we find // a close paren, then we've reached the end of the argument list and // should return FALSE. do { // Save the last character read, since the comment token ("//") is // two characters long. cLastChar = cCurrentChar; // Read the next character in the file. if (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) != sizeof(WCHAR)) return FALSE; // If we're in a comment, and the character we just read isn't a new line, // we want to ignore it. if (fInComment) { if (cCurrentChar == L'\n') fInComment = FALSE; continue; } // Check to see if we've started into a comment. if (cCurrentChar == L'/') { if (cLastChar == L'/') fInComment = TRUE; continue; } if (cCurrentChar == L')') return FALSE; } while (!iswalnum(cCurrentChar) && cCurrentChar != L'"'); // Read characters into the string until we find whitespace or punctuation. do { if (cCurrentChar == L'"') { fInQuote = !fInQuote; continue; } if (iswalnum(cCurrentChar) || fInQuote) { char strt[5] = ""; BOOL used = FALSE; #ifdef UNICODE strTemp += (TCHAR) cCurrentChar; #else WideCharToMultiByte(CP_ACP, 0, &cCurrentChar, 1, strt, 2, "?", &used); strTemp += strt; #endif } else { break; } } while (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) == sizeof(WCHAR)); // If the last character read (the one which terminated this argument) was // not a comma, then back the file up so that the character can be re-read // and interpreted. if (cCurrentChar != L',') pFile->Seek(-(LONG)sizeof(WCHAR), CFile::current); strSource = strTemp; return TRUE; } //----------------------------------------------------------------------------- // A field consists of a source string, followed by a format string, followed // by a list of zero or more arguments. //----------------------------------------------------------------------------- BOOL CTemplateFileFunctions::ReadField(CFile * pFile, GATH_FIELD & field) { // Advance past the field keyword and read the two source and format strings. if (!VerifyAndAdvanceFile(pFile, CString(FIELD_KEYWORD) + CString("("))) return FALSE; if (!ReadArgument(pFile, field.m_strSource)) return FALSE; if (!ReadArgument(pFile, field.m_strFormat)) return FALSE; // Read arguments until there are no more, building them into a list of // arguments stored by the FIELD struct. GATH_VALUE arg; GATH_VALUE * pArg = NULL; while (ReadArgument(pFile, arg.m_strText)) { if (pArg == NULL) { field.m_pArgs = new GATH_VALUE; if (field.m_pArgs == NULL) return FALSE; *field.m_pArgs = arg; pArg = field.m_pArgs; } else { pArg->m_pNext = new GATH_VALUE; if (pArg->m_pNext == NULL) return FALSE; *pArg->m_pNext = arg; pArg = pArg->m_pNext; } } return TRUE; } //----------------------------------------------------------------------------- // Read an enumline(){} block. This construct is used to group lines together // which are enumerated for each instance of a class. A line is added to // the parent node's list of lines with a m_strEnumerateClass equal to the // class to be enumerated. The added line structure will have children lines // (the lines to be enumerated) referenced by m_pEnumeratedGroup. //----------------------------------------------------------------------------- GATH_LINESPEC * CTemplateFileFunctions::ReadLineEnumRecursive(CFile * pFile, CMapWordToPtr & mapCategories) { if (!VerifyAndAdvanceFile(pFile, CString(ENUMLINE_KEYWORD) + CString("("))) return NULL; // Declare a line specification variable to store the line info. GATH_LINESPEC * pNewLineSpec = new GATH_LINESPEC; if (pNewLineSpec == NULL) return NULL; // Read in the enumerated class variable. if (!ReadArgument(pFile, pNewLineSpec->m_strEnumerateClass)) { delete pNewLineSpec; return NULL; } // Read in the variable (zero or more) number of fields for the constraints. GATH_FIELD * pNewField = new GATH_FIELD; if (pNewField == NULL) return NULL; while (ReadField(pFile, *pNewField)) { if (pNewLineSpec->m_pConstraintFields == NULL) pNewLineSpec->m_pConstraintFields = pNewField; else { // Add the newly read field to the end of the field list. Note, // this is inefficient, and should be fixed. (NOTE) GATH_FIELD * pFieldScan = pNewLineSpec->m_pConstraintFields; while (pFieldScan->m_pNext) pFieldScan = pFieldScan->m_pNext; pFieldScan->m_pNext = pNewField; } pNewField = new GATH_FIELD; if (pNewField == NULL) return NULL; } delete pNewField; // Advance past the close paren and the (necessary) open bracket. if (!VerifyAndAdvanceFile(pFile, CString("){"))) { delete pNewLineSpec; return NULL; } // Read the contents of the block (should be all lines or enumlines). CString strKeyword; while (GetKeyword(pFile, strKeyword)) { if (strKeyword.CompareNoCase(CString(LINE_KEYWORD)) == 0) { GATH_LINESPEC * pNewSubLine = ReadLineInfo(pFile); if (pNewSubLine == NULL) { delete pNewLineSpec; return NULL; } if (pNewLineSpec->m_pEnumeratedGroup == NULL) pNewLineSpec->m_pEnumeratedGroup = pNewSubLine; else { GATH_LINESPEC * pLineSpec = pNewLineSpec->m_pEnumeratedGroup; while (pLineSpec->m_pNext) pLineSpec = pLineSpec->m_pNext; pLineSpec->m_pNext = pNewSubLine; } } else if (strKeyword.CompareNoCase(CString(ENUMLINE_KEYWORD)) == 0) { GATH_LINESPEC * pNewSubLine = ReadLineEnumRecursive(pFile, mapCategories); if (pNewSubLine == NULL) { delete pNewLineSpec; return NULL; } if (pNewLineSpec->m_pEnumeratedGroup == NULL) pNewLineSpec->m_pEnumeratedGroup = pNewSubLine; else { GATH_LINESPEC * pLineSpec = pNewLineSpec->m_pEnumeratedGroup; while (pLineSpec->m_pNext) pLineSpec = pLineSpec->m_pNext; pLineSpec->m_pNext = pNewSubLine; } } else { delete pNewLineSpec; return NULL; } } if (!VerifyAndAdvanceFile(pFile, CString("}"))) { delete pNewLineSpec; return NULL; } return pNewLineSpec; } //----------------------------------------------------------------------------- // This method reads in a "column" line from the file, adding the appropriate // entries for the columns into the category referenced by dwID. The column // line contains a bunch of fields in a list. //----------------------------------------------------------------------------- BOOL CTemplateFileFunctions::ReadColumnInfo(CFile * pFile, CMapWordToPtr & mapCategories, DWORD dwID) { CString strTemp; if (!VerifyAndAdvanceFile(pFile, CString(COLUMN_KEYWORD) + CString("("))) return FALSE; // Get the internal category referenced by dwID. INTERNAL_CATEGORY * pCategory = GetInternalRep(mapCategories, dwID); if (!pCategory) return FALSE; // We only allow one column specifier list per node. if (pCategory->m_pColSpec) return FALSE; // While we are still reading fields from the file, keep adding to the column list. GATH_FIELD * pNewField = new GATH_FIELD; if (pNewField == NULL) return FALSE; while (ReadField(pFile, *pNewField)) { if (pCategory->m_pColSpec == NULL) pCategory->m_pColSpec = pNewField; else { // Scan to the last field in the linespec.m_pFields list, and insert the new field. GATH_FIELD * pFieldScan = pCategory->m_pColSpec; while (pFieldScan->m_pNext) pFieldScan = pFieldScan->m_pNext; pFieldScan->m_pNext = pNewField; } // Parse the width out of the column caption. if (pNewField->m_strFormat.ReverseFind(_T(',')) != -1) { strTemp = pNewField->m_strFormat.Right(pNewField->m_strFormat.GetLength() - pNewField->m_strFormat.ReverseFind(_T(',')) - 1); pNewField->m_usWidth = (unsigned short) _ttoi(strTemp); pNewField->m_strFormat = pNewField->m_strFormat.Left(pNewField->m_strFormat.GetLength() - strTemp.GetLength() - 1); } else { ASSERT(FALSE); pNewField->m_usWidth = (unsigned short) 80; } // Parse off any remaining information in the column label (the label ends // with [name, n], when n is the width, and name is the ID for the column // which should not be displayed). if (pNewField->m_strFormat.ReverseFind(_T('[')) != -1) pNewField->m_strFormat = pNewField->m_strFormat.Left(pNewField->m_strFormat.ReverseFind(_T('[')) - 1); // Read the sorting type from the file. if (ReadArgument(pFile, strTemp)) { if (strTemp.CompareNoCase(CString(_T(SORT_LEXICAL))) == 0) pNewField->m_sort = LEXICAL; else if (strTemp.CompareNoCase(CString(_T(SORT_VALUE))) == 0) pNewField->m_sort = BYVALUE; else pNewField->m_sort = NOSORT; } else return FALSE; // Read the complexity (BASIC or ADVANCED) from the file. if (ReadArgument(pFile, strTemp)) { if (strTemp.CompareNoCase(CString(_T(COMPLEXITY_ADVANCED))) == 0) pNewField->m_datacomplexity = ADVANCED; else pNewField->m_datacomplexity = BASIC; } else return FALSE; pNewField = new GATH_FIELD; if (pNewField == NULL) return FALSE; } delete pNewField; if (!VerifyAndAdvanceFile(pFile, CString(")"))) return FALSE; return TRUE; } //----------------------------------------------------------------------------- // Read in the information for a single line. Add the line to the internal // representation of the category. NOTE: inefficient, since this will be // called multiple times and the line list will need to be scanned to the // end each time. //----------------------------------------------------------------------------- GATH_LINESPEC * CTemplateFileFunctions::ReadLineInfo(CFile * pFile) { if (!VerifyAndAdvanceFile(pFile, CString(LINE_KEYWORD) + CString("("))) return NULL; // Declare a line specification variable to store the line info. GATH_LINESPEC * pNewLineSpec = new GATH_LINESPEC; if (pNewLineSpec == NULL) return NULL; // While we are still reading fields from the file, keep adding to the column list. // NOTE: inefficient, repeated scans through linespec.m_pFields list. GATH_FIELD * pNewField = new GATH_FIELD; if (pNewField == NULL) { delete pNewLineSpec; return NULL; } // Read in the complexity (BASIC or ADVANCED) for this line. CString strTemp; if (ReadArgument(pFile, strTemp)) { if (strTemp.CompareNoCase(CString(_T(COMPLEXITY_ADVANCED))) == 0) pNewLineSpec->m_datacomplexity = ADVANCED; else pNewLineSpec->m_datacomplexity = BASIC; } else return FALSE; while (ReadField(pFile, *pNewField)) { if (pNewLineSpec->m_pFields == NULL) pNewLineSpec->m_pFields = pNewField; else { // Scan to the last field in the linespec.m_pFields list, and insert the new field. GATH_FIELD * pFieldScan = pNewLineSpec->m_pFields; while (pFieldScan->m_pNext) pFieldScan = pFieldScan->m_pNext; pFieldScan->m_pNext = pNewField; } pNewField = new GATH_FIELD; if (pNewField == NULL) { delete pNewLineSpec; return NULL; } } delete pNewField; if (!VerifyAndAdvanceFile(pFile, CString(")"))) { delete pNewLineSpec; return NULL; } return pNewLineSpec; } //----------------------------------------------------------------------------- // This method returns the next keyword in the file. Any whitespace or // punctuation is skipped until an alphanumeric character is read. The keyword // returned is the string starting with this character until whitespace or // punctuation is encountered. Note: it is very important that this function // returns the file to the state it was in when the function started, with // the current position restored. // // NOTE: inefficient //----------------------------------------------------------------------------- BOOL CTemplateFileFunctions::GetKeyword(CFile * pFile, CString & strKeyword) { CString strTemp = CString(""); DWORD dwPosition = pFile->GetPosition(); WCHAR cLastChar, cCurrentChar = L'\0'; BOOL fInComment = FALSE; // Skip over whitespace characters until we reach an alphanumeric char. do { // Save the last character read, since the comment token ("//") is // two characters long. cLastChar = cCurrentChar; // Read the next character in the file. if (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) != sizeof(WCHAR)) return FALSE; // If we're in a comment, and the character we just read isn't a new line, // we want to ignore it. if (fInComment) { if (cCurrentChar == L'\n') fInComment = FALSE; continue; } // Check to see if we've started into a comment. if (cCurrentChar == _T('/')) { if (cLastChar == _T('/')) fInComment = TRUE; continue; } } while (iswspace(cCurrentChar) || cCurrentChar == L'/' || fInComment); // Read the keyword while it's alphanumeric. if (iswalnum(cCurrentChar)) do { strTemp += (TCHAR) cCurrentChar; if (pFile->Read((void *) &cCurrentChar, sizeof(WCHAR)) != sizeof(WCHAR)) return FALSE; } while (iswalnum(cCurrentChar)); // Reset the file, set the keyword and return. pFile->Seek((LONG)dwPosition, CFile::begin); strKeyword = strTemp; return !strTemp.IsEmpty(); } //----------------------------------------------------------------------------- // INTERNAL_CATEGORY constructor and destructor. //----------------------------------------------------------------------------- INTERNAL_CATEGORY::INTERNAL_CATEGORY() { m_categoryName.m_strText = CString(" "); m_fieldName.m_strFormat = CString(" "); m_strEnumerateClass = CString(""); m_strIdentifier = CString(""); m_strNoInstances = CString(""); m_fListView = FALSE; m_fDynamic = FALSE; m_dwID = 0; m_dwParentID = 0; m_dwChildID = 0; m_dwPrevID = 0; m_dwNextID = 0; m_dwDynamicChildID = 0; m_dwColCount = 0; m_pColSpec = NULL; m_aCols = NULL; m_pLineSpec = NULL; m_dwLineCount = 0; m_apLines = NULL; m_fIncluded = TRUE; m_fRefreshed = FALSE; m_dwLastError = S_OK; // GATH_ERR_NOERROR; } INTERNAL_CATEGORY::~INTERNAL_CATEGORY() { if (m_pColSpec) delete m_pColSpec; if (m_aCols) delete [] m_aCols; if (m_pLineSpec) delete m_pLineSpec; if (m_apLines) { for (DWORD dwIndex = 0; dwIndex < m_dwLineCount; dwIndex++) delete m_apLines[dwIndex]; delete [] m_apLines; } } //----------------------------------------------------------------------------- // GATH_FIELD constructor and destructor. //----------------------------------------------------------------------------- GATH_FIELD::GATH_FIELD() { m_pArgs = NULL; m_pNext = NULL; m_usWidth = 0; m_sort = NOSORT; m_datacomplexity = BASIC; } GATH_FIELD::~GATH_FIELD() { if (m_pArgs) delete m_pArgs; if (m_pNext) delete m_pNext; } //----------------------------------------------------------------------------- // GATH_VALUE constructor and destructor. //----------------------------------------------------------------------------- GATH_VALUE::GATH_VALUE() { m_pNext = NULL; m_dwValue = 0L; } GATH_VALUE::~GATH_VALUE() { if (m_pNext) delete m_pNext; } //----------------------------------------------------------------------------- // GATH_LINESPEC constructor and destructor. //----------------------------------------------------------------------------- GATH_LINESPEC::GATH_LINESPEC() { m_pFields = NULL; m_pEnumeratedGroup = NULL; m_pConstraintFields = NULL; m_pNext = NULL; m_datacomplexity = BASIC; } GATH_LINESPEC::~GATH_LINESPEC() { if (m_pFields) delete m_pFields; if (m_pEnumeratedGroup) delete m_pEnumeratedGroup; if (m_pConstraintFields) delete m_pConstraintFields; if (m_pNext) delete m_pNext; } //----------------------------------------------------------------------------- // GATH_LINE constructor and destructor. //----------------------------------------------------------------------------- GATH_LINE::GATH_LINE() { m_datacomplexity = BASIC; m_aValue = NULL; } GATH_LINE::~GATH_LINE() { if (m_aValue) delete [] m_aValue; } //----------------------------------------------------------------------------- // This function is called to refresh the data for all of the extensions. It // will use the refresh index to look up the line spec, and then call some // version 5.0 functions to do the refresh. Finally, it will convert the // data generated by those functions into our new format. //----------------------------------------------------------------------------- HRESULT RefreshExtensions(CWMIHelper * pWMI, DWORD dwIndex, volatile BOOL * pfCancel, CPtrList * aColValues, int iColCount, void ** ppCache) { HRESULT hr = S_OK; if (pWMI == NULL) return hr; pWMI->m_hrLastVersion5Error = S_OK; // Reset the caches so the data is actually refreshed (140535). pWMI->Version5ClearCache(); pWMI->m_enumMap.Reset(); // Get the line spec pointer for this index. GATH_LINESPEC * pLineSpec = gmapExtensionRefreshData.Lookup(dwIndex); if (pLineSpec == NULL) return hr; // Here's some code from 5.0 for refreshing a list of line pointers for a line spec. CPtrList listLinePtrs; if (CRefreshFunctions::RefreshLines(pWMI, pLineSpec, (DWORD) iColCount, listLinePtrs, pfCancel)) { // Move the contents of the list of lines to our internal structures. if (listLinePtrs.GetCount() > 0) { GATH_LINE * pLine; for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;) { pLine = (GATH_LINE *) listLinePtrs.GetNext(pos); if (pLine == NULL || pLine->m_aValue == NULL) continue; for (int iCol = 0; iCol < iColCount; iCol++) { CString strValue = pLine->m_aValue[iCol].m_strText; DWORD dwValue = pLine->m_aValue[iCol].m_dwValue; BOOL fAdvanced = (pLine->m_datacomplexity == ADVANCED); pWMI->AppendCell(aColValues[iCol], strValue, dwValue, fAdvanced); } delete pLine; } } else { CString * pstrNoData = gmapExtensionRefreshData.LookupString(dwIndex); if (pstrNoData && !pstrNoData->IsEmpty() && iColCount > 0) { pWMI->AppendCell(aColValues[0], *pstrNoData, 0, FALSE); for (int iCol = 1; iCol < iColCount; iCol++) pWMI->AppendCell(aColValues[iCol], _T(""), 0, FALSE); } } } return pWMI->m_hrLastVersion5Error; } //----------------------------------------------------------------------------- // Refresh the list of lines based on the list of line fields. We'll also // need to set the number of lines. The list of lines is generated based on // the pLineSpec pointer and dwColumns variables. The generated lines are // returned in the listLinePtrs parameter. //----------------------------------------------------------------------------- BOOL CRefreshFunctions::RefreshLines(CWMIHelper * pWMI, GATH_LINESPEC * pLineSpec, DWORD dwColumns, CPtrList & listLinePtrs, volatile BOOL * pfCancel) { BOOL bReturn = TRUE; // Traverse the list of line specifiers to generate the list of lines. GATH_LINESPEC * pCurrentLineSpec = pLineSpec; GATH_LINE * pLine = NULL; while (pCurrentLineSpec && (pfCancel == NULL || *pfCancel == FALSE)) { // Check if the current line spec is for a single line or an enumerated group. if (pCurrentLineSpec->m_strEnumerateClass.IsEmpty() || pCurrentLineSpec->m_strEnumerateClass.CompareNoCase(CString(STATIC_SOURCE)) == 0) { // This is for a single line. Allocate a new line structure and fill it // in with the data generated from the line spec. pLine = new GATH_LINE; if (pLine == NULL) { bReturn = FALSE; break; } if (RefreshOneLine(pWMI, pLine, pCurrentLineSpec, dwColumns)) listLinePtrs.AddTail((void *) pLine); else { bReturn = FALSE; break; } } else { // This line represents an enumerated group of lines. We need to enumerate // the class and call RefreshLines for the group of enumerated lines, once // for each class instance. if (pWMI->Version5ResetClass(pCurrentLineSpec->m_strEnumerateClass, pCurrentLineSpec->m_pConstraintFields)) do { if (!RefreshLines(pWMI, pCurrentLineSpec->m_pEnumeratedGroup, dwColumns, listLinePtrs, pfCancel)) break; } while (pWMI->Version5EnumClass(pCurrentLineSpec->m_strEnumerateClass, pCurrentLineSpec->m_pConstraintFields)); } pCurrentLineSpec = pCurrentLineSpec->m_pNext; } if (pfCancel && *pfCancel) return FALSE; // If there was a failure generating the lines, clean up after ourselves. if (!bReturn) { if (pLine) delete pLine; for (POSITION pos = listLinePtrs.GetHeadPosition(); pos != NULL;) { pLine = (GATH_LINE *) listLinePtrs.GetNext(pos) ; if (pLine) delete pLine; } listLinePtrs.RemoveAll(); return FALSE; } return TRUE; } //----------------------------------------------------------------------------- // Refresh a line based on a line spec. //----------------------------------------------------------------------------- BOOL CRefreshFunctions::RefreshOneLine(CWMIHelper * pWMI, GATH_LINE * pLine, GATH_LINESPEC * pLineSpec, DWORD dwColCount) { // Allocate the new array of values. if (pLine->m_aValue) delete [] pLine->m_aValue; pLine->m_aValue = new GATH_VALUE[dwColCount]; if (pLine->m_aValue == NULL) return FALSE; // Set the data complexity for the line based on the line spec. pLine->m_datacomplexity = pLineSpec->m_datacomplexity; // Compute each of the values for the fields. GATH_FIELD * pField = pLineSpec->m_pFields; for (DWORD dwIndex = 0; dwIndex < dwColCount; dwIndex++) { if (pField == NULL) return FALSE; if (!RefreshValue(pWMI, &pLine->m_aValue[dwIndex], pField)) return FALSE; pField = pField->m_pNext; } return TRUE; } //----------------------------------------------------------------------------- // This method takes the information in a GATH_FIELD struct and uses it to // generate a current GATH_VALUE struct. //----------------------------------------------------------------------------- BOOL CRefreshFunctions::RefreshValue(CWMIHelper * pWMI, GATH_VALUE * pVal, GATH_FIELD * pField) { TCHAR szFormatFragment[MAX_PATH]; const TCHAR *pSourceChar; TCHAR *pDestinationChar; TCHAR cFormat = _T('\0'); BOOL fReadPercent = FALSE; BOOL fReturnValue = TRUE; CString strResult, strTemp; int iArgNumber = 0; DWORD dwValue = 0L; // Process the format string. Because of the difficulty caused by having // variable number of arguments to be inserted (like printf), we'll need // to break the format string into chunks and do the sprintf function // for each format flag we come across. pSourceChar = (LPCTSTR) pField->m_strFormat; pDestinationChar = szFormatFragment; while (*pSourceChar) { if (fReadPercent) { // If we read a percent sign, we should be looking for a valid flag. // We are using some additional flags to printf (and not supporting // others). If we read another percent, just insert a single percent. switch (*pSourceChar) { case _T('%'): fReadPercent = FALSE; break; case _T('b'): case _T('B'): case _T('l'): case _T('L'): case _T('u'): case _T('U'): case _T('s'): case _T('S'): fReadPercent = FALSE; cFormat = *pSourceChar; *pDestinationChar = _T('s'); break; case _T('t'): case _T('T'): fReadPercent = FALSE; cFormat = *pSourceChar; *pDestinationChar = _T('s'); break; case _T('x'): case _T('X'): case _T('d'): case _T('D'): fReadPercent = FALSE; cFormat = _T('d'); *pDestinationChar = *pSourceChar; break; case _T('q'): case _T('Q'): fReadPercent = FALSE; cFormat = _T('q'); *pDestinationChar = _T('s'); break; case _T('z'): case _T('Z'): fReadPercent = FALSE; cFormat = _T('z'); *pDestinationChar = _T('s'); break; case _T('y'): case _T('Y'): fReadPercent = FALSE; cFormat = _T('y'); *pDestinationChar = _T('s'); break; case _T('v'): case _T('V'): fReadPercent = FALSE; cFormat = _T('v'); *pDestinationChar = _T('s'); break; case _T('f'): case _T('F'): fReadPercent = FALSE; cFormat = *pSourceChar; *pDestinationChar = *pSourceChar; break; default: *pDestinationChar = *pSourceChar; } } else if (*pSourceChar == _T('%')) { *pDestinationChar = _T('%'); fReadPercent = TRUE; } else *pDestinationChar = *pSourceChar; pSourceChar++; pDestinationChar++; // If a format flag is set or we are at the end of the source string, // then we have a complete fragment and we should produce some output, // which will be concatenated to the strResult string. if (cFormat || *pSourceChar == _T('\0')) { *pDestinationChar = _T('\0'); if (cFormat) { // Based on the format type, get a value from the provider for // the next argument. Format the result using the formatting // fragment we extracted, and concatenate it. if (GetValue(pWMI, cFormat, szFormatFragment, strTemp, dwValue, pField, iArgNumber++)) { strResult += strTemp; cFormat = _T('\0'); } else { strResult = strTemp; break; } } else { // There was no format flag, but we are at the end of the string. // Add the fragment we got to the result string. strResult += CString(szFormatFragment); } pDestinationChar = szFormatFragment; } } // Assign the values we generated to the GATH_VALUE structure. Important note: // the dwValue variable will only have ONE value, even though multiple values // might have been generated to build the strResult string. Only the last // value will be saved in dwValue. This is OK, because this value is only // used for sorting a column when the column is marked for non-lexical sorting. // In that case, there should be only one value used to generat the string. pVal->m_strText = strResult; pVal->m_dwValue = dwValue; return fReturnValue; } //----------------------------------------------------------------------------- // Return a string with delimiters added for the number. //----------------------------------------------------------------------------- CString DelimitNumber(double dblValue) { NUMBERFMT fmt; TCHAR szResult[MAX_PATH] = _T(""); TCHAR szDelimiter[4] = _T(","); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szDelimiter, 4); memset(&fmt, 0, sizeof(NUMBERFMT)); fmt.Grouping = 3; fmt.lpDecimalSep = _T(""); // doesn't matter - there aren't decimal digits fmt.lpThousandSep = szDelimiter; CString strValue; strValue.Format(_T("%.0f"), dblValue); GetNumberFormat(LOCALE_USER_DEFAULT, 0, strValue, &fmt, szResult, MAX_PATH); return CString(szResult); } //----------------------------------------------------------------------------- // This method gets a single value from the provider, based on the format // character from the template file. It formats the results using the // format string szFormatFragment, which should only take one argument. //----------------------------------------------------------------------------- BOOL CRefreshFunctions::GetValue(CWMIHelper * pWMI, TCHAR cFormat, TCHAR *szFormatFragment, CString &strResult, DWORD &dwResult, GATH_FIELD *pField, int iArgNumber) { CString strTemp; COleDateTime datetimeTemp; double dblValue; AFX_MANAGE_STATE(AfxGetStaticModuleState()); strResult.Empty(); dwResult = 0L; if (!pField->m_strSource.IsEmpty() && pField->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) != 0) { // Find the right argument for this formatting (indicated by the iArgNumber // parameter. GATH_VALUE * pArg = pField->m_pArgs; while (iArgNumber && pArg) { pArg = pArg->m_pNext; iArgNumber--; } if (pArg == NULL) return FALSE; switch (cFormat) { case 'b': case 'B': // This is a boolean type. Show either true or false, depending on // the numeric value. if (pWMI->Version5QueryValueDWORD(pField->m_strSource, pArg->m_strText, dwResult, strTemp)) { strTemp = (dwResult) ? pWMI->m_strTrue : pWMI->m_strFalse; strResult.Format(szFormatFragment, strTemp); return TRUE; } else { strResult = strTemp; return FALSE; } break; case 'd': case 'D': // This is the numeric type. if (pWMI->Version5QueryValueDWORD(pField->m_strSource, pArg->m_strText, dwResult, strTemp)) { strResult.Format(szFormatFragment, dwResult); return TRUE; } else { strResult = strTemp; return FALSE; } break; case 'f': case 'F': // This is the double floating point type. if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp)) { strResult.Format(szFormatFragment, dblValue); return TRUE; } else { strResult = strTemp; return FALSE; } break; case 't': case 'T': // This is the OLE date and time type. Format the date and time into the // string result, and return the date part in the DWORD (the day number is // to the left of the decimal in the DATE type). if (pWMI->Version5QueryValueDateTime(pField->m_strSource, pArg->m_strText, datetimeTemp, strTemp)) { strResult = datetimeTemp.Format(); dwResult = (DWORD)(DATE)datetimeTemp; return TRUE; } else { strResult = strTemp; return FALSE; } break; case 'l': case 'L': // This is a string type, with the string converted to lower case. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp)) { strTemp.MakeLower(); strResult.Format(szFormatFragment, strTemp); return TRUE; } else { strResult = strTemp; return FALSE; } break; case 'u': case 'U': // This is a string type, with the string converted to upper case. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp)) { strTemp.MakeUpper(); strResult.Format(szFormatFragment, strTemp); return TRUE; } else { strResult = strTemp; return FALSE; } break; case 's': case 'S': // This is the string type (string is the default type). if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp)) { strResult.Format(szFormatFragment, strTemp); // We only need to do this when the value returned is a number // and is going in a column that we want to sort numerically. // This won't break the case where a numeric string is to be // sorted as a string because dwResult will be ignored. if (!strTemp.IsEmpty() && iswdigit( strTemp[0])) dwResult = _ttol( (LPCTSTR)strTemp); return TRUE; } else { strResult = strTemp; return FALSE; } break; case 'q': case 'Q': // This is a specialized type for the Win32_BIOS class. We want to show // the "Version" property - if it isn't there, then we want to show // the "Name" property and "ReleaseDate" properties concatenated // together. if (pWMI->Version5QueryValue(pField->m_strSource, CString(_T("Version")), strTemp)) { strResult = strTemp; return TRUE; } else { if (pWMI->Version5QueryValue(pField->m_strSource, CString(_T("Name")), strTemp)) strResult = strTemp; if (pWMI->Version5QueryValueDateTime(pField->m_strSource, CString(_T("ReleaseDate")), datetimeTemp, strTemp)) strResult += CString(_T(" ")) + datetimeTemp.Format(); return TRUE; } break; case 'z': case 'Z': // This is a specialized size type, where the value is a numeric count // of bytes. We want to convert it into the best possible units for // display (for example, display "4.20 MB (4,406,292 bytes)"). if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp)) { double dValue = (double) dblValue; DWORD dwDivisor = 1; // Reduce the dValue to the smallest possible number (with a larger unit). while (dValue > 1024.0 && dwDivisor < (1024 * 1024 * 1024)) { dwDivisor *= 1024; dValue /= 1024.0; } if (dwDivisor == 1) strResult.Format(IDS_SIZEBYTES, DelimitNumber(dblValue)); else if (dwDivisor == (1024)) strResult.Format(IDS_SIZEKB_BYTES, dValue, DelimitNumber(dblValue)); else if (dwDivisor == (1024 * 1024)) strResult.Format(IDS_SIZEMB_BYTES, dValue, DelimitNumber(dblValue)); else if (dwDivisor == (1024 * 1024 * 1024)) strResult.Format(IDS_SIZEGB_BYTES, dValue, DelimitNumber(dblValue)); dwResult = (DWORD) dblValue; // So we can sort on this value (bug 391127). } else { strResult = strTemp; return FALSE; } break; case 'y': case 'Y': // This is a specialized size type, where the value is a numeric count // of bytes, already in KB. If it's big enough, show it in MB or GB. if (pWMI->Version5QueryValueDoubleFloat(pField->m_strSource, pArg->m_strText, dblValue, strTemp)) { strResult.Format(IDS_SIZEKB, DelimitNumber(dblValue)); dwResult = (DWORD) dblValue; // So we can sort on this value (bug 391127). } else { strResult = strTemp; return FALSE; } break; case 'v': case 'V': // This is a specialized type, assumed to be an LCID (locale ID). Show the // locale. if (pWMI->Version5QueryValue(pField->m_strSource, pArg->m_strText, strTemp)) { // strTemp contains a string locale ID (like "0409"). Convert it into // and actual LCID. LCID lcid = (LCID) _tcstoul(strTemp, NULL, 16); TCHAR szCountry[MAX_PATH]; if (GetLocaleInfo(lcid, LOCALE_SCOUNTRY, szCountry, MAX_PATH)) strResult = szCountry; else strResult = strTemp; } else { strResult = strTemp; return FALSE; } break; default: ASSERT(FALSE); // unknown formatting flag return TRUE; } } return FALSE; } //============================================================================= // Functions extending the CWMILiveHelper to support version 5 style refreshes. //============================================================================= #include "wmilive.h" //----------------------------------------------------------------------------- // Reset the CMSIEnumerator pointer to the start of the enumeration (and // make sure there is one). Remove the object pointer, so the first call // to GetObject will return the first item in the enumerator. //----------------------------------------------------------------------------- BOOL CWMILiveHelper::Version5ResetClass(const CString & strClass, GATH_FIELD * pConstraints) { CMSIEnumerator * pMSIEnumerator = Version5GetEnumObject(strClass, pConstraints); if (pMSIEnumerator == NULL) return FALSE; // Reset the enumerator, and remove the cached object pointer if there is one. pMSIEnumerator->Reset(pConstraints); Version5RemoveObject(strClass); CMSIObject * pObject = Version5GetObject(strClass, pConstraints); if (pObject == NULL || pObject->IsValid() == MOS_NO_INSTANCES) return FALSE; return TRUE; } //----------------------------------------------------------------------------- // Move the cached IWbemClassObject pointer to the next instance. //----------------------------------------------------------------------------- BOOL CWMILiveHelper::Version5EnumClass(const CString & strClass, GATH_FIELD * pConstraints) { // Verify that there is an object enumerator in place. if (Version5GetEnumObject(strClass, pConstraints) == NULL) return FALSE; // If there is an object interface, remove it, then make a new one. // Then retrieve the object pointer (this will do the Next on the // enumerator to get the next instance). Version5RemoveObject(strClass); CMSIObject * pObject = Version5GetObject(strClass, pConstraints); if (pObject && (pObject->IsValid() == MOS_INSTANCE)) return TRUE; return FALSE; } //----------------------------------------------------------------------------- // Retrieve the interface pointer for the specified IEnumWbemClassObject. // If there isn't one cached, create one and cache it. It's possible for the // pConstraints parameter to contain a field specify a WBEM SQL condition for // this enumerator. //----------------------------------------------------------------------------- CMSIEnumerator * CWMILiveHelper::Version5GetEnumObject(const CString & strClass, const GATH_FIELD * pConstraints) { // See if we've cached this enumerator object. CMSIEnumerator * pReturn = NULL; if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pReturn)) return pReturn; // We'll need to create this enumerator here, and save it in the cache. pReturn = new CMSIEnumerator; if (pReturn == NULL) return NULL; if (FAILED(pReturn->Create(strClass, pConstraints, this))) { delete pReturn; return NULL; } m_mapClassToEnumInterface.SetAt(strClass, (void *) pReturn); return pReturn; } //----------------------------------------------------------------------------- // Remove the specified IWbemClassObject pointer from the cache. //----------------------------------------------------------------------------- void CWMILiveHelper::Version5RemoveObject(const CString & strClass) { CMSIObject * pObject = NULL; if (m_mapClassToInterface.Lookup(strClass, (void * &) pObject) && pObject) delete pObject; m_mapClassToInterface.RemoveKey(strClass); } //----------------------------------------------------------------------------- // Retrieve the interface pointer for the specified IWbemClassObject. // If there isn't one cached, create one and cache it. //----------------------------------------------------------------------------- CMSIObject * CWMILiveHelper::Version5GetObject(const CString & strClass, const GATH_FIELD * pConstraints, CString * pstrLabel) { CMSIObject * pReturn = NULL; if (m_mapClassToInterface.Lookup(strClass, (void * &) pReturn)) return pReturn; // We don't have one of these objects cached. Get one from the enumerator. CMSIEnumerator * pEnumerator = Version5GetEnumObject(strClass); if (pEnumerator) { HRESULT hr = pEnumerator->Next(&pReturn); if (S_OK != hr) { if (pReturn) delete pReturn; pReturn = NULL; m_hrError = hr; } } if (pReturn) m_mapClassToInterface.SetAt(strClass, (void *) pReturn); return pReturn; } //----------------------------------------------------------------------------- // This method is used to get the current value for a given class and property // string. Starting with the IWbemServices interface, it gets an interface // for the requested class enums the first instance. Performance is improved // by caching the instance interfaces in m_mapClassToInterface. //----------------------------------------------------------------------------- BOOL CWMILiveHelper::Version5QueryValue(const CString & strClass, const CString & strProperty, CString & strResult) { strResult.Empty(); CMSIObject * pObject = Version5GetObject(strClass, NULL); ASSERT(pObject); if (!pObject) return FALSE; switch (pObject->IsValid()) { case MOS_INSTANCE: { BOOL fUseValueMap = FALSE; CString strProp(strProperty); if (strProp.Left(8) == CString(_T("ValueMap"))) { strProp = strProp.Right(strProp.GetLength() - 8); fUseValueMap = TRUE; } VARIANT variant; BSTR propName = strProp.AllocSysString(); VariantInit(&variant); VariantClear(&variant); if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK) { // If the property we just got is an array, we should convert it to string // containing a list of the items in the array. if ((variant.vt & VT_ARRAY) && (variant.vt & VT_BSTR) && variant.parray) { if (SafeArrayGetDim(variant.parray) == 1) { long lLower = 0, lUpper = 0; SafeArrayGetLBound(variant.parray, 0, &lLower); SafeArrayGetUBound(variant.parray, 0, &lUpper); CString strWorking; BSTR bstr = NULL; for (long i = lLower; i <= lUpper; i++) if (SUCCEEDED(SafeArrayGetElement(variant.parray, &i, (wchar_t*)&bstr))) { if (i != lLower) strWorking += _T(", "); strWorking += bstr; } strResult = strWorking; return TRUE; } } else if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { strResult = V_BSTR(&variant); CString strFound; if (fUseValueMap && SUCCEEDED(Version5CheckValueMap(strClass, strProp, strResult, strFound))) strResult = strFound; return TRUE; } else strResult = m_strPropertyUnavail; } else strResult = m_strBadProperty; } break; case MOS_MSG_INSTANCE: pObject->GetErrorLabel(strResult); break; case MOS_NO_INSTANCES: default: ASSERT(FALSE); break; } return FALSE; } //----------------------------------------------------------------------------- // This method is equivalent to QueryValue, except it returns a DWORD value. // If FALSE is returned, then the string in strMessage should be displayed. //----------------------------------------------------------------------------- BOOL CWMILiveHelper::Version5QueryValueDWORD(const CString & strClass, const CString & strProperty, DWORD & dwResult, CString & strMessage) { dwResult = 0L; strMessage.Empty(); CMSIObject * pObject = Version5GetObject(strClass, NULL); ASSERT(pObject); if (!pObject) return FALSE; switch (pObject->IsValid()) { case MOS_INSTANCE: { VARIANT variant; BSTR propName = strProperty.AllocSysString(); VariantInit(&variant); VariantClear(&variant); if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK) { if (VariantChangeType(&variant, &variant, 0, VT_I4) == S_OK) { dwResult = V_I4(&variant); return TRUE; } else strMessage = m_strPropertyUnavail; } else strMessage = m_strBadProperty; } break; case MOS_MSG_INSTANCE: pObject->GetErrorLabel(strMessage); break; case MOS_NO_INSTANCES: default: ASSERT(FALSE); break; } return FALSE; } //----------------------------------------------------------------------------- // This method is equivalent to QueryValue, except it returns a double float // value. If FALSE is returned, then the string in strMessage should // be displayed. //----------------------------------------------------------------------------- BOOL CWMILiveHelper::Version5QueryValueDoubleFloat(const CString & strClass, const CString & strProperty, double & dblResult, CString & strMessage) { dblResult = 0L; strMessage.Empty(); CMSIObject * pObject = Version5GetObject(strClass, NULL); ASSERT(pObject); if (!pObject) return FALSE; switch (pObject->IsValid()) { case MOS_INSTANCE: { VARIANT variant; BSTR propName = strProperty.AllocSysString(); VariantInit(&variant); VariantClear(&variant); if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK) { if (VariantChangeType(&variant, &variant, 0, VT_R8) == S_OK) { dblResult = V_R8(&variant); return TRUE; } else strMessage = m_strPropertyUnavail; } else strMessage = m_strBadProperty; } break; case MOS_MSG_INSTANCE: pObject->GetErrorLabel(strMessage); break; case MOS_NO_INSTANCES: default: ASSERT(FALSE); break; } return FALSE; } //----------------------------------------------------------------------------- // This method is equivalent to QueryValue, except it returns an OLE date // & time object. If FALSE is returned, then the string in strMessage should // be displayed. //----------------------------------------------------------------------------- BOOL CWMILiveHelper::Version5QueryValueDateTime(const CString & strClass, const CString & strProperty, COleDateTime & datetime, CString & strMessage) { datetime.SetDateTime(0, 1, 1, 0, 0, 0); strMessage.Empty(); CMSIObject * pObject = Version5GetObject(strClass, NULL); ASSERT(pObject); if (!pObject) return FALSE; switch (pObject->IsValid()) { case MOS_INSTANCE: { VARIANT variant; BSTR propName = strProperty.AllocSysString(); VariantInit(&variant); VariantClear(&variant); if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK) { if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { // Parse the date and time into an COleDateTime object. Note: we should // be able to get an OLE date from WBEM, but for now we need to just // deal with the string returned. int nYear, nMonth, nDay, nHour, nMin, nSec; CString strTemp = V_BSTR(&variant); nYear = _ttoi(strTemp.Mid(0, 4)); nMonth = _ttoi(strTemp.Mid(4, 2)); nDay = _ttoi(strTemp.Mid(6, 2)); nHour = _ttoi(strTemp.Mid(8, 2)); nMin = _ttoi(strTemp.Mid(10, 2)); nSec = _ttoi(strTemp.Mid(12, 2)); datetime.SetDateTime(nYear, nMonth, nDay, nHour, nMin, nSec); return TRUE; } else strMessage = m_strPropertyUnavail; } else strMessage = m_strBadProperty; } break; case MOS_MSG_INSTANCE: pObject->GetErrorLabel(strMessage); break; case MOS_NO_INSTANCES: default: ASSERT(FALSE); break; } return FALSE; } //----------------------------------------------------------------------------- // Evaluate whether or not a specific object meets the filtering requirements // set by the constraints (filtering are the constraints where one half is // a static value). //----------------------------------------------------------------------------- BOOL CWMILiveHelper::Version5EvaluateFilter(IWbemClassObject * pObject, const GATH_FIELD * pConstraints) { const GATH_FIELD * pLHS = pConstraints, * pRHS = NULL; VARIANT variant; CString strValue; BSTR propName; ASSERT(pObject); if (pObject == NULL) return FALSE; while (pLHS && pLHS->m_pNext) { pRHS = pLHS->m_pNext; VariantInit(&variant); // If either the left or right hand side is static, we need to do the check. // First check out if the left side is static. if (pLHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0 && pRHS->m_pArgs) { propName = pRHS->m_pArgs->m_strText.AllocSysString(); strValue.Empty(); VariantClear(&variant); if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK) if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { strValue = V_BSTR(&variant); if (strValue.CompareNoCase(pLHS->m_strFormat) != 0) return FALSE; } } // Next check out if the right side is static. if (pRHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0 && pLHS->m_pArgs) { propName = pLHS->m_pArgs->m_strText.AllocSysString(); strValue.Empty(); VariantClear(&variant); if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK) if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { strValue = V_BSTR(&variant); if (strValue.CompareNoCase(pRHS->m_strFormat) != 0) return FALSE; } } // Advance our pointer to the left hand side by two. pLHS = pRHS->m_pNext; } return TRUE; } //----------------------------------------------------------------------------- // This method uses an object interface and the constraint fields to advance // any joined classes to the correct instances. //----------------------------------------------------------------------------- void CWMILiveHelper::Version5EvaluateJoin(const CString & strClass, IWbemClassObject * pObject, const GATH_FIELD * pConstraints) { const GATH_FIELD *pLHS = pConstraints, *pRHS = NULL; const GATH_FIELD *pEnumerated, *pJoinedTo; GATH_FIELD fieldEnumerated, fieldJoinedTo; VARIANT variant; CString strValue; BSTR propName; ASSERT(pObject); if (pObject == NULL) return; while (pLHS && pLHS->m_pNext) { pRHS = pLHS->m_pNext; // If either side is static, this is a filter, rather than a join. if ((pRHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0) || (pLHS->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)) { pLHS = pRHS->m_pNext; continue; } // Find out which side refers to the class we're enumerating. if (pRHS->m_strSource.CompareNoCase(strClass) == 0) { pEnumerated = pRHS; pJoinedTo = pLHS; } else if (pLHS->m_strSource.CompareNoCase(strClass) == 0) { pEnumerated = pLHS; pJoinedTo = pRHS; } else { pLHS = pRHS->m_pNext; continue; } // Next, enumerate through the instances of the joined to class until // we find one which satisfies the constraint. We can use the EvaluateFilter // method to find out when the constraint is met. Set up a field pointer // for the constraint (get the value from the enumerated class and use it // as a static. fieldJoinedTo = *pJoinedTo; fieldJoinedTo.m_pNext = NULL; VariantInit(&variant); strValue.Empty(); if (pEnumerated->m_pArgs) { propName = pEnumerated->m_pArgs->m_strText.AllocSysString(); VariantClear(&variant); if (pObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK) if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { strValue = V_BSTR(&variant); } } fieldEnumerated.m_strSource = CString(STATIC_SOURCE); fieldEnumerated.m_pNext = &fieldJoinedTo; fieldEnumerated.m_strFormat = strValue; // Now, enumerate the joined to class until it meets the constraints. Version5RemoveObject(pJoinedTo->m_strSource); Version5ResetClass(pJoinedTo->m_strSource, &fieldEnumerated); Version5GetObject(pJoinedTo->m_strSource, &fieldEnumerated); // Advance our pointer to the left hand side by two. pLHS = pRHS->m_pNext; } // Because the GATH_FIELD destructor follows next pointers, we want // to unlink our two GATH_FIELD locals. Also, we don't want the // destructor for fieldJoinedTo to delete the arguments. fieldEnumerated.m_pNext = NULL; fieldJoinedTo.m_pArgs = NULL; } //----------------------------------------------------------------------------- // Evaluate whether or not the constraints indicate that a class is being // enumerated as a dependency class. This is currently indicated by a single // field structure with a static value of "dependency". //----------------------------------------------------------------------------- BOOL CWMILiveHelper::Version5IsDependencyJoin(const GATH_FIELD * pConstraints) { if (pConstraints != NULL && pConstraints->m_pNext == NULL) if (pConstraints->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0) if (pConstraints->m_strFormat.CompareNoCase(CString(DEPENDENCY_JOIN)) == 0) return TRUE; return FALSE; } //----------------------------------------------------------------------------- // This method is used when a dependency class is being enumerated. In a // dependency class, each property of a class instance contains a reference // to an instance in another class. This method will cache eache of the // instances specified by the dependency class so properties of those instances // can be referred to in the line structures. //----------------------------------------------------------------------------- void CWMILiveHelper::Version5EvaluateDependencyJoin(IWbemClassObject * pObject) { VARIANT variant, varClassName; IWbemClassObject * pNewObject = NULL; //if (pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_LOCAL_ONLY) == S_OK) //while (pObject->Next(0, NULL, &variant, NULL, NULL) == S_OK) VariantInit(&variant); VariantClear(&variant); if (pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY) == WBEM_S_NO_ERROR) while (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR) { if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { // Use the object path to create a pointer to the specified // object and store it in the cache. CString strObjectPath = V_BSTR(&variant); BSTR bstrObjectPath = strObjectPath.AllocSysString(); HRESULT hRes = m_pIWbemServices->GetObject(bstrObjectPath, 0, NULL, &pNewObject, NULL); if (SUCCEEDED(hRes)) { // We need to get the class name of the new object so we know // where to cache it. We could parse it out of the object path, // but it will be better in the long run to get it by querying // the new object. if (pNewObject) { CString strClassName, strClassNameProp(_T("__CLASS")); BSTR classNameProp = strClassNameProp.AllocSysString(); VariantInit(&varClassName); VariantClear(&varClassName); if (pNewObject->Get(classNameProp, 0L, &varClassName, NULL, NULL) == S_OK) if (VariantChangeType(&varClassName, &varClassName, 0, VT_BSTR) == S_OK) strClassName = V_BSTR(&varClassName); if (!strClassName.IsEmpty()) { CMSIObject * pNewMSIObject = new CMSIObject(pNewObject, CString(_T("")), S_OK, this, MOS_INSTANCE); if (pNewMSIObject) { CMSIObject * pOldObject = NULL; if (m_mapClassToInterface.Lookup(strClassName, (void * &) pOldObject) && pOldObject) delete pOldObject; m_mapClassToInterface.SetAt(strClassName, (void *) pNewMSIObject); } } else { delete pNewObject; pNewObject = NULL; } } } } VariantClear(&variant); } } //----------------------------------------------------------------------------- // Remove the specified IEnumWbemClassObject pointer from the cache. //----------------------------------------------------------------------------- void CWMILiveHelper::Version5RemoveEnumObject(const CString & strClass) { CMSIEnumerator * pEnumObject = NULL; if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pEnumObject) && pEnumObject) delete pEnumObject; m_mapClassToEnumInterface.RemoveKey(strClass); } //----------------------------------------------------------------------------- // Clear out the contents of the caches (forcing the interfaces to be // retrieved again). //----------------------------------------------------------------------------- void CWMILiveHelper::Version5ClearCache() { CMSIObject * pObject = NULL; CMSIEnumerator * pEnumObject = NULL; POSITION pos; CString strClass; for (pos = m_mapClassToInterface.GetStartPosition(); pos != NULL;) { m_mapClassToInterface.GetNextAssoc(pos, strClass, (void * &) pObject); if (pObject) delete pObject; } m_mapClassToInterface.RemoveAll(); for (pos = m_mapClassToEnumInterface.GetStartPosition(); pos != NULL;) { m_mapClassToEnumInterface.GetNextAssoc(pos, strClass, (void * &) pEnumObject); if (pEnumObject) delete pEnumObject; } m_mapClassToEnumInterface.RemoveAll(); } //----------------------------------------------------------------------------- // This function is used to retrieve a pointer to IWbemServices for a // particular namespace. The default namespace is cimv2. //----------------------------------------------------------------------------- IWbemServices * CWMILiveHelper::Version5GetWBEMService(CString * pstrNamespace) { if (pstrNamespace == NULL || pstrNamespace->IsEmpty()) return m_pServices; // Something like the following is useful for forcing a provider error when // testing the error containment: // // if (*pstrNamespace == _T("MSAPPS")) *pstrNamespace += _T("X"); IWbemServices * pServices; // In 5.0 we kept a map, but we probably won't do it here... // // if (m_mapNamespaceToService.Lookup(*pstrNamespace, (void * &) pServices) && pServices) // return pServices; // There is no WBEM services pointer for that namespace, we need to create one. CString strNamespace(_T("")); if (m_strMachine.IsEmpty()) strNamespace = CString(_T("\\\\.\\root\\")) + *pstrNamespace; else { if (m_strMachine.Right(1) == CString(_T("\\"))) strNamespace = m_strMachine + CString(_T("root\\")) + *pstrNamespace; else strNamespace = m_strMachine + CString(_T("\\root\\")) + *pstrNamespace; if (strNamespace.Left(2).Compare(CString(_T("\\\\"))) != 0) strNamespace = CString(_T("\\\\")) + strNamespace; } IWbemLocator * pIWbemLocator = NULL; if (CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pIWbemLocator) == S_OK) { BSTR pNamespace = strNamespace.AllocSysString(); HRESULT hrServer = pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pServices); if (pNamespace) SysFreeString(pNamespace); if (pIWbemLocator) { pIWbemLocator->Release(); pIWbemLocator = NULL; } if (SUCCEEDED(hrServer) && pServices) { ChangeWBEMSecurity(pServices); // In 5.0 we kept a map, but we probably won't do it here... // // m_mapNamespaceToService.SetAt(*pstrNamespace, (void *) pServices); if (m_pIWbemServices) m_pIWbemServices->Release(); m_pIWbemServices = pServices; m_pIWbemServices->AddRef(); return pServices; } m_hrLastVersion5Error = hrServer; } return NULL; } //----------------------------------------------------------------------------- // The CMSIEnumerator class encapsulates the WBEM enumerator interface, or // implements our own form of enumerator (such as for the LNK command in the // template file). // // Nothing particularly interesting about the constructor or destructor. //----------------------------------------------------------------------------- CMSIEnumerator::CMSIEnumerator() { m_enumtype = CMSIEnumerator::CLASS; m_fOnlyDups = FALSE; m_fGotDuplicate = FALSE; m_fMinOfOne = FALSE; m_iMinOfOneCount = 0; m_pEnum = NULL; m_pWMI = NULL; m_pConstraints = NULL; m_pSavedDup = NULL; m_pstrList = NULL; m_hresCreation = S_OK; } CMSIEnumerator::~CMSIEnumerator() { if (m_pEnum) { switch (m_enumtype) { case CMSIEnumerator::WQL: break; case CMSIEnumerator::LNK: m_pWMI->m_enumMap.SetEnumerator(m_strAssocClass, m_pEnum); break; case CMSIEnumerator::INTERNAL: if (m_pstrList) { delete m_pstrList; m_pstrList = NULL; } break; case CMSIEnumerator::CLASS: default: m_pWMI->m_enumMap.SetEnumerator(m_strClass, m_pEnum); break; } m_pEnum->Release(); m_pEnum = NULL; } } //----------------------------------------------------------------------------- // Creating the CMSIEnumerator object involves determining what sort of // enumerator is required. We support the following types: // // 1. Straight enumeration of a class // 2. Enumerate class, with applied constraints // 3. Enumerate the results of a WQL statement. // 4. Interprete a LNK command to enumerate associated classes. // 5. Do internal processing on an INTERNAL type. //----------------------------------------------------------------------------- HRESULT CMSIEnumerator::Create(const CString & strClass, const GATH_FIELD * pConstraints, CWMIHelper * pWMI) { if (strClass.IsEmpty() || !pWMI) return E_INVALIDARG; // Create may be called multiple times (to reset the enumerator). So we may need to // release the enumerator pointer. if (m_pEnum) { m_pEnum->Release(); m_pEnum = NULL; } // Divide the specified class into class and namespace parts, get the WBEM service. CString strNamespacePart(_T("")), strClassPart(strClass); int i = strClass.Find(_T(":")); if (i != -1) { strNamespacePart = strClass.Left(i); strClassPart = strClass.Right(strClass.GetLength() - i - 1); } IWbemServices * pServices = pWMI->Version5GetWBEMService(&strNamespacePart); if (pServices == NULL) return NULL; // First, we need to determine what type of enumerator this is. Scan through // the constraints - if we see one which has a string starting with "WQL:" or // "LNK:", then we know what type this enumerator is. CString strStatement; const GATH_FIELD * pScanConstraint = pConstraints; while (pScanConstraint) { if (pScanConstraint->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0) { if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("WQL:"))) == 0) m_enumtype = CMSIEnumerator::WQL; else if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("LNK:"))) == 0) m_enumtype = CMSIEnumerator::LNK; else if (pScanConstraint->m_strFormat.Left(4).CompareNoCase(CString(_T("INT:"))) == 0) m_enumtype = CMSIEnumerator::INTERNAL; if (m_enumtype != CMSIEnumerator::CLASS) { strStatement = pScanConstraint->m_strFormat; strStatement = strStatement.Right(strStatement.GetLength() - 4); break; } } pScanConstraint = pScanConstraint->m_pNext; } // If this is a WQL or a LNK enumerator, processes the statement by replacing // [class.property] with the actual value from WBEM. If we find the string // "[min-of-one]", make a note of it for later. if (m_enumtype == CMSIEnumerator::WQL) ProcessEnumString(strStatement, m_fMinOfOne, m_fOnlyDups, pWMI, m_strNoInstanceLabel, TRUE); else if (m_enumtype == CMSIEnumerator::LNK) if (SUCCEEDED(ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass))) { // Save the object path for later - so we can change the object without // completely reprocessing the statement. Then replace the keywords in // the statement and break out the pieces again. m_strLNKObject = m_strObjPath; ProcessEnumString(strStatement, m_fMinOfOne, m_fOnlyDups, pWMI, m_strNoInstanceLabel); ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass); } // Now, based on the enumerator type, create the WBEM enumerator object. switch (m_enumtype) { case CMSIEnumerator::WQL: { BSTR language = SysAllocString(L"WQL"); BSTR query = strStatement.AllocSysString(); m_hresCreation = pServices->ExecQuery(language, query, WBEM_FLAG_RETURN_IMMEDIATELY, 0, &m_pEnum); SysFreeString(query); SysFreeString(language); } break; case CMSIEnumerator::LNK: { m_hresCreation = ParseLNKCommand(strStatement, m_strObjPath, m_strAssocClass, m_strResultClass); if (SUCCEEDED(m_hresCreation)) { BSTR className = m_strAssocClass.AllocSysString(); m_pEnum = pWMI->m_enumMap.GetEnumerator(m_strAssocClass); if (m_pEnum) m_hresCreation = S_OK; else m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum); SysFreeString(className); } } break; case CMSIEnumerator::INTERNAL: // We'll call a function here so we can do whatever processing is required // to create this internal enumeration. m_hresCreation = CreateInternalEnum(strStatement, pWMI); break; case CMSIEnumerator::CLASS: default: { BSTR className = strClassPart.AllocSysString(); m_pEnum = pWMI->m_enumMap.GetEnumerator(strClassPart); if (m_pEnum) m_hresCreation = S_OK; else m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum); SysFreeString(className); } } // Set some of the other member variables. m_strClass = strClass; m_pWMI = pWMI; m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0; m_pConstraints = pConstraints; if (m_pEnum) ChangeWBEMSecurity(m_pEnum); // Based on the HRESULT from creating the enumeration, determine what to return. // For certain errors, we want to act like the creation succeeded, then supply // objects which return the error text. if (FAILED(m_hresCreation)) { m_fMinOfOne = TRUE; m_iMinOfOneCount = 1; } pServices->Release(); return S_OK; } //----------------------------------------------------------------------------- // This function is used to create internal enumeration types (enumerations // which are beyond the template file syntax). Basically a bunch of special // cases. //----------------------------------------------------------------------------- HRESULT CMSIEnumerator::CreateInternalEnum(const CString & strInternal, CWMIHelper * pWMI) { if (strInternal.CompareNoCase(CString(_T("dlls"))) == 0) { // We want to enumerate all the loaded dlls and exes on the system. // This can be done by enumerating the CIM_ProcessExecutable class // and removing duplicate file names. We'll keep the filenames (with // path information) in a string list. if (m_pstrList == NULL) { m_pstrList = new CStringList; if (m_pstrList == NULL) return E_FAIL; } else m_pstrList->RemoveAll(); HRESULT hr = S_OK; IWbemServices * pServices = pWMI->Version5GetWBEMService(); if (pServices) { BSTR className = SysAllocString(L"CIM_ProcessExecutable"); IEnumWbemClassObject * pEnum = NULL; hr = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnum); if (SUCCEEDED(hr)) { IWbemClassObject * pWBEMObject = NULL; ULONG uReturned; VARIANT variant; BSTR propName = SysAllocString(L"Antecedent"); VariantInit(&variant); do { uReturned = 0; hr = pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned); if (SUCCEEDED(hr) && pWBEMObject && uReturned) { // For each instance of CIM_ProcessExecutable, get the // Antecedent property (which contains the file path). // If it is unique, save it in the list. VariantClear(&variant); if (pWBEMObject->Get(propName, 0L, &variant, NULL, NULL) == S_OK) { if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { CString strResult = V_BSTR(&variant); strResult.MakeLower(); if (m_pstrList->Find(strResult) == NULL) m_pstrList->AddHead(strResult); } } } } while (SUCCEEDED(hr) && pWBEMObject && uReturned); ::SysFreeString(propName); pEnum->Release(); } ::SysFreeString(className); pServices->Release(); } return hr; } return S_OK; } //----------------------------------------------------------------------------- // Help function for ProcessEnumString, used to convert single backslashes // into double backslashes (required for WQL statements). //----------------------------------------------------------------------------- void MakeDoubleBackslashes(CString & strValue) { CString strTemp(strValue); CString strResults; while (!strTemp.IsEmpty()) { if (strTemp[0] != _T('\\')) { int index = strTemp.Find(_T('\\')); if (index < 0) index = strTemp.GetLength(); strResults += strTemp.Left(index); strTemp = strTemp.Right(strTemp.GetLength() - index); } else { strResults += CString("\\\\"); strTemp = strTemp.Mid(1); } } strValue = strResults; } //----------------------------------------------------------------------------- // This function replaces [class.property] with the actual value of the // property, and strings out [min-of-one], indicating if it was present in // the fMinOfOne parameter. //----------------------------------------------------------------------------- void CMSIEnumerator::ProcessEnumString(CString & strStatement, BOOL & fMinOfOne, BOOL & fOnlyDups, CWMIHelper * pWMI, CString & strNoInstanceLabel, BOOL fMakeDoubleBackslashes) { CString strMinOfOne(_T("min-of-one")); CString strOnlyDups(_T("more-than-one")); CString strResults; fMinOfOne = FALSE; fOnlyDups = FALSE; while (!strStatement.IsEmpty()) { if (strStatement[0] != _T('[')) { int index = strStatement.Find(_T('[')); if (index < 0) index = strStatement.GetLength(); strResults += strStatement.Left(index); strStatement = strStatement.Right(strStatement.GetLength() - index); } else { CString strKeyword; strStatement = strStatement.Right(strStatement.GetLength() - 1); int index = strStatement.Find(_T(']')); if (index < 0) break; strKeyword = strStatement.Left(index); if (strKeyword.Left(strMinOfOne.GetLength()).CompareNoCase(strMinOfOne) == 0) { fMinOfOne = TRUE; int iEqualsIndex = strKeyword.Find(_T('=')); if (iEqualsIndex > 0) strNoInstanceLabel = strKeyword.Right(strKeyword.GetLength() - iEqualsIndex - 1); } else if (strKeyword.Left(strOnlyDups.GetLength()).CompareNoCase(strOnlyDups) == 0) { fOnlyDups = TRUE; int iEqualsIndex = strKeyword.Find(_T('=')); if (iEqualsIndex > 0) strNoInstanceLabel = strKeyword.Right(strKeyword.GetLength() - iEqualsIndex - 1); } else if (!strKeyword.IsEmpty()) { int iDotIndex = strKeyword.Find(_T('.')); if (iDotIndex >= 0) { CString strValue; if (pWMI->Version5QueryValue(strKeyword.Left(iDotIndex), strKeyword.Right(strKeyword.GetLength() - iDotIndex - 1), strValue)) { if (fMakeDoubleBackslashes) MakeDoubleBackslashes(strValue); strResults += strValue; } } } strStatement = strStatement.Right(strStatement.GetLength() - (index + 1)); } } strStatement = strResults; } //----------------------------------------------------------------------------- // Parse the component classes from the LNK command. //----------------------------------------------------------------------------- HRESULT CMSIEnumerator::ParseLNKCommand(const CString & strStatement, CString & strObjPath, CString & strAssocClass, CString & strResultClass) { // We need to parse out the LNK statement into two or three components, // from the form "objPath->assocClass[->resultClass]", with the // brackets indicating that the resultClass is optional. CString strWorking(strStatement); int iArrowIndex = strWorking.Find(_T("->")); if (iArrowIndex == -1) return E_INVALIDARG; strObjPath = strWorking.Left(iArrowIndex); strWorking = strWorking.Right(strWorking.GetLength() - (iArrowIndex + 2)); iArrowIndex = strWorking.Find(_T("->")); if (iArrowIndex == -1) strAssocClass = strWorking; else { strAssocClass = strWorking.Left(iArrowIndex); strWorking = strWorking.Right(strWorking.GetLength() - (iArrowIndex + 2)); strResultClass = strWorking; strResultClass.MakeLower(); } strAssocClass.TrimRight(); strAssocClass.TrimLeft(); strObjPath.TrimRight(); strObjPath.TrimLeft(); strResultClass.TrimRight(); strResultClass.TrimLeft(); return S_OK; } //----------------------------------------------------------------------------- // The Next method will advance the enumerator based on the type of this // enumerator. //----------------------------------------------------------------------------- HRESULT CMSIEnumerator::Next(CMSIObject ** ppObject) { if (!ppObject) return E_INVALIDARG; *ppObject = NULL; // If there was an error creating the enumeration, return the error code. if (FAILED(m_hresCreation)) return m_hresCreation; if (m_pEnum == NULL && m_enumtype != CMSIEnumerator::INTERNAL) return E_UNEXPECTED; HRESULT hRes = S_OK; IWbemClassObject * pWBEMObject = NULL; switch (m_enumtype) { case CMSIEnumerator::LNK: { // Scan through the enumerated associate class. Look for one which // satisfies our requirements. CString strTemp, strAssociatedObject(_T("")); ULONG uReturned; IWbemClassObject * pAssocObj; do { pAssocObj = NULL; uReturned = 0; hRes = m_pEnum->Next(TIMEOUT, 1, &pAssocObj, &uReturned); if (!pAssocObj || FAILED(hRes) || uReturned != 1) { // Even if we didn't succeed in getting a new object, // we might have a saved one if we're only showing // "more-than-one" objects. if (m_fOnlyDups && m_pSavedDup && m_fGotDuplicate) { // We have found one previously, so return it. // Make it look like the Next call was successful. m_pSavedDup = NULL; hRes = S_OK; uReturned = 1; strAssociatedObject = m_strSavedDup; break; } else { if (m_pSavedDup) { // We only got one object instance, so get rid of it. m_pSavedDup->Release(); m_pSavedDup = NULL; } break; } } if (AssocObjectOK(pAssocObj, strTemp)) { // This object passed the filter - but if we're showing // only "more-than-one" objects, save this one and return // the saved one. if (m_fOnlyDups) { if (m_pSavedDup) { // We have found one previously, so return it and // save the current. IWbemClassObject * pSwap = pAssocObj; CString strSwap = strTemp; pAssocObj = m_pSavedDup; m_pSavedDup = pSwap; strTemp = m_strSavedDup; m_strSavedDup = strSwap; m_fGotDuplicate = TRUE; } else { // This is the first one we've found - don't // return it until we find another. m_pSavedDup = pAssocObj; m_strSavedDup = strTemp; m_fGotDuplicate = FALSE; continue; } } strAssociatedObject = strTemp; pAssocObj->Release(); break; } pAssocObj->Release(); } while (pAssocObj); // If there is an associated object path, get the object. if (!strAssociatedObject.IsEmpty()) { BSTR path = strAssociatedObject.AllocSysString(); if (m_pWMI->m_pIWbemServices != NULL) hRes = m_pWMI->m_pIWbemServices->GetObject(path, 0, NULL, &pWBEMObject, NULL); else hRes = E_FAIL; SysFreeString(path); } } break; case CMSIEnumerator::WQL: { ULONG uReturned; hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned); } break; case CMSIEnumerator::INTERNAL: hRes = InternalNext(&pWBEMObject); break; case CMSIEnumerator::CLASS: default: { ULONG uReturned; // EvaluateFilter and IsDependencyJoin handle a NULL pConstraints parameter, // but for efficiency we're going to have a distinct branch for a non-NULL // value (since it will usually be NULL). if (m_pConstraints) { // Keep enumerating the instances of this class until we've // found one which satisfies all of the filters. do { pWBEMObject = NULL; hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned); if (!pWBEMObject || hRes != S_OK || uReturned != 1) break; else if (m_pWMI->Version5EvaluateFilter(pWBEMObject, m_pConstraints)) break; pWBEMObject->Release(); } while (pWBEMObject); // If this class is being enumerated as a dependency class, then // locate all the objects it references. If it isn't, we still // need to check for any joins to other classes formed by the constraints. if (pWBEMObject) if (m_pWMI->Version5IsDependencyJoin(m_pConstraints)) m_pWMI->Version5EvaluateDependencyJoin(pWBEMObject); else m_pWMI->Version5EvaluateJoin(m_strClass, pWBEMObject, m_pConstraints); } else hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned); } break; } if (pWBEMObject == NULL) { // There was no object to get. We'll still create a CMSIObject, but // we'll set its state to indicate either that there are no instances, // or one instance with an error message. if (SUCCEEDED(hRes) && (m_iMinOfOneCount == 0)) *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_NO_INSTANCES); else *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_MSG_INSTANCE); } else *ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pWMI, MOS_INSTANCE); if (m_iMinOfOneCount) m_iMinOfOneCount--; return S_OK; } //----------------------------------------------------------------------------- // InternalNext is used to return a WBEM object for an internal enumeration // (one that requires processing beyond the template file). Basically a // set of special cases. //----------------------------------------------------------------------------- HRESULT CMSIEnumerator::InternalNext(IWbemClassObject ** ppWBEMObject) { if (m_pstrList && !m_pstrList->IsEmpty()) { CString strNextObject = m_pstrList->RemoveHead(); if (!strNextObject.IsEmpty()) { IWbemServices * pServices = m_pWMI->Version5GetWBEMService(); if (pServices) { BSTR objectpath = strNextObject.AllocSysString(); HRESULT hr = S_OK; if (FAILED(pServices->GetObject(objectpath, 0, NULL, ppWBEMObject, NULL))) hr = E_FAIL; ::SysFreeString(objectpath); pServices->Release(); return hr; } } } return S_OK; } //----------------------------------------------------------------------------- // Reset should just reset the enumerator pointer. //----------------------------------------------------------------------------- HRESULT CMSIEnumerator::Reset(const GATH_FIELD * pConstraints) { HRESULT hRes = S_OK; if (m_pEnum) { switch (m_enumtype) { case CMSIEnumerator::WQL: hRes = Create(m_strClass, pConstraints, m_pWMI); break; case CMSIEnumerator::LNK: { BOOL fDummy, fDummy2; CString strDummy; m_strObjPath = m_strLNKObject; ProcessEnumString(m_strObjPath, fDummy, fDummy2, m_pWMI, strDummy); m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0; hRes = m_pEnum->Reset(); } break; case CMSIEnumerator::INTERNAL: hRes = Create(m_strClass, pConstraints, m_pWMI); break; case CMSIEnumerator::CLASS: default: m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0; hRes = m_pEnum->Reset(); break; } } else hRes = E_UNEXPECTED; return hRes; } //----------------------------------------------------------------------------- // Evaluate if the pObject parameter is valid for this LNK enumerator. In // particular, we must find the m_strObjPath in one of its properties, and // possibly finding another property containing the m_strResultClass string. //----------------------------------------------------------------------------- BOOL CMSIEnumerator::AssocObjectOK(IWbemClassObject * pObject, CString & strAssociatedObject) { strAssociatedObject.Empty(); ASSERT(pObject); if (pObject == NULL) return FALSE; VARIANT variant; CString strReturn(_T("")), strValue; // Traverse the set of non-system properties. Look for one the is the same // as the object path. pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY); VariantInit(&variant); while (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR) { if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) strValue = V_BSTR(&variant); VariantClear(&variant); if (strValue.CompareNoCase(m_strObjPath) == 0) break; } pObject->EndEnumeration(); // If we found a property containing the object path, look through for other // paths which might be to objects we're insterested in. if (strValue.CompareNoCase(m_strObjPath) == 0) { pObject->BeginEnumeration(WBEM_FLAG_REFS_ONLY | WBEM_FLAG_NONSYSTEM_ONLY); while (strReturn.IsEmpty() && (pObject->Next(0, NULL, &variant, NULL, NULL) == WBEM_S_NO_ERROR)) { if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) strValue = V_BSTR(&variant); if (strValue.CompareNoCase(m_strObjPath) != 0) { if (m_strResultClass.IsEmpty()) strReturn = strValue; else { CString strSearch(strValue); strSearch.MakeLower(); if (strSearch.Find(m_strResultClass) != -1) strReturn = strValue; } } VariantClear(&variant); } pObject->EndEnumeration(); } if (!strReturn.IsEmpty()) { strAssociatedObject = strReturn; return TRUE; } return FALSE; } //----------------------------------------------------------------------------- // Implement the CMSIObject class. This is just a thin wrapper for the // IWbemClassObject interface. //----------------------------------------------------------------------------- CMSIObject::CMSIObject(IWbemClassObject * pObject, const CString & strLabel, HRESULT hres, CWMIHelper * pWMI, MSIObjectState objState) { m_pObject = pObject; m_strLabel = strLabel; m_hresCreation = hres; m_pWMI = pWMI; m_objState = objState; } CMSIObject::~CMSIObject() { if (m_pObject) { m_pObject->Release(); m_pObject = NULL; } } HRESULT CMSIObject::Get(BSTR property, LONG lFlags, VARIANT *pVal, VARTYPE *pvtType, LONG *plFlavor) { ASSERT(m_objState != MOS_NO_INSTANCES); // If there is an object interface, just pass the request on through. if (m_pObject) return m_pObject->Get(property, lFlags, pVal, NULL /* pvtType */, plFlavor); // Otherwise, we need to return the appropriate string. CString strReturn; GetErrorLabel(strReturn); V_BSTR(pVal) = strReturn.AllocSysString(); pVal->vt = VT_BSTR; return S_OK; } MSIObjectState CMSIObject::IsValid() { return m_objState; } HRESULT CMSIObject::GetErrorLabel(CString & strError) { switch (m_hresCreation) { case WBEM_E_ACCESS_DENIED: strError = m_pWMI->m_strBadProperty; // shouldn't be showing errors this way in 6.0 break; case WBEM_E_TRANSPORT_FAILURE: strError = m_pWMI->m_strBadProperty; break; case S_OK: case WBEM_S_FALSE: default: // This object was created from an enumeration that was marked as "min-of-one", // meaning that at least one object, even if it's not valid, needed to be // returned from the enumeration. Return the string we saved at object creation. if (!m_strLabel.IsEmpty()) strError = m_strLabel; else strError = m_pWMI->m_strBadProperty; break; } return S_OK; } //----------------------------------------------------------------------------- // Look up strVal in the ValueMap (if it exists) for strClass.strProperty // If the value or the ValueMap is not found, return E_Something. // // Useful code snippet - this will dump the contents of the cache of // saved values. To find all value mapped properties, but this code // someplace where it will execute when MSInfo exits, change QueryValue // to call CheckValueMap for all properties, then run MSInfo and do a global // refresh (like to save an NFO). // // msiLog.WriteLog(CMSInfoLog::BASIC, _T("BEGIN Dump of ValueMap Cache\r\n")); // CString key, val, log; // for (POSITION pos = g_mapValueMap.GetStartPosition(); pos != NULL;) // { // g_mapValueMap.GetNextAssoc(pos, key, val); // log.Format(_T(" %s = %s\r\n", key, val); // msiLog.WriteLog(CMSInfoLog::BASIC, log); // } // msiLog.WriteLog(CMSInfoLog::BASIC, _T("END Dump of ValueMap Cache\r\n")); //----------------------------------------------------------------------------- CMapStringToString g_mapValueMap; HRESULT CWMILiveHelper::Version5CheckValueMap(const CString& strClass, const CString& strProperty, const CString& strVal, CString &strResult) { IWbemClassObject * pWBEMClassObject = NULL; HRESULT hrMap = S_OK, hr = S_OK; VARIANT vArray, vMapArray; IWbemQualifierSet * qual = NULL; // Check the cache of saved values. CString strLookup = strClass + CString(_T(".")) + strProperty + CString(_T(":")) + strVal; if (g_mapValueMap.Lookup(strLookup, strResult)) return S_OK; // Get the class object (not instance) for this class. IWbemServices * pServices = Version5GetWBEMService(); if (!pServices) return E_FAIL; CString strFullClass(_T("\\\\.\\root\\cimv2:")); strFullClass += strClass; BSTR bstrObjectPath = strFullClass.AllocSysString(); hr = pServices->GetObject(bstrObjectPath, WBEM_FLAG_USE_AMENDED_QUALIFIERS, NULL, &pWBEMClassObject, NULL); ::SysFreeString(bstrObjectPath); pServices->Release(); if (FAILED(hr)) return hr; // Get the qualifiers from the class object. BSTR bstrProperty = strProperty.AllocSysString(); hr = pWBEMClassObject->GetPropertyQualifierSet(bstrProperty, &qual); ::SysFreeString(bstrProperty); if (SUCCEEDED(hr) && qual) { // Get the ValueMap and Value arrays. hrMap = qual->Get(L"ValueMap", 0, &vMapArray, NULL); hr = qual->Get(L"Values", 0, &vArray, NULL); if (SUCCEEDED(hr) && vArray.vt == (VT_BSTR | VT_ARRAY)) { // Get the property value we're mapping. long index; if (SUCCEEDED(hrMap)) { SAFEARRAY * pma = V_ARRAY(&vMapArray); long lLowerBound = 0, lUpperBound = 0 ; SafeArrayGetLBound(pma, 1, &lLowerBound); SafeArrayGetUBound(pma, 1, &lUpperBound); BSTR vMap; for (long x = lLowerBound; x <= lUpperBound; x++) { SafeArrayGetElement(pma, &x, &vMap); if (0 == strVal.CompareNoCase((LPCTSTR)vMap)) { index = x; break; // found it } } } else { // Shouldn't hit this case - if mof is well formed // means there is no value map where we are expecting one. // If the strVal we are looking for is a number, treat it // as an index for the Values array. If it's a string, // then this is an error. TCHAR * szTest = NULL; index = _tcstol((LPCTSTR)strVal, &szTest, 10); if (szTest == NULL || (index == 0 && *szTest != 0) || strVal.IsEmpty()) hr = E_FAIL; } // Lookup the string. if (SUCCEEDED(hr)) { SAFEARRAY * psa = V_ARRAY(&vArray); long ix[1] = {index}; BSTR str2; hr = SafeArrayGetElement(psa, ix, &str2); if (SUCCEEDED(hr)) { strResult = str2; SysFreeString(str2); hr = S_OK; } else { hr = WBEM_E_VALUE_OUT_OF_RANGE; } } } qual->Release(); } if (SUCCEEDED(hr)) g_mapValueMap.SetAt(strLookup, strResult); return hr; } //----------------------------------------------------------------------------- // The CEnumMap is a utility class to cache IEnumWbemClassObject pointers. // There will be one instance of this class used to improve performance // by avoiding the high overhead associated with creating enumerators for // certain classes. //----------------------------------------------------------------------------- IEnumWbemClassObject * CEnumMap::GetEnumerator(const CString & strClass) { IEnumWbemClassObject * pEnum = NULL; IEnumWbemClassObject * pNewEnum = NULL; if (m_mapEnum.Lookup(strClass, (void * &) pEnum)) { if (pEnum && SUCCEEDED(pEnum->Clone(&pNewEnum)) && pNewEnum) pNewEnum->Reset(); else pNewEnum = NULL; } return pNewEnum; } void CEnumMap::SetEnumerator(const CString & strClass, IEnumWbemClassObject * pEnum) { if (pEnum) { IEnumWbemClassObject * pEnumExisting = NULL; if (m_mapEnum.Lookup(strClass, (void * &) pEnumExisting)) { ; //WRITE(_T("SetEnumerator for %s, already exists, ignoring.\r\n"), strClass); } else { pEnum->AddRef(); m_mapEnum.SetAt(strClass, pEnum); } } } void CEnumMap::Reset() { IEnumWbemClassObject * pEnum = NULL; CString key; for (POSITION pos = m_mapEnum.GetStartPosition(); pos != NULL;) { m_mapEnum.GetNextAssoc(pos, key, (void * &) pEnum); if (pEnum) pEnum->Release(); } m_mapEnum.RemoveAll(); }