WindowsXP-SP1/admin/pchealth/sysinfo/control/wmihelper.cpp
2020-09-30 16:53:49 +02:00

606 lines
17 KiB
C++

//=============================================================================
// Contains the functions for the base WMI helper class.
//=============================================================================
#include "stdafx.h"
#include "category.h"
#include "wmiabstraction.h"
#include "resource.h"
#include "dataset.h"
//-----------------------------------------------------------------------------
// Loads the string identified by uiResourceID, and parses it into the columns
// in aColValues. The string should be of the form "www|xxx|yyy|zzz" - this
// will be parsed into two rows: www,xxx and yyy,zzz. Values will be inserted
// into the aColValues array of pointer lists of CMSIValue structs.
//-----------------------------------------------------------------------------
void CWMIHelper::LoadColumnsFromResource(UINT uiResourceID, CPtrList * aColValues, int iColCount)
{
AfxSetResourceHandle(_Module.GetResourceInstance());
CString strResource;
if (strResource.LoadString(uiResourceID))
{
CMSIValue * pValue;
int iCol = 0;
while (!strResource.IsEmpty())
{
pValue = new CMSIValue(strResource.SpanExcluding(_T("|\n")), 0);
if (pValue)
{
ASSERT(!pValue->m_strValue.IsEmpty());
strResource = strResource.Right(strResource.GetLength() - pValue->m_strValue.GetLength() - 1);
aColValues[iCol].AddTail((void *) pValue);
iCol += 1;
if (iCol == iColCount)
iCol = 0;
}
else
strResource.Empty();
}
}
}
//-----------------------------------------------------------------------------
// Same as the previous, but uses a string instead of a resource ID.
//-----------------------------------------------------------------------------
void CWMIHelper::LoadColumnsFromString(LPCTSTR szColumns, CPtrList * aColValues, int iColCount)
{
if (szColumns != NULL)
{
CString strColumns(szColumns);
CMSIValue * pValue;
int iCol = 0;
while (!strColumns.IsEmpty())
{
pValue = new CMSIValue(strColumns.SpanExcluding(_T("|\n")), 0);
if (pValue)
{
ASSERT(!pValue->m_strValue.IsEmpty());
strColumns = strColumns.Right(strColumns.GetLength() - pValue->m_strValue.GetLength() - 1);
aColValues[iCol].AddTail((void *) pValue);
iCol += 1;
if (iCol == iColCount)
iCol = 0;
}
else
strColumns.Empty();
}
}
}
//-----------------------------------------------------------------------------
// Return the first object of the specified class.
//-----------------------------------------------------------------------------
CWMIObject * CWMIHelper::GetSingleObject(LPCTSTR szClass, LPCTSTR szProperties)
{
ASSERT(szClass);
CWMIObjectCollection * pCollection = NULL;
CWMIObject * pObject = NULL;
if (SUCCEEDED(Enumerate(szClass, &pCollection, szProperties)))
{
if (FAILED(pCollection->GetNext(&pObject)))
pObject = NULL;
delete pCollection;
}
return pObject;
}
//-----------------------------------------------------------------------------
// Delimit the specified number.
//-----------------------------------------------------------------------------
CString DelimitNumber(double dblValue, int iDecimalDigits = 0)
{
NUMBERFMT fmt;
TCHAR szResult[MAX_PATH] = _T("");
TCHAR szDelimiter[4] = _T(",");
TCHAR szDecimal[4] = _T(".");
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szDelimiter, 4);
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimal, 4);
memset(&fmt, 0, sizeof(NUMBERFMT));
fmt.Grouping = 3;
fmt.lpDecimalSep = (iDecimalDigits) ? szDecimal : _T("");
fmt.NumDigits = iDecimalDigits;
fmt.lpThousandSep = szDelimiter;
CString strValue;
CString strFormatString;
strFormatString.Format(_T("%%.%df"), iDecimalDigits);
strValue.Format(strFormatString, dblValue);
// GetNumberFormat requires the decimal to be a '.', while CString::Format
// uses the locale value. So we need to go back and replace it.
StringReplace(strValue, szDecimal, _T("."));
GetNumberFormat(LOCALE_USER_DEFAULT, 0, strValue, &fmt, szResult, MAX_PATH);
return CString(szResult);
}
//-----------------------------------------------------------------------------
// Return the requested value from the object, as a string and/or a DWORD.
// Use the chFormat flag to determine how to format the results.
//
// The return result is the actual format character to use for displaying the
// results in a string.
//
// TBD - do something better with the HRESULTs returned.
//-----------------------------------------------------------------------------
CString gstrYes; // global string "yes" (will be localized)
CString gstrNo; // global string "no" (will be localized)
CString gstrBytes; // global string "bytes" (will be localized)
CString gstrKB; // global string "KB" (will be localized)
CString gstrMB; // global string "MB" (will be localized)
CString gstrGB; // global string "GB" (will be localized)
CString gstrTB; // global string "TB" (will be localized)
HRESULT CWMIObject::GetInterpretedValue(LPCTSTR szProperty, LPCTSTR szFormat, TCHAR chFormat, CString * pstrValue, DWORD * pdwValue)
{
HRESULT hr = E_FAIL;
CString strValue(_T(""));
DWORD dwValue = 0;
::AfxSetResourceHandle(_Module.GetResourceInstance());
switch (chFormat)
{
case _T('s'):
case _T('u'):
case _T('l'):
{
hr = GetValueString(szProperty, &strValue);
if (SUCCEEDED(hr))
{
if (chFormat == _T('u'))
strValue.MakeUpper();
else if (chFormat == _T('l'))
strValue.MakeLower();
strValue.TrimRight();
}
}
break;
case _T('v'):
{
hr = GetValueValueMap(szProperty, &strValue);
}
break;
case _T('d'):
case _T('x'):
{
hr = GetValueDWORD(szProperty, &dwValue);
if (SUCCEEDED(hr))
{
strValue.Format(szFormat, dwValue);
}
}
break;
case _T('f'):
{
double dblValue;
hr = GetValueDoubleFloat(szProperty, &dblValue);
if (SUCCEEDED(hr))
{
strValue.Format(szFormat, dblValue);
dwValue = (DWORD) dblValue;
}
}
break;
case _T('b'):
{
if (gstrYes.IsEmpty())
gstrYes.LoadString(IDS_YES);
if (gstrNo.IsEmpty())
gstrNo.LoadString(IDS_NO);
hr = GetValueDWORD(szProperty, &dwValue);
if (SUCCEEDED(hr))
{
strValue = (dwValue) ? gstrYes : gstrNo;
}
}
break;
case _T('w'):
case _T('y'):
case _T('z'):
{
if (gstrBytes.IsEmpty())
gstrBytes.LoadString(IDS_BYTES);
if (gstrKB.IsEmpty())
gstrKB.LoadString(IDS_KB);
if (gstrMB.IsEmpty())
gstrMB.LoadString(IDS_MB);
if (gstrGB.IsEmpty())
gstrGB.LoadString(IDS_GB);
if (gstrTB.IsEmpty())
gstrTB.LoadString(IDS_TB);
double dblValue;
hr = GetValueDoubleFloat(szProperty, &dblValue);
if (SUCCEEDED(hr))
{
CString strFormattedNumber;
dwValue = (DWORD) dblValue; // TBD potential loss of digits
if (chFormat == _T('w'))
strFormattedNumber = DelimitNumber(dblValue);
else
{
int iDivTimes = (chFormat == _T('y')) ? 1 : 0;
double dblWorking(dblValue);
for (; iDivTimes <= 4 && dblWorking >= 1024.0; iDivTimes++)
dblWorking /= 1024.0;
strFormattedNumber = DelimitNumber(dblWorking, (iDivTimes) ? 2 : 0);
switch (iDivTimes)
{
case 0:
strFormattedNumber += _T(" ") + gstrBytes;
break;
case 1:
strFormattedNumber += _T(" ") + gstrKB;
break;
case 2:
strFormattedNumber += _T(" ") + gstrMB;
break;
case 3:
strFormattedNumber += _T(" ") + gstrGB;
break;
case 4:
strFormattedNumber += _T(" ") + gstrTB;
break;
}
if (chFormat == _T('z') && iDivTimes)
strFormattedNumber += _T(" (") + DelimitNumber(dblValue) + _T(" ") + gstrBytes + _T(")");
}
strValue = strFormattedNumber;
}
}
break;
case _T('t'):
{
COleDateTime oledatetime;
SYSTEMTIME systimeValue;
hr = GetValueTime(szProperty, &systimeValue);
oledatetime = (COleDateTime) systimeValue;
if (SUCCEEDED(hr))
{
dwValue = (DWORD)(DATE)oledatetime;
// Try to get the date in the localized format.
strValue.Empty();
TCHAR szBuffer[MAX_PATH]; // seems plenty big
if (::GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systimeValue, NULL, szBuffer, MAX_PATH))
{
strValue = szBuffer;
if (::GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &systimeValue, NULL, szBuffer, MAX_PATH))
strValue += CString(_T(" ")) + CString(szBuffer);
}
// Fall back on our old (partially incorrect) method.
if (strValue.IsEmpty())
strValue = oledatetime.Format(0, LOCALE_USER_DEFAULT);
}
}
break;
case _T('c'):
{
COleDateTime oledatetime;
SYSTEMTIME systimeValue;
hr = GetValueTime(szProperty, &systimeValue);
oledatetime = (COleDateTime) systimeValue;
if (SUCCEEDED(hr))
{
dwValue = (DWORD)(DATE)oledatetime;
// Try to get the date in the localized format.
strValue.Empty();
TCHAR szBuffer[MAX_PATH]; // seems plenty big
if (::GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systimeValue, NULL, szBuffer, MAX_PATH))
strValue = szBuffer;
// Fall back on our old (partially incorrect) method.
if (strValue.IsEmpty())
strValue = oledatetime.Format(0, LOCALE_USER_DEFAULT);
}
}
break;
case _T('a'):
{
hr = GetValueString(szProperty, &strValue);
if (SUCCEEDED(hr))
{
// strValue contains a string locale ID (like "0409"). Convert it into
// and actual LCID.
LCID lcid = (LCID) _tcstoul(strValue, NULL, 16);
TCHAR szCountry[MAX_PATH];
if (GetLocaleInfo(lcid, LOCALE_SCOUNTRY, szCountry, MAX_PATH))
strValue = szCountry;
}
}
break;
default:
break;
// Just continue with the loop.
}
if (SUCCEEDED(hr))
{
if (pstrValue)
{
if (chFormat == _T('d') || chFormat == _T('x') || chFormat == _T('f'))
*pstrValue = strValue;
else
{
CString strFormat(szFormat);
int iPercent = strFormat.Find(_T("%"));
int iLength = strFormat.GetLength();
if (iPercent != -1)
{
while (iPercent < iLength && strFormat[iPercent] != chFormat)
iPercent++;
if (iPercent < iLength)
{
strFormat.SetAt(iPercent, _T('s'));
pstrValue->Format(strFormat, strValue);
}
}
}
}
if (pdwValue)
*pdwValue = dwValue;
}
else
{
if (pstrValue)
*pstrValue = GetMSInfoHRESULTString(hr);
if (pdwValue)
*pdwValue = 0;
}
return hr;
}
//-----------------------------------------------------------------------------
// These functions implement features found in the new versions of MFC (new
// than what we're currently building with).
//-----------------------------------------------------------------------------
int StringFind(CString & str, LPCTSTR szLookFor, int iStartFrom)
{
CString strWorking(str.Right(str.GetLength() - iStartFrom));
int iFind = strWorking.Find(szLookFor);
if (iFind != -1)
iFind += iStartFrom;
return iFind;
}
//-----------------------------------------------------------------------------
// Process the specified string. It will contain a format string with one
// or more flags (flags specific to MSInfo). We need to replace is flag with
// a properly formatted value from pObject, determined by the next property
// in pstrProperties.
//-----------------------------------------------------------------------------
BOOL ProcessColumnString(CMSIValue * pValue, CWMIObject * pObject, CString * pstrProperties)
{
CString strPropertyValue, strProperty, strFragment;
CString strResults(_T(""));
CString strFormatString(pValue->m_strValue);
DWORD dwResults;
BOOL fAdvanced = FALSE;
BOOL fAllPiecesFailed = TRUE;
HRESULT hr = S_OK;
while (!strFormatString.IsEmpty() && SUCCEEDED(hr))
{
// Get the next fragment of the format string with a single format specifier.
int iPercent = strFormatString.Find(_T("%"));
if (iPercent == -1)
{
strResults += strFormatString;
break;
}
int iSecondPercent = StringFind(strFormatString, _T("%"), iPercent + 1);
if (iSecondPercent == -1)
{
strFragment = strFormatString;
strFormatString.Empty();
}
else
{
strFragment = strFormatString.Left(iSecondPercent);
strFormatString = strFormatString.Right(strFormatString.GetLength() - iSecondPercent);
}
// Find the format character for this fragment.
TCHAR chFormat;
do
chFormat = strFragment[++iPercent];
while (!_istalpha(chFormat));
// Get the property name for this fragment.
int iComma = pstrProperties->Find(_T(","));
if (iComma != -1)
{
strProperty = pstrProperties->Left(iComma);
*pstrProperties = pstrProperties->Right(pstrProperties->GetLength() - iComma - 1);
}
else
{
strProperty = *pstrProperties;
pstrProperties->Empty();
}
strProperty.TrimLeft();
strProperty.TrimRight();
if (strProperty.Left(11) == CString(_T("MSIAdvanced")))
{
fAdvanced = TRUE;
strProperty = strProperty.Right(strProperty.GetLength() - 11);
}
// Get the actual value the property and add it to the string.
hr = pObject->GetInterpretedValue(strProperty, strFragment, chFormat, &strPropertyValue, &dwResults);
if (SUCCEEDED(hr))
{
fAllPiecesFailed = FALSE;
strResults += strPropertyValue;
}
else
strResults += GetMSInfoHRESULTString(hr);
}
if (!fAllPiecesFailed)
{
pValue->m_strValue = strResults;
pValue->m_dwValue = dwResults;
}
else
{
pValue->m_strValue = GetMSInfoHRESULTString(hr);
pValue->m_dwValue = 0;
}
pValue->m_fAdvanced = fAdvanced;
return TRUE;
}
//-----------------------------------------------------------------------------
// A general purpose function to add the contents of object pObject to the
// columns, based on the properties in szProperties and the string referenced
// by uiColumns.
//-----------------------------------------------------------------------------
void CWMIHelper::AddObjectToOutput(CPtrList * aColValues, int iColCount, CWMIObject * pObject, LPCTSTR szProperties, UINT uiColumns)
{
POSITION aPositions[32]; // should never be more than 32 columns
ASSERT(iColCount < 32);
CString strProperties(szProperties);
// Save the starting position for the new entries we're adding from the resoure.
int iColListStart = (int)aColValues[0].GetCount();
LoadColumnsFromResource(uiColumns, aColValues, iColCount);
// Look through each of the new cells. For each string in a cell, if we
// find a formatting flag (like %s), get the next property out of the
// property list and format the string.
for (int iCol = 0; iCol < iColCount; iCol++)
aPositions[iCol] = aColValues[iCol].FindIndex(iColListStart);
while (aPositions[0])
for (iCol = 0; iCol < iColCount; iCol++)
{
ASSERT(aPositions[iCol]);
if (aPositions[iCol])
{
CMSIValue * pValue = (CMSIValue *) aColValues[iCol].GetNext(aPositions[iCol]);
if (pValue && pValue->m_strValue.Find(_T("%")) != -1)
ProcessColumnString(pValue, pObject, &strProperties);
}
}
}
//-----------------------------------------------------------------------------
// Same as previous, but takes a string instead of a resource ID.
//-----------------------------------------------------------------------------
void CWMIHelper::AddObjectToOutput(CPtrList * aColValues, int iColCount, CWMIObject * pObject, LPCTSTR szProperties, LPCTSTR szColumns)
{
POSITION aPositions[32]; // should never be more than 32 columns
ASSERT(iColCount < 32);
CString strProperties(szProperties);
// Save the starting position for the new entries we're adding from the resoure.
int iColListStart = (int)aColValues[0].GetCount();
LoadColumnsFromString(szColumns, aColValues, iColCount);
// Look through each of the new cells. For each string in a cell, if we
// find a formatting flag (like %s), get the next property out of the
// property list and format the string.
for (int iCol = 0; iCol < iColCount; iCol++)
aPositions[iCol] = aColValues[iCol].FindIndex(iColListStart);
while (aPositions[0])
for (iCol = 0; iCol < iColCount; iCol++)
{
ASSERT(aPositions[iCol]);
if (aPositions[iCol])
{
CMSIValue * pValue = (CMSIValue *) aColValues[iCol].GetNext(aPositions[iCol]);
if (pValue && pValue->m_strValue.Find(_T("%")) != -1)
ProcessColumnString(pValue, pObject, &strProperties);
}
}
}
void CWMIHelper::AppendBlankLine(CPtrList * aColValues, int iColCount, BOOL fOnlyIfNotEmpty)
{
if (aColValues[0].GetCount() || fOnlyIfNotEmpty == FALSE)
for (int iCol = 0; iCol < iColCount; iCol++)
AppendCell(aColValues[iCol], _T(""), 0);
}
void CWMIHelper::AppendCell(CPtrList & listColumns, const CString & strValue, DWORD dwValue, BOOL fAdvanced)
{
CMSIValue * pValue = new CMSIValue(strValue, dwValue, fAdvanced);
if (pValue)
listColumns.AddTail((void *) pValue);
}