2285 lines
65 KiB
C++
2285 lines
65 KiB
C++
//=============================================================================
|
|
// File: provider.cpp
|
|
// Author: a-jammar
|
|
// Covers: CDataProvider
|
|
//
|
|
// Copyright (c) 1998-1999 Microsoft Corporation
|
|
//
|
|
// This file contains functions implementing the class which uses WBEM to
|
|
// retrieve information, including handling enumeration of class instances.
|
|
// For usage, see gathint.h.
|
|
//
|
|
// NOTE: define GATH_STANDALONE if this class is being used in an application
|
|
// which doesn't call CoInitialize().
|
|
//=============================================================================
|
|
|
|
#include "stdafx.h"
|
|
#include "gather.h"
|
|
#include "gathint.h"
|
|
#pragma warning(disable : 4099)
|
|
#include "wbemcli.h"
|
|
#pragma warning(default : 4099)
|
|
#include "resource.h"
|
|
#include "resrc1.h"
|
|
|
|
#ifndef WBEM_FLAG_USE_AMENDED_QUALIFIERS
|
|
#define WBEM_FLAG_USE_AMENDED_QUALIFIERS 0x20000
|
|
#endif
|
|
|
|
#ifdef PROFILE_MSINFO
|
|
CFile fileProfile(_T("c:\\msinfo32.txt"), CFile::modeCreate | CFile::modeWrite);
|
|
static DWORD dwProfileTime;
|
|
|
|
inline void START_TIMER()
|
|
{
|
|
dwProfileTime = ::GetTickCount();
|
|
}
|
|
|
|
inline void END_TIMER(const CString & strOperation)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
DWORD dwDelta = ::GetTickCount() - dwProfileTime;
|
|
CString strMessage;
|
|
|
|
strMessage.Format(_T("%ld.%03d\t%s\r\n"), dwDelta / 1000, dwDelta % 100, strOperation);
|
|
fileProfile.Write((const void *) (LPCTSTR) strMessage, strMessage.GetLength()*sizeof(TCHAR));
|
|
}
|
|
|
|
inline void END_TIMER(const CString & strOperation, const CString & strArg)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
DWORD dwDelta = ::GetTickCount() - dwProfileTime;
|
|
CString strTemp;
|
|
CString strMessage;
|
|
|
|
strTemp.Format(strOperation, strArg);
|
|
strMessage.Format(_T("%ld.%03d\t%s\r\n"), dwDelta / 1000, dwDelta % 100, strTemp);
|
|
fileProfile.Write((const void *) (LPCTSTR) strMessage, strMessage.GetLength()*sizeof(TCHAR));
|
|
}
|
|
|
|
inline void WRITE(const CString & strMessage)
|
|
{
|
|
fileProfile.Write((const void *) (LPCTSTR) strMessage, strMessage.GetLength()*sizeof(TCHAR));
|
|
}
|
|
|
|
inline void WRITE(const CString & strMessage, const CString & strArg)
|
|
{
|
|
CString strTemp;
|
|
strTemp.Format(strMessage, strArg);
|
|
fileProfile.Write((const void *) (LPCTSTR) strTemp, strTemp.GetLength()*sizeof(TCHAR));
|
|
}
|
|
#else
|
|
#define START_TIMER()
|
|
#define END_TIMER(X, Y)
|
|
inline void WRITE(const CString & strMessage) {};
|
|
inline void WRITE(const CString & strMessage, const CString & strArg) {};
|
|
#endif
|
|
|
|
// TIMEOUT is used when enumerating classes.
|
|
|
|
#define TIMEOUT -1
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This function is used to change the impersonation of certain WBEM interface
|
|
// pointers.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline HRESULT ChangeWBEMSecurity(IUnknown * pUnknown)
|
|
{
|
|
IClientSecurity * pCliSec = NULL;
|
|
|
|
HRESULT hr = pUnknown->QueryInterface(IID_IClientSecurity, (void **) &pCliSec);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = pCliSec->SetBlanket(pUnknown, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
|
|
pCliSec->Release();
|
|
return hr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The constructor just initializes some member variables.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CDataProvider::CDataProvider()
|
|
{
|
|
m_fInitialized = FALSE;
|
|
m_pIWbemServices = NULL;
|
|
m_pGatherer = NULL;
|
|
m_dwRefreshingCategoryID = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The destructor releases the IWbemServices pointer, if we have one, and
|
|
// empties the interface pointer caches.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CDataProvider::~CDataProvider()
|
|
{
|
|
if (m_fInitialized)
|
|
ClearCache();
|
|
|
|
if (m_pIWbemServices)
|
|
{
|
|
m_pIWbemServices->Release();
|
|
m_pIWbemServices = NULL;
|
|
}
|
|
|
|
// Remove all of the WBEM service pointers for different namespaces
|
|
|
|
IWbemServices * pServices;
|
|
CString strNamespace;
|
|
for (POSITION pos = m_mapNamespaceToService.GetStartPosition(); pos != NULL;)
|
|
{
|
|
m_mapNamespaceToService.GetNextAssoc(pos, strNamespace, (void * &) pServices);
|
|
if (pServices)
|
|
pServices->Release();
|
|
}
|
|
m_mapNamespaceToService.RemoveAll();
|
|
|
|
m_fInitialized = FALSE;
|
|
|
|
#ifdef GATH_STANDALONE
|
|
CoUninitialize();
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The create method actually attempts to get a WBEM interface pointer for the
|
|
// specified machine. If no machine is specified, the local computer is used.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL CDataProvider::Create(const CString & strComputer, CDataGatherer * pGatherer)
|
|
{
|
|
IWbemLocator * pIWbemLocator = NULL;
|
|
CString strNamespace;
|
|
|
|
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
|
|
|
|
// The create method should never be called twice.
|
|
|
|
ASSERT(!m_fInitialized);
|
|
if (m_fInitialized)
|
|
return FALSE;
|
|
|
|
m_strComputer = strComputer;
|
|
m_pGatherer = pGatherer;
|
|
|
|
// The namespace string either uses the computer name, or a '.' for the local computer.
|
|
|
|
if (strComputer.IsEmpty())
|
|
strNamespace = CString(_T("\\\\.\\root\\cimv2"));
|
|
else
|
|
{
|
|
strNamespace = strComputer + CString(_T("\\root\\cimv2"));
|
|
if (strNamespace.Left(2).Compare(CString(_T("\\\\"))) != 0)
|
|
strNamespace = CString(_T("\\\\")) + strNamespace;
|
|
}
|
|
|
|
#ifdef GATH_STANDALONE
|
|
CoInitialize(NULL);
|
|
#endif
|
|
|
|
// Initialize security.
|
|
|
|
CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0);
|
|
|
|
// TBD: make sure WBEM is started.
|
|
|
|
// We get a WBEM interface pointer by first creating a WBEM locator interface, then
|
|
// using it to connect to a server to get an IWbemServices pointer.
|
|
|
|
ASSERT(m_pIWbemServices == NULL);
|
|
if (CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pIWbemLocator) == S_OK)
|
|
{
|
|
BSTR pNamespace = strNamespace.AllocSysString();
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Connect \"%s\" - "), strNamespace);
|
|
|
|
HRESULT hrServer = pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &m_pIWbemServices);
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
|
|
|
|
if (hrServer != S_OK)
|
|
{
|
|
m_pIWbemServices = NULL;
|
|
switch (hrServer)
|
|
{
|
|
case WBEM_E_OUT_OF_MEMORY:
|
|
pGatherer->SetLastError(GATH_ERR_NOWBEMOUTOFMEM);
|
|
break;
|
|
|
|
case WBEM_E_ACCESS_DENIED:
|
|
pGatherer->SetLastError(GATH_ERR_NOWBEMACCESSDENIED);
|
|
break;
|
|
|
|
case WBEM_E_INVALID_NAMESPACE:
|
|
pGatherer->SetLastError(GATH_ERR_NOWBEMBADSERVER);
|
|
break;
|
|
|
|
case WBEM_E_TRANSPORT_FAILURE:
|
|
pGatherer->SetLastError(GATH_ERR_NOWBEMNETWORKFAILURE);
|
|
break;
|
|
|
|
case WBEM_E_FAILED:
|
|
case WBEM_E_INVALID_PARAMETER:
|
|
default:
|
|
pGatherer->SetLastError(GATH_ERR_NOWBEMCONNECT);
|
|
}
|
|
}
|
|
else if (m_pIWbemServices)
|
|
ChangeWBEMSecurity(m_pIWbemServices);
|
|
|
|
if (pNamespace)
|
|
SysFreeString(pNamespace);
|
|
|
|
if (pIWbemLocator)
|
|
{
|
|
pIWbemLocator->Release();
|
|
pIWbemLocator = NULL;
|
|
}
|
|
}
|
|
else
|
|
pGatherer->SetLastError(GATH_ERR_NOWBEMLOCATOR);
|
|
|
|
// Load in the strings for TRUE and FALSE. (When we query for a boolean value,
|
|
// we want to return a string representing the results).
|
|
|
|
m_strTrue.LoadString(IDS_TRUE);
|
|
ASSERT(!m_strTrue.IsEmpty());
|
|
if (m_strTrue.IsEmpty())
|
|
m_strTrue = CString("1");
|
|
|
|
m_strFalse.LoadString(IDS_FALSE);
|
|
ASSERT(!m_strFalse.IsEmpty());
|
|
if (m_strFalse.IsEmpty())
|
|
m_strFalse = CString("0");
|
|
|
|
m_strBadProperty.LoadString(IDS_BAD_PROPERTY);
|
|
ASSERT(!m_strBadProperty.IsEmpty());
|
|
|
|
m_strPropertyUnavail.LoadString(IDS_PROPERTY_UNAVAILABLE);
|
|
ASSERT(!m_strPropertyUnavail.IsEmpty());
|
|
|
|
m_strAccessDeniedLabel.LoadString(IDS_WBEM_ACCESS_DENIED);
|
|
ASSERT(!m_strAccessDeniedLabel.IsEmpty());
|
|
|
|
m_strTransportFailureLabel.LoadString(IDS_WBEM_TRANSPORT_FAILURE);
|
|
ASSERT(!m_strTransportFailureLabel.IsEmpty());
|
|
|
|
m_dwRefreshingCategoryID = 0;
|
|
|
|
m_fInitialized = (m_pIWbemServices != NULL);
|
|
return (m_fInitialized);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This function is used to retrieve a pointer to IWbemServices for a
|
|
// particular namespace. The default namespace is cimv2.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
IWbemServices * CDataProvider::GetWBEMService(CString * pstrNamespace)
|
|
{
|
|
if (pstrNamespace == NULL || pstrNamespace->IsEmpty())
|
|
return m_pIWbemServices;
|
|
|
|
// 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;
|
|
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_strComputer.IsEmpty())
|
|
strNamespace = CString(_T("\\\\.\\root\\")) + *pstrNamespace;
|
|
else
|
|
{
|
|
if (m_strComputer.Right(1) == CString(_T("\\")))
|
|
strNamespace = m_strComputer + CString(_T("root\\")) + *pstrNamespace;
|
|
else
|
|
strNamespace = m_strComputer + 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();
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Connect \"%s\" - "), strNamespace);
|
|
|
|
HRESULT hrServer = pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pServices);
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
|
|
|
|
if (pNamespace)
|
|
SysFreeString(pNamespace);
|
|
|
|
if (pIWbemLocator)
|
|
{
|
|
pIWbemLocator->Release();
|
|
pIWbemLocator = NULL;
|
|
}
|
|
|
|
if (SUCCEEDED(hrServer) && pServices)
|
|
{
|
|
ChangeWBEMSecurity(pServices);
|
|
m_mapNamespaceToService.SetAt(*pstrNamespace, (void *) pServices);
|
|
return pServices;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 CDataProvider::QueryValue(const CString & strClass, const CString & strProperty, CString & strResult)
|
|
{
|
|
strResult.Empty();
|
|
|
|
ASSERT(m_fInitialized && m_pIWbemServices);
|
|
if (!m_fInitialized || m_pIWbemServices == NULL)
|
|
return FALSE;
|
|
|
|
CMSIObject * pObject = GetObject(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(CheckValueMap(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 CDataProvider::QueryValueDWORD(const CString & strClass, const CString & strProperty, DWORD & dwResult, CString & strMessage)
|
|
{
|
|
dwResult = 0L;
|
|
strMessage.Empty();
|
|
|
|
ASSERT(m_fInitialized && m_pIWbemServices);
|
|
if (!m_fInitialized || m_pIWbemServices == NULL)
|
|
return FALSE;
|
|
|
|
CMSIObject * pObject = GetObject(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 CDataProvider::QueryValueDoubleFloat(const CString & strClass, const CString & strProperty, double & dblResult, CString & strMessage)
|
|
{
|
|
dblResult = 0L;
|
|
strMessage.Empty();
|
|
|
|
ASSERT(m_fInitialized && m_pIWbemServices);
|
|
if (!m_fInitialized || m_pIWbemServices == NULL)
|
|
return FALSE;
|
|
|
|
CMSIObject * pObject = GetObject(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 CDataProvider::QueryValueDateTime(const CString & strClass, const CString & strProperty, COleDateTime & datetime, CString & strMessage)
|
|
{
|
|
datetime.SetDateTime(0, 1, 1, 0, 0, 0);
|
|
strMessage.Empty();
|
|
|
|
ASSERT(m_fInitialized && m_pIWbemServices);
|
|
if (!m_fInitialized || m_pIWbemServices == NULL)
|
|
return FALSE;
|
|
|
|
CMSIObject * pObject = GetObject(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 = atoi(strTemp.Mid(0, 4));
|
|
nMonth = atoi(strTemp.Mid(4, 2));
|
|
nDay = atoi(strTemp.Mid(6, 2));
|
|
nHour = atoi(strTemp.Mid(8, 2));
|
|
nMin = atoi(strTemp.Mid(10, 2));
|
|
nSec = atoi(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;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 CDataProvider::ResetClass(const CString & strClass, const GATH_FIELD * pConstraints)
|
|
{
|
|
CMSIEnumerator * pMSIEnumerator = GetEnumObject(strClass, pConstraints);
|
|
if (pMSIEnumerator == NULL)
|
|
return FALSE;
|
|
|
|
// Reset the enumerator, and remove the cached object pointer if there is one.
|
|
|
|
pMSIEnumerator->Reset(pConstraints);
|
|
RemoveObject(strClass);
|
|
|
|
CMSIObject * pObject = GetObject(strClass, pConstraints);
|
|
if (pObject == NULL || pObject->IsValid() == CDataProvider::MOS_NO_INSTANCES)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Move the cached IWbemClassObject pointer to the next instance.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL CDataProvider::EnumClass(const CString & strClass, const GATH_FIELD * pConstraints)
|
|
{
|
|
ASSERT(m_pIWbemServices);
|
|
ASSERT(m_fInitialized);
|
|
if (!m_fInitialized)
|
|
return FALSE;
|
|
|
|
// Verify that there is an object enumerator in place.
|
|
|
|
if (GetEnumObject(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).
|
|
|
|
RemoveObject(strClass);
|
|
CMSIObject * pObject = GetObject(strClass, pConstraints);
|
|
if (pObject && (pObject->IsValid() == CDataProvider::MOS_INSTANCE))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
// if ((pObject == NULL) || (pObject->IsValid() == CDataProvider::MOS_NO_INSTANCES))
|
|
// 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 * CDataProvider::GetEnumObject(const CString & strClass, const GATH_FIELD * pConstraints)
|
|
{
|
|
ASSERT(m_pIWbemServices);
|
|
if (m_pIWbemServices == NULL)
|
|
return NULL;
|
|
|
|
// 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;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This function scans through the list of constraints to see if any of them
|
|
// are SQL filters for an enumerator. If one is, the string which serves
|
|
// as a SQL statement is returned, after being processed to replace any
|
|
// keywords (such as class.property values).
|
|
//-----------------------------------------------------------------------------
|
|
#if FALSE
|
|
CString CDataProvider::GetSQLStatement(const GATH_FIELD * pConstraints, BOOL & fMinOfOne)
|
|
{
|
|
CString strMinOfOne(_T("min-of-one"));
|
|
CString strSQLKeyword(SQL_FILTER);
|
|
const GATH_FIELD * pSQLConstraint = pConstraints;
|
|
|
|
fMinOfOne = FALSE;
|
|
|
|
// Look through the constaints to see if one starts with the SQL keyword.
|
|
|
|
while (pSQLConstraint)
|
|
{
|
|
if (pSQLConstraint->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
|
|
if (pSQLConstraint->m_strFormat.Left(strSQLKeyword.GetLength()).CompareNoCase(strSQLKeyword) == 0)
|
|
break;
|
|
pSQLConstraint = pSQLConstraint->m_pNext;
|
|
}
|
|
|
|
if (pSQLConstraint == NULL)
|
|
return CString(_T(""));
|
|
|
|
// Strip off the SQL keyword.
|
|
|
|
CString strWorking(pSQLConstraint->m_strFormat);
|
|
strWorking = strWorking.Right(strWorking.GetLength() - strSQLKeyword.GetLength());
|
|
|
|
// Now, replace any keywords of the form "[class.property]" with the actual
|
|
// value of the property.
|
|
|
|
CString strResults;
|
|
while (!strWorking.IsEmpty())
|
|
{
|
|
if (strWorking[0] != _T('['))
|
|
{
|
|
int index = strWorking.Find(_T('['));
|
|
if (index < 0)
|
|
index = strWorking.GetLength();
|
|
strResults += strWorking.Left(index);
|
|
strWorking = strWorking.Right(strWorking.GetLength() - index);
|
|
}
|
|
else
|
|
{
|
|
CString strKeyword;
|
|
|
|
strWorking = strWorking.Right(strWorking.GetLength() - 1);
|
|
int index = strWorking.Find(_T(']'));
|
|
if (index < 0)
|
|
{
|
|
ASSERT(FALSE);
|
|
return CString(_T(""));
|
|
}
|
|
|
|
strKeyword = strWorking.Left(index);
|
|
if (strKeyword.CompareNoCase(strMinOfOne) == 0)
|
|
fMinOfOne = TRUE;
|
|
else if (!strKeyword.IsEmpty())
|
|
{
|
|
int iDotIndex = strKeyword.Find(_T('.'));
|
|
if (iDotIndex >= 0)
|
|
{
|
|
CString strValue;
|
|
if (QueryValue(strKeyword.Left(iDotIndex), strKeyword.Right(strKeyword.GetLength() - iDotIndex - 1), strValue))
|
|
strResults += strValue;
|
|
}
|
|
}
|
|
strWorking = strWorking.Right(strWorking.GetLength() - (index + 1));
|
|
}
|
|
}
|
|
|
|
return strResults;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This function indicates if the only constraint specified is a SQL statement.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOL CDataProvider::IsSQLConstraint(const GATH_FIELD * pConstraints)
|
|
{
|
|
CString strSQLKeyword(SQL_FILTER);
|
|
|
|
if (pConstraints)
|
|
if (pConstraints->m_strSource.CompareNoCase(CString(STATIC_SOURCE)) == 0)
|
|
if (pConstraints->m_strFormat.Left(strSQLKeyword.GetLength()).CompareNoCase(strSQLKeyword) == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Retrieve the interface pointer for the specified IWbemClassObject.
|
|
// If there isn't one cached, create one and cache it.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CMSIObject * CDataProvider::GetObject(const CString & strClass, const GATH_FIELD * pConstraints, CString * pstrLabel)
|
|
{
|
|
ASSERT(m_pIWbemServices);
|
|
if (m_pIWbemServices == NULL)
|
|
return NULL;
|
|
|
|
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 = GetEnumObject(strClass);
|
|
if (pEnumerator)
|
|
{
|
|
HRESULT hr = pEnumerator->Next(&pReturn);
|
|
if (S_OK != hr)
|
|
{
|
|
if (pReturn)
|
|
delete pReturn;
|
|
pReturn = NULL;
|
|
if (m_pGatherer)
|
|
{
|
|
if (m_dwRefreshingCategoryID)
|
|
m_pGatherer->SetLastErrorHR(hr, m_dwRefreshingCategoryID);
|
|
else
|
|
m_pGatherer->SetLastErrorHR(hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pReturn)
|
|
m_mapClassToInterface.SetAt(strClass, (void *) pReturn);
|
|
|
|
return pReturn;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 CDataProvider::EvaluateFilter(IWbemClassObject * pObject, const GATH_FIELD * pConstraints)
|
|
{
|
|
const GATH_FIELD * pLHS = pConstraints, * pRHS = NULL;
|
|
VARIANT variant;
|
|
CString strValue;
|
|
BSTR propName;
|
|
|
|
ASSERT(m_pIWbemServices && pObject);
|
|
if (m_pIWbemServices == NULL || 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 CDataProvider::EvaluateJoin(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(m_pIWbemServices && pObject);
|
|
if (m_pIWbemServices == NULL || 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.
|
|
|
|
RemoveObject(pJoinedTo->m_strSource);
|
|
ResetClass(pJoinedTo->m_strSource, &fieldEnumerated);
|
|
GetObject(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 CDataProvider::IsDependencyJoin(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 CDataProvider::EvaluateDependencyJoin(IWbemClassObject * pObject)
|
|
{
|
|
VARIANT variant, varClassName;
|
|
IWbemClassObject * pNewObject = NULL;
|
|
|
|
ASSERT(m_pIWbemServices);
|
|
if (m_pIWbemServices == NULL)
|
|
return;
|
|
|
|
//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, CDataProvider::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 CDataProvider::RemoveEnumObject(const CString & strClass)
|
|
{
|
|
CMSIEnumerator * pEnumObject = NULL;
|
|
|
|
if (m_mapClassToEnumInterface.Lookup(strClass, (void * &) pEnumObject) && pEnumObject)
|
|
delete pEnumObject;
|
|
|
|
m_mapClassToEnumInterface.RemoveKey(strClass);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Remove the specified IWbemClassObject pointer from the cache.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CDataProvider::RemoveObject(const CString & strClass)
|
|
{
|
|
CMSIObject * pObject = NULL;
|
|
|
|
if (m_mapClassToInterface.Lookup(strClass, (void * &) pObject) && pObject)
|
|
delete pObject;
|
|
|
|
m_mapClassToInterface.RemoveKey(strClass);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Clear out the contents of the caches (forcing the interfaces to be
|
|
// retrieved again).
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CDataProvider::ClearCache()
|
|
{
|
|
CMSIObject * pObject = NULL;
|
|
CMSIEnumerator * pEnumObject = NULL;
|
|
POSITION pos;
|
|
CString strClass;
|
|
|
|
ASSERT(m_fInitialized);
|
|
if (!m_fInitialized)
|
|
return;
|
|
|
|
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();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Implement the CMSIObject class. This is just a thin wrapper for the
|
|
// IWbemClassObject interface.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CMSIObject::CMSIObject(IWbemClassObject * pObject, const CString & strLabel, HRESULT hres, CDataProvider * pProvider, CDataProvider::MSIObjectState objState)
|
|
{
|
|
m_pObject = pObject;
|
|
m_strLabel = strLabel;
|
|
m_hresCreation = hres;
|
|
m_pProvider = pProvider;
|
|
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 != CDataProvider::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, 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;
|
|
}
|
|
|
|
CDataProvider::MSIObjectState CMSIObject::IsValid()
|
|
{
|
|
return m_objState;
|
|
}
|
|
|
|
HRESULT CMSIObject::GetErrorLabel(CString & strError)
|
|
{
|
|
switch (m_hresCreation)
|
|
{
|
|
case WBEM_E_ACCESS_DENIED:
|
|
strError = m_pProvider->m_strAccessDeniedLabel;
|
|
break;
|
|
|
|
case WBEM_E_TRANSPORT_FAILURE:
|
|
strError = m_pProvider->m_strTransportFailureLabel;
|
|
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_pProvider->m_strBadProperty;
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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)
|
|
{
|
|
START_TIMER();
|
|
pNewEnum->Reset();
|
|
END_TIMER(_T("clone HIT [%s]\r\n"), strClass);
|
|
}
|
|
else
|
|
pNewEnum = NULL;
|
|
}
|
|
else
|
|
{
|
|
WRITE(_T("GetEnumerator MISS for %s.\r\n"), strClass);
|
|
}
|
|
|
|
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);
|
|
WRITE(_T("SetEnumerator for %s.\r\n"), strClass);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WRITE(_T("SetEnumerator with NULL pointer for %s, ignoring.\r\n"), strClass);
|
|
}
|
|
}
|
|
|
|
void CEnumMap::Reset()
|
|
{
|
|
IEnumWbemClassObject * pEnum = NULL;
|
|
CString key;
|
|
|
|
WRITE(_T("CEnumMap::Reset()\r\n"));
|
|
|
|
for (POSITION pos = m_mapEnum.GetStartPosition(); pos != NULL;)
|
|
{
|
|
m_mapEnum.GetNextAssoc(pos, key, (void * &) pEnum);
|
|
if (pEnum)
|
|
pEnum->Release();
|
|
}
|
|
|
|
m_mapEnum.RemoveAll();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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_pProvider = 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_pProvider->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_pProvider->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, CDataProvider * pProvider)
|
|
{
|
|
if (strClass.IsEmpty() || !pProvider || !pProvider->m_pIWbemServices)
|
|
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 = pProvider->GetWBEMService(&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, pProvider, 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, pProvider, 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(_T("WQL"));
|
|
BSTR query = SysAllocString(strStatement);
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Create WQL enum \"%s\" - "), strStatement);
|
|
|
|
START_TIMER();
|
|
m_hresCreation = pServices->ExecQuery(language, query, WBEM_FLAG_RETURN_IMMEDIATELY, 0, &m_pEnum);
|
|
END_TIMER(_T("create WQL[%s]"), strStatement);
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
|
|
|
|
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();
|
|
START_TIMER();
|
|
m_pEnum = pProvider->m_enumMap.GetEnumerator(m_strAssocClass);
|
|
if (m_pEnum)
|
|
m_hresCreation = S_OK;
|
|
else
|
|
{
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Create enum \"%s\" - "), m_strAssocClass);
|
|
|
|
m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum);
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
|
|
}
|
|
END_TIMER(_T("create LNK[%s]"), m_strAssocClass);
|
|
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, pProvider);
|
|
break;
|
|
|
|
case CMSIEnumerator::CLASS:
|
|
default:
|
|
{
|
|
BSTR className = SysAllocString(strClassPart);
|
|
START_TIMER();
|
|
m_pEnum = pProvider->m_enumMap.GetEnumerator(strClassPart);
|
|
if (m_pEnum)
|
|
m_hresCreation = S_OK;
|
|
else
|
|
{
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Create enum \"%s\" - "), strClassPart);
|
|
|
|
m_hresCreation = pServices->CreateInstanceEnum(className, WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &m_pEnum);
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
|
|
}
|
|
END_TIMER(_T("create CLS[%s]"), strClassPart);
|
|
SysFreeString(className);
|
|
}
|
|
}
|
|
|
|
// Set some of the other member variables.
|
|
|
|
m_strClass = strClass;
|
|
m_pProvider = pProvider;
|
|
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;
|
|
}
|
|
|
|
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, CDataProvider * pProvider)
|
|
{
|
|
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 = pProvider->GetWBEMService();
|
|
if (pServices)
|
|
{
|
|
BSTR className = SysAllocString(_T("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(_T("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);
|
|
}
|
|
|
|
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, CDataProvider * pProvider, 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 (pProvider->QueryValue(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;
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Enumerate LNK \"%s\" - "), m_strAssocClass);
|
|
|
|
do
|
|
{
|
|
pAssocObj = NULL;
|
|
uReturned = 0;
|
|
START_TIMER();
|
|
hRes = m_pEnum->Next(TIMEOUT, 1, &pAssocObj, &uReturned);
|
|
END_TIMER(_T("next LNK[%s]"), m_strAssocClass);
|
|
|
|
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 (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
|
|
|
|
// If there is an associated object path, get the object.
|
|
|
|
if (!strAssociatedObject.IsEmpty())
|
|
{
|
|
BSTR path = strAssociatedObject.AllocSysString();
|
|
hRes = m_pProvider->m_pIWbemServices->GetObject(path, 0, NULL, &pWBEMObject, NULL);
|
|
SysFreeString(path);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CMSIEnumerator::WQL:
|
|
{
|
|
ULONG uReturned;
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Enumerate WQL \"%s\" - "), m_strClass);
|
|
|
|
START_TIMER();
|
|
hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
|
|
END_TIMER(_T("next WQL[%s]"), m_strClass);
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
|
|
}
|
|
break;
|
|
|
|
case CMSIEnumerator::INTERNAL:
|
|
hRes = InternalNext(&pWBEMObject);
|
|
break;
|
|
|
|
case CMSIEnumerator::CLASS:
|
|
default:
|
|
{
|
|
ULONG uReturned;
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("WMI Enumerate CLASS \"%s\" - "), m_strClass);
|
|
|
|
// 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;
|
|
START_TIMER();
|
|
hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
|
|
END_TIMER(_T("next CLS[%s]"), m_strClass);
|
|
|
|
if (!pWBEMObject || hRes != S_OK || uReturned != 1)
|
|
break;
|
|
else if (m_pProvider->EvaluateFilter(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_pProvider->IsDependencyJoin(m_pConstraints))
|
|
m_pProvider->EvaluateDependencyJoin(pWBEMObject);
|
|
else
|
|
m_pProvider->EvaluateJoin(m_strClass, pWBEMObject, m_pConstraints);
|
|
}
|
|
else
|
|
hRes = m_pEnum->Next(TIMEOUT, 1, &pWBEMObject, &uReturned);
|
|
|
|
if (msiLog.IsLogging(CMSInfoLog::WMI))
|
|
msiLog.WriteLog(CMSInfoLog::WMI, _T("OK\r\n"), TRUE);
|
|
}
|
|
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_pProvider, CDataProvider::MOS_NO_INSTANCES);
|
|
else
|
|
*ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pProvider, CDataProvider::MOS_MSG_INSTANCE);
|
|
}
|
|
else
|
|
*ppObject = new CMSIObject(pWBEMObject, m_strNoInstanceLabel, hRes, m_pProvider, CDataProvider::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_pProvider->GetWBEMService();
|
|
if (pServices)
|
|
{
|
|
BSTR objectpath = ::SysAllocString(strNextObject);
|
|
HRESULT hr = S_OK;
|
|
|
|
if (FAILED(pServices->GetObject(objectpath, 0, NULL, ppWBEMObject, NULL)))
|
|
hr = E_FAIL;
|
|
::SysFreeString(objectpath);
|
|
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:
|
|
START_TIMER();
|
|
hRes = Create(m_strClass, pConstraints, m_pProvider);
|
|
END_TIMER(_T("reset WQL[%s]"), m_strClass);
|
|
break;
|
|
|
|
case CMSIEnumerator::LNK:
|
|
{
|
|
BOOL fDummy, fDummy2;
|
|
CString strDummy;
|
|
|
|
m_strObjPath = m_strLNKObject;
|
|
ProcessEnumString(m_strObjPath, fDummy, fDummy2, m_pProvider, strDummy);
|
|
m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
|
|
START_TIMER();
|
|
hRes = m_pEnum->Reset();
|
|
END_TIMER(_T("reset LNK[%s]"), m_strAssocClass);
|
|
}
|
|
break;
|
|
|
|
case CMSIEnumerator::INTERNAL:
|
|
hRes = Create(m_strClass, pConstraints, m_pProvider);
|
|
break;
|
|
|
|
case CMSIEnumerator::CLASS:
|
|
default:
|
|
m_iMinOfOneCount = (m_fMinOfOne) ? 1 : 0;
|
|
START_TIMER();
|
|
hRes = m_pEnum->Reset();
|
|
END_TIMER(_T("reset CLS[%s]"), m_strClass);
|
|
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;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 CDataProvider::CheckValueMap(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 = GetWBEMService();
|
|
if (!pServices)
|
|
return E_FAIL;
|
|
|
|
CString strFullClass(_T("\\\\.\\root\\cimv2:"));
|
|
strFullClass += strClass;
|
|
BSTR bstrObjectPath = ::SysAllocString(strFullClass);
|
|
hr = pServices->GetObject(bstrObjectPath, WBEM_FLAG_USE_AMENDED_QUALIFIERS, NULL, &pWBEMClassObject, NULL);
|
|
::SysFreeString(bstrObjectPath);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Get the qualifiers from the class object.
|
|
|
|
BSTR bstrProperty = ::SysAllocString(strProperty);
|
|
hr = pWBEMClassObject->GetPropertyQualifierSet(bstrProperty, &qual);
|
|
::SysFreeString(bstrProperty);
|
|
|
|
if (SUCCEEDED(hr) && qual)
|
|
{
|
|
// Get the ValueMap and Value arrays.
|
|
|
|
hrMap = qual->Get(_T("ValueMap"), 0, &vMapArray, NULL);
|
|
hr = qual->Get(_T("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;
|
|
}
|