//============================================================================= // This file contains the code for the classes which implement a live WMI // data source. //============================================================================= #include "stdafx.h" #include "wmilive.h" #include "resource.h" //----------------------------------------------------------------------------- // It's necessary to modify the security settings on a new WMI interface. //----------------------------------------------------------------------------- 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_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); pCliSec->Release(); return hr; } //============================================================================= // CWMILiveObject Functions // // The constructor/destructor are really straight forward. //============================================================================= CWMILiveObject::CWMILiveObject() : m_pObject(NULL), m_pServices(NULL) { } CWMILiveObject::~CWMILiveObject() { if (m_pObject != NULL) { m_pObject->Release(); m_pObject = NULL; } if (m_pServices != NULL) { m_pServices->Release(); m_pServices = NULL; } } //----------------------------------------------------------------------------- // The Create functions will either create the object from a WMI object, or // from a service pointer and a path. //----------------------------------------------------------------------------- HRESULT CWMILiveObject::Create(IWbemServices * pServices, IWbemClassObject * pObject) { ASSERT(pObject && "calling CWMILiveObject::Create with a null object"); if (m_pObject != NULL) m_pObject->Release(); m_pObject = pObject; if (m_pObject) m_pObject->AddRef(); m_pServices = pServices; if (m_pServices) m_pServices->AddRef(); return S_OK; } HRESULT CWMILiveObject::Create(IWbemServices * pServices, LPCTSTR szObjectPath) { ASSERT(pServices && szObjectPath); if (m_pObject != NULL) { m_pObject->Release(); m_pObject = NULL; // must be NULL or GetObject bitches } #ifdef UNICODE BSTR bstrPath = SysAllocString(szObjectPath); #else USES_CONVERSION; LPOLESTR szWidePath = T2OLE(szObjectPath); BSTR bstrPath = SysAllocString(szWidePath); #endif HRESULT hr; if (bstrPath) { hr = pServices->GetObject(bstrPath, 0L, NULL, &m_pObject, NULL); SysFreeString(bstrPath); } else hr = E_OUTOFMEMORY; m_pServices = pServices; if (m_pServices) m_pServices->AddRef(); return hr; } //----------------------------------------------------------------------------- // The simple GetValue returns the named value as a variant. // // The pointer to an existing uninitialized VARIANT structure that receives // the property value, if found. Because this is an output parameter, this // method calls VariantInit on this VARIANT, so you must be sure that this // is not pointing to an active VARIANT. // // Note: You must call VariantClear on the returned VARIANT when its value // is no longer required. This will prevent memory leaks in the client process. //----------------------------------------------------------------------------- HRESULT CWMILiveObject::GetValue(LPCTSTR szProperty, VARIANT * pvarValue) { ASSERT(szProperty && pvarValue); if (m_pObject == NULL) { ASSERT(0 && "CWMILiveObject::GetValue called on a null object"); return E_FAIL; } #ifdef UNICODE BSTR bstrProperty = SysAllocString(szProperty); #else USES_CONVERSION; LPOLESTR szWideProperty = T2OLE(szProperty); BSTR bstrProperty = SysAllocString(szWideProperty); #endif HRESULT hr; if (bstrProperty) { hr = m_pObject->Get(bstrProperty, 0L, pvarValue, NULL, NULL); SysFreeString(bstrProperty); if (FAILED(hr)) hr = E_MSINFO_NOPROPERTY; } else hr = E_FAIL; return hr; } //----------------------------------------------------------------------------- // Get the named value as a string. Handle this even if the result is an // array of values. The caller is responsible for freeing the string. //----------------------------------------------------------------------------- HRESULT CWMILiveObject::GetValueString(LPCTSTR szProperty, CString * pstrValue) { ASSERT(pstrValue); VARIANT variant; HRESULT hr = GetValue(szProperty, &variant); if (SUCCEEDED(hr)) { // 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, 1, &lLower); SafeArrayGetUBound(variant.parray, 1, &lUpper); CComBSTR bstrWorking; BSTR bstr = NULL; for (long i = lLower; i <= lUpper; i++) if (SUCCEEDED(SafeArrayGetElement(variant.parray, &i, (wchar_t*)&bstr))) { if (i != lLower) bstrWorking.Append(L", "); bstrWorking.AppendBSTR(bstr); } *pstrValue = bstrWorking; } } else if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { CComBSTR bstrWorking(V_BSTR(&variant)); unsigned int i, nLength = bstrWorking.Length(); BOOL fNonPrintingChar = FALSE; for (i = 0; i < nLength && !fNonPrintingChar; i++) if (((BSTR)bstrWorking)[i] < (WCHAR)0x20) // the 0x20 is from the XML spec fNonPrintingChar = TRUE; if (fNonPrintingChar) { CString strWorking; for (i = 0; i < nLength; i++) { WCHAR c = ((BSTR)bstrWorking)[i]; if (c >= (WCHAR)0x20) strWorking += c; else { CString strTemp; strTemp.Format(_T("&#x%04x;"), c); strWorking += strTemp; } } *pstrValue = strWorking; } else *pstrValue = bstrWorking; } else { hr = E_MSINFO_NOVALUE; } } VariantClear(&variant); return hr; } //----------------------------------------------------------------------------- // Get the named value as a DWORD. //----------------------------------------------------------------------------- HRESULT CWMILiveObject::GetValueDWORD(LPCTSTR szProperty, DWORD * pdwValue) { ASSERT(pdwValue); VARIANT variant; HRESULT hr = GetValue(szProperty, &variant); if (SUCCEEDED(hr)) { if (VariantChangeType(&variant, &variant, 0, VT_I4) == S_OK) *pdwValue = V_I4(&variant); else hr = E_MSINFO_NOVALUE; } return hr; } //----------------------------------------------------------------------------- // Get the named value as a SYSTEMTIME. //----------------------------------------------------------------------------- HRESULT CWMILiveObject::GetValueTime(LPCTSTR szProperty, SYSTEMTIME * psystimeValue) { ASSERT(psystimeValue); VARIANT variant; HRESULT hr = GetValue(szProperty, &variant); if (SUCCEEDED(hr)) { if (VariantChangeType(&variant, &variant, 0, VT_BSTR) == S_OK) { USES_CONVERSION; LPTSTR szDate = OLE2T(V_BSTR(&variant)); // Parse the date string into the SYSTEMTIME struct. It would be better to // get the date from WMI directly, but there was a problem with this. TBD - // look into whether or not we can do this now. ZeroMemory(psystimeValue, sizeof(SYSTEMTIME)); psystimeValue->wSecond = (unsigned short)_ttoi(szDate + 12); szDate[12] = _T('\0'); psystimeValue->wMinute = (unsigned short)_ttoi(szDate + 10); szDate[10] = _T('\0'); psystimeValue->wHour = (unsigned short)_ttoi(szDate + 8); szDate[ 8] = _T('\0'); psystimeValue->wDay = (unsigned short)_ttoi(szDate + 6); szDate[ 6] = _T('\0'); psystimeValue->wMonth = (unsigned short)_ttoi(szDate + 4); szDate[ 4] = _T('\0'); psystimeValue->wYear = (unsigned short)_ttoi(szDate + 0); } else hr = E_MSINFO_NOVALUE; } return hr; } //----------------------------------------------------------------------------- // Get the named value as a double float. //----------------------------------------------------------------------------- HRESULT CWMILiveObject::GetValueDoubleFloat(LPCTSTR szProperty, double * pdblValue) { ASSERT(pdblValue); VARIANT variant; HRESULT hr = GetValue(szProperty, &variant); if (SUCCEEDED(hr)) { if (VariantChangeType(&variant, &variant, 0, VT_R8) == S_OK) *pdblValue = V_R8(&variant); else hr = E_MSINFO_NOVALUE; } return hr; } //----------------------------------------------------------------------------- // Check for a value map value. If there isn't one, just use the untranslated // value. //----------------------------------------------------------------------------- HRESULT CWMILiveObject::GetValueValueMap(LPCTSTR szProperty, CString * pstrValue) { CString strResult; HRESULT hr = GetValueString(szProperty, &strResult); if (SUCCEEDED(hr)) { CString strClass; CString strValueMapVal; if (m_pServices && SUCCEEDED(GetValueString(_T("__CLASS"), &strClass))) if (SUCCEEDED(CWMILiveHelper::CheckValueMap(m_pServices, strClass, szProperty, strResult, strValueMapVal))) strResult = strValueMapVal; } *pstrValue = strResult; return hr; } //============================================================================= // CWMILiveObjectCollection Functions // // The constructor and destructor for CWMILiveObjectCollection are very // straightforward. //============================================================================= CWMILiveObjectCollection::CWMILiveObjectCollection(IWbemServices * pServices) : m_pServices(pServices), m_pEnum(NULL) { ASSERT(m_pServices); if (m_pServices) m_pServices->AddRef(); } CWMILiveObjectCollection::~CWMILiveObjectCollection() { if (m_pServices) m_pServices->Release(); if (m_pEnum) m_pEnum->Release(); } //----------------------------------------------------------------------------- // Create the collection of WMI objects (a WMI enumerator) based on the // class name and the requested properties. //----------------------------------------------------------------------------- HRESULT CWMILiveObjectCollection::Create(LPCTSTR szClass, LPCTSTR szProperties) { ASSERT(szClass); if (m_pEnum) m_pEnum->Release(); // Build the appopriate WQL query statement from the class and requested properties. LPCTSTR szWQLProperties = (szProperties && szProperties[0]) ? szProperties : _T("*"); LPTSTR szQuery = new TCHAR[_tcsclen(szWQLProperties) + _tcsclen(szClass) + 14 /* length of "SELECT FROM " + 1 */]; if (szQuery == NULL) return E_OUTOFMEMORY; wsprintf(szQuery, _T("SELECT %s FROM %s"), szWQLProperties, szClass); HRESULT hr = CreateWQL(szQuery); delete [] szQuery; return hr; } //----------------------------------------------------------------------------- // Create the collection of WMI objects (a WMI enumerator) based on the query. //----------------------------------------------------------------------------- HRESULT CWMILiveObjectCollection::CreateWQL(LPCTSTR szQuery) { ASSERT(szClass); if (m_pEnum) m_pEnum->Release(); // Perform the query using our saved services pointer. HRESULT hr; BSTR bstrLanguage = SysAllocString(L"WQL"); #ifdef UNICODE BSTR bstrQuery = SysAllocString(szQuery); #else USES_CONVERSION; LPOLESTR szWideQuery = T2OLE(szQuery); BSTR bstrQuery = SysAllocString(szWideQuery); #endif if (bstrLanguage && bstrQuery) hr = m_pServices->ExecQuery(bstrLanguage, bstrQuery, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, 0, &m_pEnum); else hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) ChangeWBEMSecurity(m_pEnum); if (bstrQuery) SysFreeString(bstrQuery); if (bstrLanguage) SysFreeString(bstrLanguage); return hr; } //----------------------------------------------------------------------------- // Create this class of an existing enumerator. This may be a little odd, // since the enumerators will interact if both are advancing. //----------------------------------------------------------------------------- HRESULT CWMILiveObjectCollection::Create(IEnumWbemClassObject * pEnum) { if (m_pEnum) m_pEnum->Release(); m_pEnum = pEnum; if (m_pEnum) { m_pEnum->AddRef(); ChangeWBEMSecurity(m_pEnum); } return S_OK; } //----------------------------------------------------------------------------- // Return the next item in the WMI enumerator as a CWMILiveObject object. //----------------------------------------------------------------------------- HRESULT CWMILiveObjectCollection::GetNext(CWMIObject ** ppObject) { ASSERT(ppObject); if (m_pEnum == NULL) { ASSERT(0 && "CWMILiveObjectCollection::GetNext called on a null enumerator"); return E_FAIL; } IWbemClassObject * pRealWMIObject = NULL; ULONG uReturned; HRESULT hr = m_pEnum->Next(TIMEOUT, 1, &pRealWMIObject, &uReturned); if (hr == S_OK && uReturned == 1) { if (*ppObject == NULL) *ppObject = new CWMILiveObject; if (*ppObject) { hr = ((CWMILiveObject *)(*ppObject))->Create(m_pServices, pRealWMIObject); // this will AddRef the pointer if (FAILED(hr)) { delete (CWMILiveObject *)(*ppObject); *ppObject = NULL; } } else hr = E_OUTOFMEMORY; pRealWMIObject->Release(); } return hr; } //============================================================================= // CWMILiveHelper Functions // // The constructor/destructor are really straight forward. //============================================================================= CWMILiveHelper::CWMILiveHelper() : m_hrError(S_OK), m_strMachine(_T("")), m_strNamespace(_T("")), m_pServices(NULL) { } CWMILiveHelper::~CWMILiveHelper() { if (m_pServices) { m_pServices->Release(); m_pServices = NULL; } if (m_pIWbemServices) { m_pIWbemServices->Release(); m_pIWbemServices = NULL; } Version5ClearCache(); } //----------------------------------------------------------------------------- // Enumerate creates a CWMILiveObjectCollection based on the class. //----------------------------------------------------------------------------- HRESULT CWMILiveHelper::Enumerate(LPCTSTR szClass, CWMIObjectCollection ** ppCollection, LPCTSTR szProperties) { ASSERT(m_pServices); if (m_pServices == NULL) return E_FAIL; ASSERT(ppCollection); if (ppCollection == NULL) return E_INVALIDARG; CWMILiveObjectCollection * pLiveCollection; if (*ppCollection) pLiveCollection = (CWMILiveObjectCollection *) *ppCollection; else pLiveCollection = new CWMILiveObjectCollection(m_pServices); if (pLiveCollection == NULL) return E_FAIL; // TBD - memory failure CString strProperties(szProperties); StringReplace(strProperties, _T("MSIAdvanced"), _T("")); HRESULT hr = pLiveCollection->Create(szClass, strProperties); if (SUCCEEDED(hr)) *ppCollection = (CWMIObjectCollection *) pLiveCollection; else delete pLiveCollection; return hr; } //----------------------------------------------------------------------------- // WQLQuery creates a CWMILiveObjectCollection based on the query. //----------------------------------------------------------------------------- HRESULT CWMILiveHelper::WQLQuery(LPCTSTR szQuery, CWMIObjectCollection ** ppCollection) { ASSERT(m_pServices); if (m_pServices == NULL) return E_FAIL; ASSERT(ppCollection); if (ppCollection == NULL) return E_INVALIDARG; CWMILiveObjectCollection * pLiveCollection; if (*ppCollection) pLiveCollection = (CWMILiveObjectCollection *) *ppCollection; else pLiveCollection = new CWMILiveObjectCollection(m_pServices); if (pLiveCollection == NULL) return E_FAIL; // TBD - memory failure HRESULT hr = pLiveCollection->CreateWQL(szQuery); if (SUCCEEDED(hr)) *ppCollection = (CWMIObjectCollection *) pLiveCollection; else delete pLiveCollection; return hr; } //----------------------------------------------------------------------------- // Get the named object. //----------------------------------------------------------------------------- HRESULT CWMILiveHelper::GetObject(LPCTSTR szObjectPath, CWMIObject ** ppObject) { ASSERT(ppObject); if (ppObject == NULL) return E_INVALIDARG; CString strPath(szObjectPath); if (strPath.Find(_T(":")) == -1) { // The path passed in is not a full object path if it doesn't have a colon. CString strMachine(_T(".")); CString strNamespace(_T("cimv2")); if (!m_strMachine.IsEmpty()) strMachine = m_strMachine; if (!m_strNamespace.IsEmpty()) strNamespace = m_strNamespace; strPath = CString(_T("\\\\")) + strMachine + CString(_T("\\root\\")) + strNamespace + CString(_T(":")) + strPath; } HRESULT hr = E_FAIL; CWMILiveObject * pObject = NULL; if (m_pServices) { pObject = new CWMILiveObject; if (pObject) hr = pObject->Create(m_pServices, strPath); } if (SUCCEEDED(hr)) *ppObject = pObject; else if (pObject) delete pObject; return hr; } //----------------------------------------------------------------------------- // Create this WMI helper based on the machine and namespace. //----------------------------------------------------------------------------- HRESULT CWMILiveHelper::Create(LPCTSTR szMachine, LPCTSTR szNamespace) { if (m_pServices) m_pServices->Release(); m_strMachine = _T("."); if (szMachine && *szMachine) { m_strMachine = szMachine; if (m_strMachine.Left(2) == _T("\\\\")) m_strMachine = m_strMachine.Right(m_strMachine.GetLength() - 2); } m_strNamespace = _T("cimv2"); if (szNamespace && *szNamespace) m_strNamespace = szNamespace; // 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. CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0); IWbemServices * pService = NULL; IWbemLocator * pIWbemLocator = NULL; HRESULT hr = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pIWbemLocator); if (SUCCEEDED(hr)) { if (pIWbemLocator) { CString strWMINamespace; strWMINamespace.Format(_T("\\\\%s\\root\\%s"), m_strMachine, m_strNamespace); BSTR pNamespace = strWMINamespace.AllocSysString(); if (pNamespace) { hr = pIWbemLocator->ConnectServer(pNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pService); SysFreeString(pNamespace); } pIWbemLocator->Release(); pIWbemLocator = NULL; } } if (pService && SUCCEEDED(hr)) ChangeWBEMSecurity(pService); m_hrError = hr; m_pServices = pService; return hr; } //----------------------------------------------------------------------------- // Get a new WMI helper for the specified namespace. // // TBD - this should do something. //----------------------------------------------------------------------------- HRESULT CWMILiveHelper::NewNamespace(LPCTSTR szNamespace, CWMIHelper **ppNewHelper) { return E_FAIL; } //----------------------------------------------------------------------------- // Get the current namespace of this WMI helper. // // TBD - this should do something. //----------------------------------------------------------------------------- HRESULT CWMILiveHelper::GetNamespace(CString * pstrNamespace) { return E_FAIL; } //----------------------------------------------------------------------------- // Check to see if there is an associated value in a value map for this // combination of class, property and value. This is lifted from the // version 5.0 code. //----------------------------------------------------------------------------- CMapStringToString g_mapValueMapV7; HRESULT CWMILiveHelper::CheckValueMap(IWbemServices * pServices, 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; if (!pServices) return E_FAIL; // Check the cache of saved values. CString strLookup = strClass + CString(_T(".")) + strProperty + CString(_T(":")) + strVal; if (g_mapValueMapV7.Lookup(strLookup, strResult)) return S_OK; // Get the class object (not instance) for this class. CString strFullClass(_T("\\\\.\\root\\cimv2:")); strFullClass += strClass; BSTR bstrObjectPath = strFullClass.AllocSysString(); hr = pServices->GetObject(bstrObjectPath, WBEM_FLAG_USE_AMENDED_QUALIFIERS, NULL, &pWBEMClassObject, NULL); ::SysFreeString(bstrObjectPath); if (FAILED(hr)) return hr; // Get the qualifiers from the class object. BSTR bstrProperty = strProperty.AllocSysString(); hr = pWBEMClassObject->GetPropertyQualifierSet(bstrProperty, &qual); ::SysFreeString(bstrProperty); if (SUCCEEDED(hr) && qual) { // Get the ValueMap and Value arrays. hrMap = qual->Get(L"ValueMap", 0, &vMapArray, NULL); hr = qual->Get(L"Values", 0, &vArray, NULL); if (SUCCEEDED(hr) && vArray.vt == (VT_BSTR | VT_ARRAY)) { // Get the property value we're mapping. long index; if (SUCCEEDED(hrMap)) { SAFEARRAY * pma = V_ARRAY(&vMapArray); long lLowerBound = 0, lUpperBound = 0 ; SafeArrayGetLBound(pma, 1, &lLowerBound); SafeArrayGetUBound(pma, 1, &lUpperBound); BSTR vMap; for (long x = lLowerBound; x <= lUpperBound; x++) { SafeArrayGetElement(pma, &x, &vMap); CString strMapVal(vMap); if (0 == strVal.CompareNoCase(strMapVal)) { 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_mapValueMapV7.SetAt(strLookup, strResult); return hr; } //----------------------------------------------------------------------------- // This function supplies a string for a given MSInfo specific HRESULT. //----------------------------------------------------------------------------- CString gstrNoValue, gstrNoProperty; CString GetMSInfoHRESULTString(HRESULT hr) { switch (hr) { case E_MSINFO_NOVALUE: if (gstrNoValue.IsEmpty()) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); gstrNoValue.LoadString(IDS_ERROR_NOVALUE); } return (gstrNoValue); case E_MSINFO_NOPROPERTY: if (gstrNoProperty.IsEmpty()) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); gstrNoProperty.LoadString(IDS_ERROR_NOPROPERTY); } return (gstrNoProperty); default: return (CString(_T(""))); } } //----------------------------------------------------------------------------- // This is a wrapper for the GetObject() method in IWbemServices. This is // called when there is a certain set of properties to get. // // Turns out this doesn't speed us up appreciably on our WMI uses. //----------------------------------------------------------------------------- /* HRESULT CWMILiveObject::PartialInstanceGetObject(IWbemServices * pServices, BSTR bstrPath, IWbemClassObject ** ppObject, LPCTSTR szProperties) { HRESULT hr = WBEM_S_NO_ERROR; if ((pServices != NULL) && (bstrPath != NULL) && (ppObject != NULL)) { IWbemContext * pWbemContext = NULL; hr = CoCreateInstance(CLSID_WbemContext, NULL, CLSCTX_INPROC_SERVER, IID_IWbemContext, (void**) &pWbemContext); CStringArray csaProperties; CString strProperties(szProperties), strProperty; int index = 0; while (!strProperties.IsEmpty()) { strProperty = strProperties.SpanExcluding(_T(", ")); strProperties = strProperties.Mid(strProperty.GetLength()); strProperties.TrimLeft(_T(", ")); csaProperties.SetAtGrow(index++, strProperty); } if (pWbemContext != NULL) { variant_t vValue; V_VT(&vValue) = VT_BOOL; V_BOOL(&vValue) = VARIANT_TRUE; // First set the value that says we are using Get extensions if ((SUCCEEDED(hr = pWbemContext->SetValue(L"__GET_EXTENSIONS", 0L, &vValue))) && (SUCCEEDED(hr = pWbemContext->SetValue(L"__GET_EXT_CLIENT_REQUEST", 0L, &vValue))) ) { // Delete any unneeded properties pWbemContext->DeleteValue(L"__GET_EXT_KEYS_ONLY", 0L); // Now build the array of properties SAFEARRAYBOUND rgsabound [ 1 ] ; rgsabound[0].cElements = csaProperties.GetSize() ; rgsabound[0].lLbound = 0 ; V_ARRAY(&vValue) = SafeArrayCreate ( VT_BSTR , 1 , rgsabound ) ; if ( V_ARRAY(&vValue) ) { V_VT(&vValue) = VT_BSTR | VT_ARRAY; for (long x=0; x < csaProperties.GetSize(); x++) { bstr_t bstrProp = csaProperties[x]; SafeArrayPutElement( V_ARRAY(&vValue), &x, (LPVOID) (BSTR) bstrProp); } // Put the array into the context object if (SUCCEEDED(hr = pWbemContext->SetValue(L"__GET_EXT_PROPERTIES", 0L, &vValue))) { hr = pServices->GetObject(bstrPath, 0, pWbemContext, ppObject, 0); vValue.Clear(); V_VT(&vValue) = VT_BOOL; V_BOOL(&vValue) = VARIANT_FALSE; pWbemContext->SetValue(L"__GET_EXTENSIONS", 0L, &vValue); } } else { } } } else { hr = WBEM_E_INVALID_PARAMETER; } } else { hr = WBEM_E_INVALID_PARAMETER; } return hr; } */