windows-nt/Source/XPSP1/NT/enduser/troubleshoot/msinfo/refresh.cpp

622 lines
17 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//=============================================================================
// File: refresh.cpp
// Author: a-jammar
// Covers: CRefreshFunctions
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
// This file contains functions useful for refreshing the internal data
// categories. The CRefreshFunctions class just has static functions, and
// is used to group the functions together.
//=============================================================================
#include "stdafx.h"
#include "gather.h"
#include "gathint.h"
#include "resrc1.h"
//-----------------------------------------------------------------------------
// This method takes the information in a GATH_FIELD struct and uses it to
// generate a current GATH_VALUE struct.
//-----------------------------------------------------------------------------
BOOL CRefreshFunctions::RefreshValue(CDataGatherer * pGatherer, 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(pGatherer, 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(CDataGatherer *pGatherer, 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)
{
CDataProvider * pProvider = pGatherer->GetProvider();
if (!pProvider)
return FALSE;
// 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 (pProvider->QueryValueDWORD(pField->m_strSource, pArg->m_strText, dwResult, strTemp))
{
strTemp = (dwResult) ? pProvider->m_strTrue : pProvider->m_strFalse;
strResult.Format(szFormatFragment, strTemp);
return TRUE;
}
else
{
strResult = strTemp;
return FALSE;
}
break;
case 'd': case 'D':
// This is the numeric type.
if (pProvider->QueryValueDWORD(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 (pProvider->QueryValueDoubleFloat(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 (pProvider->QueryValueDateTime(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 (pProvider->QueryValue(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 (pProvider->QueryValue(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 (pProvider->QueryValue(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 (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 (pProvider->QueryValue(pField->m_strSource, CString(_T("Version")), strTemp))
{
strResult = strTemp;
return TRUE;
}
else
{
if (pProvider->QueryValue(pField->m_strSource, CString(_T("Name")), strTemp))
strResult = strTemp;
if (pProvider->QueryValueDateTime(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 (pProvider->QueryValueDoubleFloat(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 (pProvider->QueryValueDoubleFloat(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 (pProvider->QueryValue(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;
}
//-----------------------------------------------------------------------------
// Refresh the list of columns based on the list of column fields. We'll also
// need to set the number of columns.
//-----------------------------------------------------------------------------
BOOL CRefreshFunctions::RefreshColumns(CDataGatherer * pGatherer, INTERNAL_CATEGORY * pInternal)
{
ASSERT(pInternal);
GATH_FIELD * pField = pInternal->m_pColSpec;
// Count the number of columns.
pInternal->m_dwColCount = 0;
while (pField)
{
pInternal->m_dwColCount++;
pField = pField->m_pNext;
}
// Allocate the array of column values.
if (pInternal->m_aCols)
delete [] pInternal->m_aCols;
if (pInternal->m_dwColCount == 0)
return TRUE;
pInternal->m_aCols = new GATH_VALUE[pInternal->m_dwColCount];
if (pInternal->m_aCols == NULL)
return FALSE;
// Finally get each column name based on the column field.
pField = pInternal->m_pColSpec;
for (DWORD dwIndex = 0; dwIndex < pInternal->m_dwColCount; dwIndex++)
{
if (!RefreshValue(pGatherer, &pInternal->m_aCols[dwIndex], pField))
return FALSE;
pField = pField->m_pNext;
}
return TRUE;
}
//-----------------------------------------------------------------------------
// 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(CDataGatherer * pGatherer, 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(pGatherer, 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.
CDataProvider * pProvider = pGatherer->GetProvider();
if (pProvider && pProvider->ResetClass(pCurrentLineSpec->m_strEnumerateClass, pCurrentLineSpec->m_pConstraintFields))
do
{
if (!RefreshLines(pGatherer, pCurrentLineSpec->m_pEnumeratedGroup, dwColumns, listLinePtrs))
break;
} while (pProvider->EnumClass(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(CDataGatherer * pGatherer, 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(pGatherer, &pLine->m_aValue[dwIndex], pField))
return FALSE;
pField = pField->m_pNext;
}
return TRUE;
}