// persistmgr.cpp: implementation of the SCE provider persistence related classes // declared inside persistmgr.h. // Copyright (c)1997-2001 Microsoft Corporation // ////////////////////////////////////////////////////////////////////// // original author: shawnwu // creation date: 1/3/2001 #include "precomp.h" #include #include "genericclass.h" #include "persistmgr.h" #include "requestobject.h" #include "extbase.h" // // some global constants // const WCHAR wchCookieSep = L':'; const WCHAR wchTypeValSep = L':'; const WCHAR wchValueSep = L':'; const WCHAR wchParamSep = L','; const WCHAR wchTypeValLeft = L'<'; const WCHAR wchTypeValRight = L'>'; const WCHAR wchMethodLeft = L'('; const WCHAR wchMethodRight = L')'; const WCHAR wchMethodSep = L';'; const WCHAR wchCySeparator = L'.'; const WCHAR wchQuote = L'\"'; LPCWSTR pszListPrefix = L"A"; LPCWSTR pszKeyPrefix = L"K"; LPCWSTR pszAttachSectionValue = L"1"; LPCWSTR pszNullKey = L"NULL_KEY"; // // the types that we support // VtTypeStruct gVtTypeToStructArray[] = { {L"VT_BOOL", VT_BOOL}, {L"VT_I2", VT_I2}, {L"VT_I4", VT_I4}, //{L"VT_I8", VT_I8}, {L"VT_R4", VT_R4}, {L"VT_R8", VT_R8}, {L"VT_CY", VT_CY}, {L"VT_DATE", VT_DATE}, {L"VT_BSTR", VT_BSTR}, {L"VT_UI1", VT_UI1}, {L"VT_UI2", VT_UI2}, {L"VT_UI4", VT_UI4}, // somehow, WMI doesn't work with VT_UI4 {L"VT_UINT", VT_UINT}, //{L"VT_UI8", VT_UI8}, {L"VT_ARRAY(VT_BOOL)", VT_ARRAY | VT_BOOL}, {L"VT_ARRAY(VT_I2)", VT_ARRAY | VT_I2}, {L"VT_ARRAY(VT_I4)", VT_ARRAY | VT_I4}, //{L"VT_ARRAY(VT_I8)", VT_ARRAY | VT_I8}, {L"VT_ARRAY(VT_R4)", VT_ARRAY | VT_R4}, {L"VT_ARRAY(VT_R8)", VT_ARRAY | VT_R8}, {L"VT_ARRAY(VT_CY)", VT_ARRAY | VT_CY}, {L"VT_ARRAY(VT_DATE)", VT_ARRAY | VT_DATE}, {L"VT_ARRAY(VT_BSTR)", VT_ARRAY | VT_BSTR}, {L"VT_ARRAY(VT_UI1)", VT_ARRAY | VT_UI1}, {L"VT_ARRAY(VT_UI2)", VT_ARRAY | VT_UI2}, {L"VT_ARRAY(VT_UI4)", VT_ARRAY | VT_I4}, // somehow, WMI doesn't work with VT_ARRAY(VT_UI4) {L"VT_ARRAY(VT_UINT)", VT_ARRAY | VT_UINT}, //{L"VT_ARRAY(VT_UI8)", VT_ARRAY | VT_UI8}, }; /* Routine Description: Name: IsVT_Array Functionality: test if a VARTYPE is a safearray. Virtual: N/A. Arguments: vt - The VARTYPE to test. Return Value: true if yes, false if no. Notes: */ bool IsVT_Array ( IN VARTYPE vt ) { return ( (vt & VT_ARRAY) == VT_ARRAY ); } /* Routine Description: Name: GetSubType Functionality: Get the safearray's element type. Virtual: N/A. Arguments: vt - The VARTYPE to get the sub-type. The result is not defined if vt is not representing a safearray type. Return Value: true if yes, false if no. Notes: */ VARTYPE GetSubType ( IN VARTYPE vt ) { return (vt & (~VT_ARRAY)); } // // global instance of maps from string to vt and from vt to string // CMapVtToString gVtToStringMap(sizeof(gVtTypeToStructArray)/sizeof(VtTypeStruct), gVtTypeToStructArray); CMapStringToVt gStringToVtMap(sizeof(gVtTypeToStructArray)/sizeof(VtTypeStruct), gVtTypeToStructArray); /* Routine Description: Name: IsEscapedChar Functionality: test if the wchar is a char that we need to be escaped. Virtual: N/A. Arguments: ch - The wchar to test. Return Value: true if yes, false if no. Notes: */ bool IsEscapedChar ( IN WCHAR ch ) { return (ch == L'\\' || ch == L'"'); } /* Routine Description: Name: GetEscapeCharCount Functionality: will return the count of characters that needs to be escaped - determined by ::IsEscapedChar function Virtual: N/A. Arguments: pszStr - The string to count the escape characters. Return Value: The count of escape characters. Notes: */ DWORD GetEscapeCharCount ( IN LPCWSTR pszStr ) { DWORD dwCount = 0; while (*pszStr != L'\0') { if (::IsEscapedChar(*pszStr)) { ++dwCount; } ++pszStr; } return dwCount; }; /* Routine Description: Name: TrimCopy Functionality: Will copy the portion from pSource that is of length iLen. The difference is that the result string won't have leading and trailing white spaces (determined by iswspace) Virtual: N/A. Arguments: pDest - Receives the copied sub-string. pSource - The source. iLen - The length the sub-string is. Return Value: None. Notes: Caller must guarantee pSource/pDest to have enough room, including the L'\0'. That is the buffer size is >= iLen + 1. */ void TrimCopy ( OUT LPWSTR pDest, IN LPCWSTR pSource, IN int iLen ) { LPCWSTR pHead = pSource; // // avoid modifying iLen // int iRealLen = iLen; // // skip the leading white spaces. Make sure that our target // is decreasing at the same rate. while (*pHead != L'\0' && iswspace(*pHead) && iRealLen) { ++pHead; --iRealLen; } if (iRealLen <= 0) { pDest[0] = L'\0'; } else { // // caller guarantees the pDest if long enough // wcsncpy(pDest, pHead, iRealLen); while (iLen > 1 && iswspace(pDest[iRealLen - 1])) { --iRealLen; } // // 0 terminate it // pDest[iRealLen] = '\0'; } }; /* Routine Description: Name: EscSeekToChar Functionality: Will find the target character (wchChar) in the pszSource. If found, the return pointer points to that character. Any backslash characeter L'\\' will cause an escape. If such escape happens, then pbEscaped parameter will pass back true. If no escape is found, then, pbEscaped will pass back false. The return result in case of of no escape depends on bEndIfNotFound. If bEndIfNotFound is true, then the return value points to the 0 terminator the source pszSource, otherwise, it returns NULL. This escaping won't happen if the special characters (wchChar and the escaped chars) are inside a quoted string. Virtual: N/A. Arguments: pszSource - source. wchChar - The sought wchar. pbEscaped - passback whether it has really been escaped. bEndIfNotFound - whether we should return the end of the source or not if the sought char is not found Return Value: If the wchar is not found: NULL if bEndIfNotFound == false; End of the source if bEndIfNotFound == true; If the wchar is found: address of the sought character. Notes: User must pass in valid parameter values. */ LPCWSTR EscSeekToChar ( IN LPCWSTR pszSource, IN WCHAR wchChar, OUT bool * pbEscaped, IN bool bEndIfNotFound ) { *pbEscaped = false; // // flag if we are currently escaping // bool bIsEscaping = false; // // flag if we are currently inside a quoted string // bool bIsInsideQuote = false; while (*pszSource != L'\0') { if (bIsEscaping) { bIsEscaping = false; ++pszSource; } else if (*pszSource == L'\\') { ++pszSource; bIsEscaping = true; *pbEscaped = true; } else if (*pszSource == wchChar && !bIsInsideQuote) { // // found it // return pszSource; } else { // // see if we are starting a quoted section // if (*pszSource == L'"') { bIsInsideQuote = !bIsInsideQuote; } ++pszSource; } } if (bEndIfNotFound) { return pszSource; } else { return NULL; } } /* Routine Description: Name: EscapeStringData Functionality: Given source (pszStr) string, we will produce a destination string, which will have the backslash character added in front of the characters that need escape. Virtual: N/A. Arguments: pszStr - source. pbstr - receives the result. bQuote - flag if we should quote the result string or not. Return Value: Success: WBEM_NO_ERROR Failure: (1) WBEM_E_INVALID_PARAMETER. (2) WBEM_E_OUT_OF_MEMORY. Notes: */ HRESULT EscapeStringData ( IN LPCWSTR pszStr, OUT BSTR * pbstr, IN bool bQuote ) { if (pszStr == NULL || pbstr == NULL) { return WBEM_E_INVALID_PARAMETER; } DWORD dwEscCharCount = GetEscapeCharCount(pszStr); DWORD dwStrLen = wcslen(pszStr); DWORD dwTotalLen = dwStrLen + dwEscCharCount + (bQuote ? 2 : 0) + 1; *pbstr = ::SysAllocStringLen(NULL, dwTotalLen); if (*pbstr == NULL) { return WBEM_E_OUT_OF_MEMORY; } // // nothing to escape // if (dwEscCharCount == 0) { // // add quote if necessary // LPWSTR lpszCur = *pbstr; if (bQuote) { lpszCur[0] = wchQuote; ++lpszCur; } ::memcpy(lpszCur, pszStr, dwStrLen * sizeof(WCHAR)); if (bQuote) { (*pbstr)[dwTotalLen - 2] = wchQuote; } (*pbstr)[dwTotalLen - 1] = L'\0'; } else { // // do some real escaping here // LPWSTR pszCur = *pbstr; // // add L'\"' if necessary // if (bQuote) { *pszCur = wchQuote; ++pszCur; } // // do escaping copy // bool bIsEscaping = false; while (*pszStr != L'\0') { if (!bIsEscaping && ::IsEscapedChar(*pszStr)) { *pszCur = L'\\'; ++pszCur; } if (!bIsEscaping && *pszStr == L'\\') { bIsEscaping = true; } else if (bIsEscaping) { bIsEscaping = false; } *pszCur = *pszStr; ++pszCur; ++pszStr; } // // add L'\"' if necessary // if (bQuote) { *pszCur = wchQuote; ++pszCur; } *pszCur = L'\0'; } return WBEM_NO_ERROR; } /* Routine Description: Name: DeEscapeStringData Functionality: Inverse of EscapeStringData. See EscapeStringData for functionality. Virtual: N/A. Arguments: pszStr - source. pbstr - receives the result. bQuote - flag if we should get rid of the startind and ending quote. Return Value: Success: WBEM_NO_ERROR Failure: (1) WBEM_E_INVALID_PARAMETER. (2) WBEM_E_OUT_OF_MEMORY. Notes: */ HRESULT DeEscapeStringData ( IN LPCWSTR pszStr, OUT BSTR * pbstr, IN bool bTrimQuote ) { if (pszStr == NULL || pbstr == NULL) { return WBEM_E_INVALID_PARAMETER; } *pbstr = NULL; DWORD dwLen = wcslen(pszStr); LPCWSTR pszCurSrc = pszStr; // // there is a start quote // if (bTrimQuote && *pszCurSrc == wchQuote) { // // there must be an ending quote // if (dwLen < 2 || pszCurSrc[dwLen - 1] != wchQuote) { return WBEM_E_INVALID_PARAMETER; } // // skip the leading quote // ++pszCurSrc; // // need two characters less // dwLen -= 2; } *pbstr = ::SysAllocStringLen(NULL, dwLen + 1); if (*pbstr != NULL) { LPWSTR pszCur = *pbstr; do { if (*pszCurSrc == L'\\') { // // escape it // ++pszCurSrc; } *pszCur = *pszCurSrc; ++pszCur; if (*pszCurSrc == L'\0') { break; } ++pszCurSrc; } while ((pszCurSrc - pszStr) <= dwLen); *pszCur = L'\0'; } return (*pbstr != NULL) ? WBEM_NO_ERROR : WBEM_E_OUT_OF_MEMORY; } //========================================================================= /* Routine Description: Name: CMapStringToVt::CMapStringToVt Functionality: constructor. We will create the map using the passed in array information. Virtual: No. Arguments: dwCount - The count of the array pInfoArray. pInfoArray - The array that has the information. Return Value: none Notes: Caller must guarantee that pInfoArray is at least as many elements as dwCount. */ CMapStringToVt::CMapStringToVt ( IN DWORD dwCount, IN VtTypeStruct * pInfoArray ) { for (DWORD dwIndex = 0; dwIndex < dwCount; ++dwIndex) { m_Map.insert(MapStringToVt::value_type(pInfoArray[dwIndex].pszVtTypeString, pInfoArray[dwIndex].vt)); } } /* Routine Description: Name: CMapStringToVt::GetType Functionality: Given the string version of variant type information, translate it to VARTYPE value. Virtual: No. Arguments: pszTypeStr - The string version of the VARTYPE info. pSubType - The array element's type if pszTypeStr is an array's type string. Return Value: If pszTypeStr is in correct format, we return the appropriate VARTYPE. Otherwise, we return VT_EMPTY Notes: */ VARTYPE CMapStringToVt::GetType ( IN LPCWSTR pszTypeStr, OUT VARTYPE * pSubType OPTIONAL ) { // // look up // MapStringToVt::iterator it = m_Map.find(pszTypeStr); if (pSubType) { *pSubType = VT_EMPTY; } if (it != m_Map.end()) { VARTYPE vt = (*it).second; if (::IsVT_Array(vt)) { if (pSubType) { *pSubType = ::GetSubType(vt); } return VT_ARRAY; } else { return vt; } } return VT_EMPTY; } /* Routine Description: Name: CMapVtToString::CMapVtToString Functionality: Constructor. We will create the map. Virtual: No. Arguments: dwCount - The count of the array pInfoArray. pInfoArray - The array that has the information. Return Value: None. Notes: Caller must guarantee that pInfoArray is at least as many elements as dwCount. */ CMapVtToString::CMapVtToString ( IN DWORD dwCount, IN VtTypeStruct * pInfoArray ) { for (DWORD dwIndex = 0; dwIndex < dwCount; ++dwIndex) m_Map.insert(MapVtToString::value_type(pInfoArray[dwIndex].vt, pInfoArray[dwIndex].pszVtTypeString)); } /* Routine Description: Name: CMapVtToString::GetTypeString Functionality: Given VARTYPE and (if vt == VT_ARRAY) array element's type, it returns our formatted string representation of the type. Virtual: No. Arguments: vt - The VARTYPE. vtSub - The array's element's type if vt == VT_ARRAY. Otherwise, it's ignored. Return Value: NULL if the type is not supported. The global string. As the prototype indicates, this return value is constant. Notes: If vt == VT_ARRAY, then vtSub must contain a valid vt type that we support */ LPCWSTR CMapVtToString::GetTypeString ( IN VARTYPE vt, IN VARTYPE vtSub ) { MapVtToString::iterator it; if (::IsVT_Array(vt)) { it = m_Map.find(vt | vtSub); } else { it = m_Map.find(vt); } if (it != m_Map.end()) { return (*it).second; } return NULL; } /* Routine Description: Name: CMapVtToString::GetTypeString Functionality: Given VARTYPE, it returns our formatted string representation of the type. Virtual: No. Arguments: vt - The VARTYPE. Return Value: NULL if the type is not supported. The global string. As the prototype indicates, this return value is constant. Notes: (1) If vt == VT_ARRAY, then vtSub must contain a valid vt type that we support. (2) This version of the override doesn't work for array types. */ LPCWSTR CMapVtToString::GetTypeString ( IN VARTYPE vt ) { MapVtToString::iterator it = m_Map.find(vt); if (it != m_Map.end()) { return (*it).second; } else { return NULL; } } //========================================================================= // implementations for class CScePropertyMgr /* Routine Description: Name: CScePropertyMgr::CScePropertyMgr Functionality: constructor. Trivial Virtual: No. Arguments: None. Return Value: none Notes: Consider initializing additional members if you need to add them. */ CScePropertyMgr::CScePropertyMgr () { } /* Routine Description: Name: CScePropertyMgr::~CScePropertyMgr Functionality: Destructor. Trivial since we only have smart pointer members that automatically initialize themselves. Virtual: No. Since we never intend to have sub-classes. Arguments: None. Return Value: none Notes: Consider freeing additional members if you need to add them. */ CScePropertyMgr::~CScePropertyMgr() { } /* Routine Description: Name: CScePropertyMgr::Attach Functionality: Attach the object to our property manager. You can safely reattach another object to this manager. Virtual: No. Arguments: pObj - the object this manager will be attached to. Return Value: none Notes: Caller must not call any property access functions until a valid attachment has been established. */ void CScePropertyMgr::Attach ( IN IWbemClassObject *pObj ) { m_srpClassObj.Release(); m_srpClassObj = pObj; } /* Routine Description: Name: CScePropertyMgr::PutProperty Functionality: Put a variant property for the given property. Virtual: No. Arguments: pszProperty - The property's name. pVar - The value for the property. Return Value: Whatever IWbemClassObject::Put returns. Notes: */ HRESULT CScePropertyMgr::PutProperty ( IN LPCWSTR pszProperty, IN VARIANT * pVar ) { return m_srpClassObj->Put(pszProperty, 0, pVar, CIM_EMPTY); } /* Routine Description: Name: CScePropertyMgr::PutProperty Functionality: Put a string valued property for the given property. Virtual: No. Arguments: pszProperty - The property's name. pszValue - The string value for the property. Return Value: Success: Whatever IWbemClassObject::Put returns. Failure: Either WBEM_E_OUT_OF_MEMORY or whatever IWbemClassObject::Put returns. Notes: */ HRESULT CScePropertyMgr::PutProperty ( IN LPCWSTR pszProperty, IN LPCWSTR pszValue ) { HRESULT hr = WBEM_NO_ERROR; // // WMI always wants variant // CComVariant var(pszValue); if (var.vt != VT_ERROR) { hr = m_srpClassObj->Put(pszProperty, 0, &var, CIM_EMPTY); } else { hr = WBEM_E_OUT_OF_MEMORY; } return hr; } /* Routine Description: Name: CScePropertyMgr::PutProperty Functionality: Put an integral valued property for the given property. Virtual: No. Arguments: pszProperty - The property's name. dwValue - The value for the property. SCE_NULL_INTEGER is an invalid SCE integral value Return Value: Success: Whatever IWbemClassObject::Put returns. Failure: whatever IWbemClassObject::Put returns. Notes: We have observed that WMI will always promote all 4-byte integral types to VT_I4. */ HRESULT CScePropertyMgr::PutProperty ( IN LPCWSTR pszProperty, IN DWORD dwValue ) { HRESULT hr = WBEM_NO_ERROR; // // no need to worry about resource leaking, so, use straight forward variant // VARIANT var; V_VT(&var) = VT_I4; if (dwValue == SCE_NULL_INTEGER) { hr = WBEM_E_INVALID_PARAMETER; } else { V_I4(&var) = dwValue; hr = m_srpClassObj->Put(pszProperty, 0, &var, CIM_EMPTY); } return hr; } /* Routine Description: Name: CScePropertyMgr::PutProperty Functionality: Put an float valued property for the given property. Virtual: No. Arguments: pszProperty - The property's name. fValue - The value for the property. Return Value: Success: Whatever IWbemClassObject::Put returns. Failure: whatever IWbemClassObject::Put returns. Notes: */ HRESULT CScePropertyMgr::PutProperty ( IN LPCWSTR pszProperty, IN float fValue ) { // // no need to worry about resource leaking, so, use straight forward variant // VARIANT var; V_VT(&var) = VT_R4; V_R4(&var) = fValue; return m_srpClassObj->Put(pszProperty, 0, &var, CIM_EMPTY); } /* Routine Description: Name: CScePropertyMgr::PutProperty Functionality: Put a double value property for the given property. Virtual: No. Arguments: pszProperty - The property's name. dValue - The value for the property. Return Value: Success: Whatever IWbemClassObject::Put returns. Failure: whatever IWbemClassObject::Put returns. Notes: */ HRESULT CScePropertyMgr::PutProperty ( IN LPCWSTR pszProperty, IN double dValue ) { // // no need to worry about resource leaking, so, use straight forward variant // VARIANT var; V_VT(&var) = VT_R8; V_DATE(&var) = dValue; return m_srpClassObj->Put(pszProperty, 0, &var, CIM_DATETIME); } /* Routine Description: Name: CScePropertyMgr::PutProperty Functionality: Put a boolean value property for the given property. Virtual: No. Arguments: pszProperty - The property's name. bValue - The value for the property. Return Value: Success: Whatever IWbemClassObject::Put returns. Failure: whatever IWbemClassObject::Put returns. Notes: */ HRESULT CScePropertyMgr::PutProperty ( IN LPCWSTR pszProperty, IN bool bValue ) { // // no need to worry about resource leaking, so, use straight forward variant // VARIANT var; V_VT(&var) = VT_BOOL; V_BOOL(&var) = bValue ? VARIANT_TRUE : VARIANT_FALSE; return m_srpClassObj->Put(pszProperty, 0, &var, CIM_EMPTY); } /* Routine Description: Name: CScePropertyMgr::PutProperty Functionality: Put a name list (string) value property for the given property. Virtual: No. Arguments: pszProperty - The property's name. strList - A SCE specific linked list. Return Value: Success: Whatever IWbemClassObject::Put returns. Failure: whatever IWbemClassObject::Put returns. Notes: */ HRESULT CScePropertyMgr::PutProperty ( IN LPCWSTR pszProperty, IN PSCE_NAME_LIST strList ) { // // make sure that our parameters are in good shape to proceed // if (NULL == pszProperty || *pszProperty == 0 || NULL == strList) { return WBEM_E_INVALID_PARAMETER; } else if (NULL == strList) { // // nothing to save // return WBEM_NO_ERROR; } HRESULT hr = WBEM_NO_ERROR; // // find count of the list // long lCount = 0; PSCE_NAME_LIST pTemp; for ( pTemp = strList; pTemp != NULL; pTemp=pTemp->Next) { lCount++; } if ( lCount == 0 ) { // // nothing to save // return hr; } CComVariant varValueArray; // // create a bstr safearray // SAFEARRAYBOUND sbArrayBounds ; sbArrayBounds.cElements = lCount; sbArrayBounds.lLbound = 0; // // will put all the names inside SCE_NAME_LIST into the safe array // if (V_ARRAY(&varValueArray) = ::SafeArrayCreate(VT_BSTR, 1, &sbArrayBounds)) { V_VT(&varValueArray) = VT_BSTR | VT_ARRAY ; pTemp = strList; for (long j = 0; SUCCEEDED(hr) && pTemp != NULL ; pTemp=pTemp->Next) { CComVariant varVal(pTemp->Name); if (varVal.vt == VT_BSTR) { hr = ::SafeArrayPutElement(V_ARRAY(&varValueArray), &j, varVal.bstrVal); } else { hr = WBEM_E_OUT_OF_MEMORY; break; } j++; } // // only put if we succeeded in the previous actions // if (SUCCEEDED(hr)) { hr = m_srpClassObj->Put(pszProperty, 0, &varValueArray, CIM_EMPTY); } } else { hr = WBEM_E_FAILED; } return hr; } /* Routine Description: Name: CScePropertyMgr::GetProperty Functionality: Get the property's value in variant form. Virtual: No. Arguments: pszProperty - The property's name. pVar - receives the value. Return Value: Success: Whatever IWbemClassObject::Get returns. Failure: whatever IWbemClassObject::Get returns. Notes: */ HRESULT CScePropertyMgr::GetProperty ( IN LPCWSTR pszProperty, OUT VARIANT * pVar ) { return m_srpClassObj->Get(pszProperty, 0, pVar, NULL, NULL); } /* Routine Description: Name: CScePropertyMgr::GetProperty Functionality: Get the property's value in bstr form. Virtual: No. Arguments: pszProperty - The property's name. pbstrValues - receives bstr value. Return Value: Success: Whatever IWbemClassObject::Get returns, or WBEM_S_RESET_TO_DEFAULT if the value is not returned from the wbem object. Failure: whatever IWbemClassObject::Get returns. Notes: Caller must not pass NULL for the out parameter. Caller is responsible for releasing the received bstr. */ HRESULT CScePropertyMgr::GetProperty ( IN LPCWSTR pszProperty, OUT BSTR * pbstrValue ) { *pbstrValue = NULL; // // we don't know what this Get will give us, but we are asking for BSTR // so, let the CComVariant take care of the resource issues CComVariant varVal; HRESULT hr = m_srpClassObj->Get(pszProperty, 0, &varVal, NULL, NULL); if (varVal.vt == VT_BSTR && wcslen(varVal.bstrVal) > INTERNET_MAX_PATH_LENGTH) { hr = WBEM_E_INVALID_METHOD_PARAMETERS; } else if (varVal.vt == VT_BSTR) { *pbstrValue = ::SysAllocString(varVal.bstrVal); if (*pbstrValue == NULL) { hr = WBEM_E_OUT_OF_MEMORY; } } else if(varVal.vt == VT_EMPTY || varVal.vt == VT_NULL ) { hr = WBEM_S_RESET_TO_DEFAULT; } return hr; } /* Routine Description: Name: CScePropertyMgr::GetProperty Functionality: Get the property's value in DWORD form. Virtual: No. Arguments: pszProperty - The property's name. pdwValue - receives DWORD value. Return Value: Success: Whatever IWbemClassObject::Get returns, or WBEM_S_RESET_TO_DEFAULT if the value is not returned from the wbem object. Failure: whatever IWbemClassObject::Get returns. Notes: Caller must not pass NULL for the out parameter. */ HRESULT CScePropertyMgr::GetProperty ( IN LPCWSTR pszProperty, OUT DWORD * pdwValue ) { // // this is a unusable integer for SCE // *pdwValue = SCE_NULL_INTEGER; // // we are asking for int, but Get may not give us that // CComVariant var; HRESULT hr = m_srpClassObj->Get(pszProperty, 0, &var, NULL, NULL); if (SUCCEEDED(hr)) { if (var.vt == VT_I4) { *pdwValue = var.lVal; } else if (var.vt == VT_UI4) { *pdwValue = var.ulVal; } else if (var.vt == VT_BOOL) { *pdwValue = (var.boolVal == VARIANT_TRUE) ? 1 : 0; } else if (var.vt == VT_EMPTY || var.vt == VT_NULL ) { *pdwValue = SCE_NO_VALUE; hr = WBEM_S_RESET_TO_DEFAULT; } } return hr; } /* Routine Description: Name: CScePropertyMgr::GetProperty Functionality: Get the property's value in boolean form. Virtual: No. Arguments: pszProperty - The property's name. pbValue - receives DWORD value. Return Value: Success: Whatever IWbemClassObject::Get returns, or WBEM_S_RESET_TO_DEFAULT if the value is not returned from the wbem object. Failure: whatever IWbemClassObject::Get returns. Notes: Caller must not pass NULL for the out parameter. */ HRESULT CScePropertyMgr::GetProperty ( IN LPCWSTR pszProperty, OUT bool *pbValue ) { *pbValue = false; CComVariant var; HRESULT hr = m_srpClassObj->Get(pszProperty, 0, &var, NULL, NULL); if (var.vt == VT_BOOL) { *pbValue = (var.boolVal == VARIANT_TRUE) ? true : false; } else if (var.vt == VT_EMPTY || var.vt == VT_NULL ) { *pbValue = false; hr = WBEM_S_RESET_TO_DEFAULT; } return hr; } /* Routine Description: Name: CScePropertyMgr::GetProperty Functionality: Get the property's value in string list form (SCE specific) Virtual: No. Arguments: pszProperty - The property's name. strList - receives names list. Return Value: Success: Whatever IWbemClassObject::Get returns, or WBEM_S_RESET_TO_DEFAULT if the value is not returned from the wbem object. Failure: whatever IWbemClassObject::Get returns. Notes: Caller must not pass NULL for the out parameter. */ HRESULT CScePropertyMgr::GetProperty ( IN LPCWSTR pszProperty, OUT PSCE_NAME_LIST * strList ) { *strList = NULL; CComVariant var; HRESULT hr = m_srpClassObj->Get(pszProperty, 0, &var, NULL, NULL); if (SUCCEEDED(hr)) { if ( var.vt == (VT_BSTR | VT_ARRAY) ) { // // walk the array // if( var.parray ) { LONG lDimension = 1; LONG lLowerBound = 0; LONG lUpperBound = 0; BSTR bstrElement = NULL; SafeArrayGetLBound ( var.parray , lDimension , &lLowerBound ) ; SafeArrayGetUBound ( var.parray , lDimension , &lUpperBound ) ; for ( LONG lIndex = lLowerBound ; lIndex <= lUpperBound ; lIndex++ ) { ::SafeArrayGetElement ( var.parray , &lIndex , &bstrElement ) ; if ( bstrElement ) { // // add it to the list // SCESTATUS rc = SceAddToNameList(strList, bstrElement, 0); ::SysFreeString(bstrElement); bstrElement = NULL; if ( rc != SCESTATUS_SUCCESS ) { // // SCE returned errors needs to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); break; } } } } } else if (var.vt == VT_EMPTY || var.vt == VT_NULL ) { hr = WBEM_S_RESET_TO_DEFAULT; } } if ( FAILED(hr) && *strList ) { SceFreeMemory(*strList, SCE_STRUCT_NAME_LIST); *strList = NULL; } return hr; } /* Routine Description: Name: CScePropertyMgr::GetExpandedPath Functionality: Given a path's property name, we will get the path property and expand it, if necessary, and then pass back the expanded path to the caller. Virtual: No. Arguments: pszPathName - Property name for the path. pbstrExpandedPath - receives the expanded path. pbIsDB - Confirms if this is a database (.sdb) path or not. Return Value: Success: Various success code. Failure: various error code. Any of them indicates failure to get the property and expanded it. Notes: Caller must not pass NULL for the out parameters. */ HRESULT CScePropertyMgr::GetExpandedPath ( IN LPCWSTR pszPathName, OUT BSTR * pbstrExpandedPath, OUT BOOL * pbIsDB ) { if (pbstrExpandedPath == NULL || pbIsDB == NULL) { return WBEM_E_INVALID_PARAMETER; } *pbstrExpandedPath = NULL; *pbIsDB = false; CComBSTR bstrPath; HRESULT hr = GetProperty(pszPathName, &bstrPath); if (SUCCEEDED(hr) && hr != WBEM_S_RESET_TO_DEFAULT) { hr = CheckAndExpandPath(bstrPath, pbstrExpandedPath, pbIsDB); } else { hr = WBEM_E_NOT_AVAILABLE; } return hr; } //======================================================================================== // implementation of CSceStore /* Routine Description: Name: CSceStore::CSceStore Functionality: constructor. Trivial Virtual: No. Arguments: None. Return Value: none Notes: Consider initializing additional members if you need to add them. */ CSceStore::CSceStore() : m_SceStoreType(SCE_STORE_TYPE_INVALID) { } /* Routine Description: Name: CSceStore::SetPersistProperties Functionality: Inform the store what type of persistence context it is expected to handle. The caller calls this function to indicate that it is expected to handle persistence on behalf of this wbem object. The store path is available as the property value of the given proeprty name. Virtual: No. Arguments: pClassObj - the object. lpszPathPropertyName - path's property name. Return Value: Whatever CScePropertyMgr::GetExpandedPath returns. Notes: */ HRESULT CSceStore::SetPersistProperties ( IN IWbemClassObject * pClassObj, IN LPCWSTR lpszPathPropertyName ) { // // CScePropertyMgr helps us to access WMI object's properties // create an instance and attach the WMI object to it. // This will always succeed. // CScePropertyMgr ScePropMgr; ScePropMgr.Attach(pClassObj); // // get the expanded persist path // m_bstrExpandedPath.Empty(); // // now, we need to get the path property and see what type of store we are expectig to deal with // BOOL bIsDB = FALSE; HRESULT hr = ScePropMgr.GetExpandedPath(lpszPathPropertyName, &m_bstrExpandedPath, &bIsDB); // // cache our store type information. // m_SceStoreType = (SCE_STORE_TYPE)(bIsDB ? SCE_STORE_TYPE_CONFIG_DB : SCE_STORE_TYPE_TEMPLATE); return hr; } /* Routine Description: Name: CSceStore::SetPersistPath Functionality: Inform the store what type of persistence context it is expected to handle by directly giving the store path. Since store type is determined by the path name, this is all we need. Virtual: No. Arguments: pszPath - the given path. Return Value: Success: whatever MakeSingleBackSlashPath returns. Failure: WBEM_E_INVALID_PARAMETER or whatever CheckAndExpandPath returns or whatever MakeSingleBackSlashPath returns. Notes: */ HRESULT CSceStore::SetPersistPath ( IN LPCWSTR pszPath ) { if (pszPath == NULL || *pszPath == L'\0') { return WBEM_E_INVALID_PARAMETER; } // // clean up first // m_bstrExpandedPath.Empty(); m_SceStoreType = SCE_STORE_TYPE_INVALID; // // now expand the path if necessary // CComBSTR bstrStorePath; BOOL bIsDB = FALSE; HRESULT hr = ::CheckAndExpandPath(pszPath, &bstrStorePath, &bIsDB); if (SUCCEEDED(hr)) { hr = ::MakeSingleBackSlashPath(bstrStorePath, L'\\', &m_bstrExpandedPath); // // cache our store type // if (SUCCEEDED(hr) && m_bstrExpandedPath) { m_SceStoreType = (SCE_STORE_TYPE)(bIsDB ? SCE_STORE_TYPE_CONFIG_DB : SCE_STORE_TYPE_TEMPLATE); } } return hr; } /* Routine Description: Name: CSceStore::WriteSecurityProfileInfo Functionality: Delegate the call to Sce backend supported function in a hope to isolate persistence functionalities. See Sce API for detail. Virtual: No. Arguments: Area - Area info. ppInfoBuffer - buffer pErrlog - Error log bAppend - Whether this is appending or not. Return Value: Translated HRESULT from whatever SceWriteSecurityProfileInfo or SceAppendSecurityProfileInfo returns. Notes: See Sce API for detail. */ HRESULT CSceStore::WriteSecurityProfileInfo ( IN AREA_INFORMATION Area, IN PSCE_PROFILE_INFO ppInfoBuffer, OUT PSCE_ERROR_LOG_INFO * pErrlog, IN bool bAppend )const { HRESULT hr = WBEM_NO_ERROR; if (m_SceStoreType == SCE_STORE_TYPE_TEMPLATE) { SCESTATUS rc = SCESTATUS_SUCCESS; if (bAppend) { rc = ::SceAppendSecurityProfileInfo(m_bstrExpandedPath, Area, ppInfoBuffer, pErrlog); } else { rc = ::SceWriteSecurityProfileInfo(m_bstrExpandedPath, Area, ppInfoBuffer, pErrlog); } if ( rc != SCESTATUS_SUCCESS ) { // // SCE returned errors needs to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); } } return hr; } /* Routine Description: Name: CSceStore::SavePropertyToDB Functionality: Will save the string data into a database store. Virtual: No. Arguments: pszSection - Section name. pszKey - Key name pszData - string data Return Value: Translated HRESULT from whatever SceOpenProfile or SceSetDatabaseSetting returns. Notes: See Sce API for detail. */ HRESULT CSceStore::SavePropertyToDB ( IN LPCWSTR pszSection, IN LPCWSTR pszKey, IN LPCWSTR pszData )const { PVOID hProfile = NULL; SCESTATUS rc = ::SceOpenProfile(m_bstrExpandedPath, SCE_JET_FORMAT, &hProfile); HRESULT hr; if ( SCESTATUS_SUCCESS == rc ) { DWORD dwDataSize = (pszData != NULL) ? wcslen(pszData) * sizeof(*pszData) : 0; rc = ::SceSetDatabaseSetting( hProfile, SCE_ENGINE_SMP, (PWSTR)pszSection, // these casting are caused by SceSetDatabaseSetting prototyping errors (PWSTR)pszKey, // prototyping errors (PWSTR)pszData, // prototyping errors dwDataSize); // // SCE returned errors needs to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); ::SceCloseProfile(&hProfile); } else { // // SCE returned errors needs to be translated to HRESULT. // hr = ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); } return hr; } /* Routine Description: Name: CSceStore::SavePropertyToStore Functionality: Will save the string data into a store. This is store neutral call. Virtual: No. Arguments: pszSection - Section name. pszKey - Key name pszValue - string data Return Value: Translated HRESULT from whatever WritePrivateProfileSection/ WritePrivateProfileString or SavePropertyToDB returns. Notes: See Sce API for detail. There is a very legacy (rooted at .INF) problem: save and delete uses the same function. its behavior depends on the parameters passed in. This is confusing at least. */ HRESULT CSceStore::SavePropertyToStore ( IN LPCWSTR pszSection, IN LPCWSTR pszKey, IN LPCWSTR pszValue )const { if ( wcslen(m_bstrExpandedPath) == 0 || pszSection == NULL ) { return WBEM_E_INVALID_PARAMETER; } else if (m_SceStoreType == SCE_STORE_TYPE_STREAM) { // //we don't not supporting custom persistence yet // return WBEM_E_NOT_SUPPORTED; } HRESULT hr = WBEM_E_NOT_SUPPORTED; if (m_SceStoreType == SCE_STORE_TYPE_CONFIG_DB) { hr = SavePropertyToDB(pszSection, pszKey, pszValue); } else if (m_SceStoreType == SCE_STORE_TYPE_TEMPLATE) { hr = WBEM_NO_ERROR; BOOL bWriteResult = FALSE; if ( pszKey == NULL ) { // // delete the key // bWriteResult = ::WritePrivateProfileSection(pszSection, NULL, m_bstrExpandedPath); } else { // // may be deleting the (key, value) if pszValue == NULL // bWriteResult = ::WritePrivateProfileString(pszSection, pszKey, pszValue, m_bstrExpandedPath); } if (!bWriteResult) { // // GetLastError() eeds to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(GetLastError()); } } return hr; } /* Routine Description: Name: CSceStore::SavePropertyToStore Functionality: Will save the DWORD data property into a store. This is store neutral call. Virtual: No. Arguments: pszSection - Section name. pszKey - Key name dwData - DWORD data Return Value: if error happens before writing. WBEM_E_NOT_SUPPORTED, WBEM_E_INVALID_PARAMETER and WBEM_E_OUT_OF_MEMORY If writing is attempted, then Translated HRESULT from whatever WritePrivateProfileSection/WritePrivateProfileString or SavePropertyToDB returns. Notes: See MSDN for INF file API's. There is a very legacy (rooted at .INF) problem: save and delete uses the same function. its behavior depends on the parameters passed in. This is confusing at least. */ HRESULT CSceStore::SavePropertyToStore ( IN LPCWSTR pszSection, IN LPCWSTR pszKey, IN DWORD dwData )const { if ( wcslen(m_bstrExpandedPath) == 0 || pszSection == NULL ) { return WBEM_E_INVALID_PARAMETER; } else if (m_SceStoreType == SCE_STORE_TYPE_STREAM) { // //we don't not supporting custom persistence yet // return WBEM_E_NOT_SUPPORTED; } LPCWSTR pszData = NULL; WCHAR wchData[MAX_INT_LENGTH]; // // need to format pszData to write // if (pszKey != NULL && dwData != SCE_NO_VALUE) { // // this is safe even though prefast will complain // swprintf(wchData, L"%d", dwData); pszData = wchData; } HRESULT hr = WBEM_E_NOT_SUPPORTED; // // if it is saving to database store // if (m_SceStoreType == SCE_STORE_TYPE_CONFIG_DB) { hr = SavePropertyToDB(pszSection, pszKey, pszData); } else if (m_SceStoreType == SCE_STORE_TYPE_TEMPLATE) { BOOL bWriteResult = FALSE; if ( pszKey == NULL ) { // // delete the section // bWriteResult = ::WritePrivateProfileSection(pszSection, NULL, m_bstrExpandedPath); } else { // // set data, might be deleting when pszData == NULL // bWriteResult = ::WritePrivateProfileString(pszSection, pszKey, pszData, m_bstrExpandedPath); } if (!bWriteResult) { // // GetLastError() eeds to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(GetLastError()); } } return hr; } /* Routine Description: Name: CSceStore::SavePropertyToStore Functionality: This is a very SCE specific save. It pretty much format the data and save. See its use for any samples. Virtual: No. Arguments: pszSection - Section name. pszKey - Key name dwData - DWORD data Return Value: if error happens before writing. WBEM_E_NOT_SUPPORTED, WBEM_E_INVALID_PARAMETER and WBEM_E_OUT_OF_MEMORY If writing is attempted, then Translated HRESULT from whatever WritePrivateProfileSection/WritePrivateProfileString or SavePropertyToDB returns. Notes: See MSDN for INF file API's */ HRESULT CSceStore::SavePropertyToStore ( IN LPCWSTR pszSection, IN LPCWSTR pszKey, IN DWORD dwData, IN WCHAR delim, IN LPCWSTR pszValue )const { if ( wcslen(m_bstrExpandedPath) == 0 || pszSection == NULL ) { return WBEM_E_INVALID_PARAMETER; } else if (m_SceStoreType == SCE_STORE_TYPE_STREAM) { // //we don't not supporting custom persistence yet // return WBEM_E_NOT_SUPPORTED; } LPWSTR pszData = NULL; // // need to format pszData // if (pszKey != NULL && dwData != SCE_NO_VALUE) { pszData = new WCHAR[MAX_INT_LENGTH + 1 + wcslen(pszValue) + 1]; if (pszData == NULL) { return WBEM_E_OUT_OF_MEMORY; } if ( pszValue ) { swprintf(pszData, L"%d%c%s", dwData, delim, pszValue); } else { swprintf(pszData, L"%d", dwData); } } HRESULT hr = WBEM_E_NOT_SUPPORTED; if (m_SceStoreType == SCE_STORE_TYPE_CONFIG_DB) { hr = SavePropertyToDB(pszSection, pszKey, pszData); } else if (m_SceStoreType == SCE_STORE_TYPE_TEMPLATE) { BOOL bWriteResult = FALSE; if ( pszKey == NULL ) { // // delete the section // bWriteResult = ::WritePrivateProfileSection(pszSection, NULL, m_bstrExpandedPath); } else { // // when ( dwData == SCE_NO_VALUE ) we will delete the key, that is accomplished by pszData == NULL // bWriteResult = ::WritePrivateProfileString(pszSection, pszKey, pszData, m_bstrExpandedPath); } if (!bWriteResult) { // // GetLastError() eeds to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(GetLastError()); } } delete [] pszData; return hr; } /* Routine Description: Name: CSceStore::WriteAttachmentSection Functionality: SCE backend won't be able to import an INF template for non-native sections unless there is a entry in its Attachments section. This is to write (for a particular non-native section) such an entry. Virtual: No. Arguments: pszKey - The section name. pszData - value for the key. This is not pretty much ignored for now. Return Value: Success: WBEM_NO_ERROR Failure: Translated HRESULT from whatever WritePrivateProfileString returns. Notes: (1) See MSDN for INF file API's. (2) This is No-Op for database store. */ HRESULT CSceStore::WriteAttachmentSection ( IN LPCWSTR pszKey, IN LPCWSTR pszData )const { HRESULT hr = WBEM_NO_ERROR; if (m_SceStoreType == SCE_STORE_TYPE_TEMPLATE) { BOOL bWriteResult = ::WritePrivateProfileString(pAttachmentSections, pszKey, pszData, m_bstrExpandedPath); if (!bWriteResult) { // // GetLastError() eeds to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(GetLastError()); } } return hr; } /* Routine Description: Name: CSceStore::DeleteSectionFromStore Functionality: remove the entire section. Virtual: No. Arguments: pszSection - The section name. Must NOT be NULL. Return Value: Success: WBEM_NO_ERROR. Failure: Whatever SavePropertyToStore or WriteAttachmentSection returns. Notes: */ HRESULT CSceStore::DeleteSectionFromStore ( IN LPCWSTR pszSection )const { HRESULT hr = SavePropertyToStore(pszSection, (LPCWSTR)NULL, (LPCWSTR)NULL); // // we should also delete the attachment entry because all are gone now. // if (SUCCEEDED(hr)) { hr = WriteAttachmentSection(pszSection, (LPCWSTR)NULL); } return hr; } /* Routine Description: Name: CSceStore::GetPropertyFromStore Functionality: Get the named property value into a string buffer. This is store neutral. Virtual: No. Arguments: pszSection - The section name. Must NOT be NULL. pszKey - The key name. ppszBuffer - Receiving heap allocated memory containing the string. Must NOT be NULL. pdwRead - receives information about how many bytes we have read. Must NOT be NULL. Return Value: Success: WBEM_NO_ERROR Failure: Translated HRESULT from whatever GetPrivateProfileString returns. Notes: (1) caller must free memory allocated by this function */ HRESULT CSceStore::GetPropertyFromStore ( IN LPCWSTR pszSection, IN LPCWSTR pszKey, IN LPWSTR * ppszBuffer, IN DWORD * pdwRead )const { if ( wcslen(m_bstrExpandedPath) == 0 || pszSection == NULL || ppszBuffer == NULL || pdwRead == NULL ) { return WBEM_E_INVALID_PARAMETER; } *pdwRead = 0; *ppszBuffer = NULL; HRESULT hr = WBEM_E_NOT_SUPPORTED; if (m_SceStoreType == SCE_STORE_TYPE_CONFIG_DB) { hr = GetPropertyFromDB(pszSection, pszKey, ppszBuffer, pdwRead); } else if (m_SceStoreType == SCE_STORE_TYPE_STREAM) { // //we don't not supporting custom persistence yet // return WBEM_E_NOT_SUPPORTED; } else if (m_SceStoreType == SCE_STORE_TYPE_TEMPLATE) { int TotalLength = MAX_PATH; *ppszBuffer = new WCHAR[TotalLength]; if (ppszBuffer == NULL) { hr = WBEM_E_OUT_OF_MEMORY; } else { // // try to read the buffer out // while (true) { *pdwRead = ::GetPrivateProfileString(pszSection, pszKey, NULL, *ppszBuffer, TotalLength, m_bstrExpandedPath); if (*pdwRead > 0 && *pdwRead < TotalLength - 1) { // // everything is read out // hr = WBEM_NO_ERROR; break; } else if (*pdwRead > 0) { // // buffer is most likely truncated, will try to continue unless out of memory // delete [] *ppszBuffer; if (TotalLength < 0x00010000) { // // if TotalLength is small enough, we will double its length // TotalLength *= 2; } else { // // if TotalLength is already big, we will try to add 0x00100000 more bytes // TotalLength += 0x00010000; } *ppszBuffer = new WCHAR[TotalLength]; if (*ppszBuffer == NULL) { *pdwRead = 0; hr = WBEM_E_OUT_OF_MEMORY; break; } } else { // // *pdwRead == 0 // // // GetLastError() needs to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(GetLastError()); // // if no error is encountered, then we will simply say we can't find the property // if (SUCCEEDED(hr)) { hr = WBEM_E_NOT_FOUND; } break; } } } // // if we say we failed, then better not return any valid string pointer to caller // if (FAILED(hr) && *ppszBuffer) { delete [] *ppszBuffer; *ppszBuffer = NULL; } } return hr; } /* Routine Description: Name: CSceStore::GetPropertyFromDB Functionality: Get the named property's value into a string buffer. This is database store specific. Virtual: No. Arguments: pszSection - The section name. Must NOT be NULL. pszKey - The key name. ppszBuffer - Receiving heap allocated memory containing the string. Must NOT be NULL. pdwRead - receives information about how many bytes we have read. Must NOT be NULL. Return Value: Success: WBEM_NO_ERROR Failure: Translated HRESULT from whatever WritePrivateProfileString returns. Notes: (1) caller must free memory allocated by this function */ HRESULT CSceStore::GetPropertyFromDB ( IN LPCWSTR pszSection, IN LPCWSTR pszKey, IN LPWSTR * ppszBuffer, IN DWORD * pdwRead )const { if ( wcslen(m_bstrExpandedPath) == 0 || pszSection == NULL ) { return WBEM_E_INVALID_PARAMETER; } else if (m_SceStoreType == SCE_STORE_TYPE_STREAM) { // //we don't not supporting custom persistence yet // return WBEM_E_NOT_SUPPORTED; } *pdwRead = 0; *ppszBuffer = NULL; HRESULT hr = WBEM_E_NOT_SUPPORTED; if (m_SceStoreType == SCE_STORE_TYPE_CONFIG_DB) { // // get the information from database // PVOID hProfile=NULL; LPWSTR pszSceBuffer = NULL; SCESTATUS rc = ::SceOpenProfile(m_bstrExpandedPath, SCE_JET_FORMAT, &hProfile); if ( SCESTATUS_SUCCESS == rc ) { rc = ::SceGetDatabaseSetting ( hProfile, SCE_ENGINE_SMP, (PWSTR)pszSection, (PWSTR)pszKey, &pszSceBuffer, // needs to be freed, use LocalFree!!! pdwRead ); ::SceCloseProfile(&hProfile); } // // SCE returned errors needs to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // hr = ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); if (SUCCEEDED(hr) && *pdwRead > 0 && pszSceBuffer != NULL) { long lLen = wcslen(pszSceBuffer); *ppszBuffer = new WCHAR[lLen + 1]; if (*ppszBuffer == NULL) { hr = WBEM_E_OUT_OF_MEMORY; } else { ::memcpy(*ppszBuffer, pszSceBuffer, sizeof(WCHAR) * (lLen + 1)); } } ::LocalFree(pszSceBuffer); } return hr; } /* Routine Description: Name: CSceStore::GetSecurityProfileInfo Functionality: Delegate the call to SceGetSecurityProfileInfo in an effort to hide the sce persistence detail from caller. Virtual: No. Arguments: Area - Area of the profile section. ppInfo - Receiving the profile info. pErrlog - Receiving the error log info. Return Value: Success: WBEM_NO_ERROR Failure: Translated HRESULT from whatever SceOpenProfile/SceGetSecurityProfileInfo returns. Notes: (1) This is very SCE specific function. (2) Caller must remember to call FreeSecurityProfileInfo to free the resource allocated by this function through ppInfo. (3) See SCE API for detail. */ HRESULT CSceStore::GetSecurityProfileInfo ( IN AREA_INFORMATION Area, OUT PSCE_PROFILE_INFO * ppInfo, OUT PSCE_ERROR_LOG_INFO * pErrlog )const { if (ppInfo == NULL) { return WBEM_E_INVALID_PARAMETER; } *ppInfo = NULL; SCETYPE ProfileType = (m_SceStoreType == SCE_INF_FORMAT) ? SCE_ENGINE_SCP : SCE_ENGINE_SMP; PVOID hProfile = NULL; SCE_FORMAT_TYPE SceFormatType = SCE_INF_FORMAT; if (m_SceStoreType == SCE_STORE_TYPE_CONFIG_DB) { SceFormatType = SCE_JET_FORMAT; } SCESTATUS rc = SceOpenProfile(m_bstrExpandedPath, SceFormatType, &hProfile); if ( rc != SCESTATUS_SUCCESS ) { // // SCE returned errors needs to be translated to HRESULT. // return ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); } rc = SceGetSecurityProfileInfo(hProfile, ProfileType, Area, ppInfo, pErrlog ); SceCloseProfile( &hProfile ); if ( rc != SCESTATUS_SUCCESS || *ppInfo == NULL ) { // // SCE returned errors needs to be translated to HRESULT. // return ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); } return WBEM_NO_ERROR; } /* Routine Description: Name: CSceStore::GetObjectSecurity Functionality: Delegate the call to SceGetObjectSecurity in an effort to hide the sce persistence detail from caller. Virtual: No. Arguments: Area - Area of the profile section. pszObjectName - Name of the object. ppObjSecurity - Receiving the security object. Return Value: Success: WBEM_NO_ERROR Failure: Translated HRESULT from whatever SceOpenProfile/SceGetObjectSecurity returns. Notes: (1) This is very SCE specific function. (2) Caller must remember to call FreeObjectSecurit to free the resource allocated by this function through ppObjSecurity. (3) See SCE API for detail. */ HRESULT CSceStore::GetObjectSecurity ( IN AREA_INFORMATION Area, IN LPCWSTR pszObjectName, IN PSCE_OBJECT_SECURITY * ppObjSecurity )const { if (ppObjSecurity == NULL) { return WBEM_E_INVALID_PARAMETER; } *ppObjSecurity = NULL; SCETYPE ProfileType = (m_SceStoreType == SCE_INF_FORMAT) ? SCE_ENGINE_SCP : SCE_ENGINE_SMP; PVOID hProfile = NULL; // // let's assume INF format // SCE_FORMAT_TYPE SceFormatType = SCE_INF_FORMAT; if (m_SceStoreType == SCE_STORE_TYPE_CONFIG_DB) { SceFormatType = SCE_JET_FORMAT; } SCESTATUS rc = ::SceOpenProfile(m_bstrExpandedPath, SceFormatType, &hProfile); if ( rc != SCESTATUS_SUCCESS ) { // // SCE returned errors needs to be translated to HRESULT. // return ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); } // // the following cast (to PWSTR) is caused by a mistake in the prototype of SceGetObjectSecurity // rc = ::SceGetObjectSecurity ( hProfile, ProfileType, Area, (PWSTR)pszObjectName, ppObjSecurity ); ::SceCloseProfile( &hProfile ); if ( rc != SCESTATUS_SUCCESS || *ppObjSecurity == NULL ) { // // SCE returned errors needs to be translated to HRESULT. // In case this is not an error, hr will be assigned to WBEM_NO_ERROR // return ProvDosErrorToWbemError(ProvSceStatusToDosError(rc)); } return WBEM_S_NO_ERROR; } //========================================================================================= // CScePersistMgr implementation //========================================================================================= /* Routine Description: Name: CScePersistMgr::CScePersistMgr Functionality: Constructor. trivial since all our current members will automatically create themselves. Virtual: No. Arguments: None. Return Value: none Notes: Consider initializing your members if you add more non-self-constructing members.. */ CScePersistMgr::CScePersistMgr () { } /* Routine Description: Name: CScePersistMgr::~CScePersistMgr Functionality: Destructor. Just do a cleanup. Virtual: No. Arguments: None. Return Value: none Notes: Consider adding cleanup code if you add more non-self-destructing members.. */ CScePersistMgr::~CScePersistMgr () { } /* Routine Description: Name: CScePersistMgr::Attach Functionality: Attach an object (that knows how to provide properties) to this persistence manager so that it knows where to get data for persistence. Any use of this class without a successfuly attaching object will cause catastrophic failure. This function is the first thing you need to do with CScePersistMgr object. Virtual: Yes. (function of IScePersistMgr) Arguments: guid - the in-coming interface pointer (pObj) interface guid. Currently, we only support IID_ISceClassObject. pObj - the attaching object's interface pointer. Currently, we only take ISceClassObject*. Return Value: Success: S_OK. Failure: (1) E_NOINTERFACE if the supplied interface is not supported. (2) E_INVALIDARG if pObj is NULL. Notes: Currently, we only work with ISceClassObject interface. */ STDMETHODIMP CScePersistMgr::Attach ( IN REFIID guid, // [in] IN IUnknown * pObj // [iid_is][in] ) { if (pObj == NULL) { return E_INVALIDARG; } if (guid == IID_ISceClassObject) { return pObj->QueryInterface(IID_ISceClassObject, (void**)&m_srpObject); } else { return E_NOINTERFACE; } } /* Routine Description: Name: CScePersistMgr::Save Functionality: Save an attached instance into a store. Virtual: Yes. (function of IScePersistMgr) Arguments: None. Return Value: Success: successful code (use SUCCEEDED(hr) to test). No guarantee the return result will be S_OK. Failure: (1) E_UNEXPECTED no successful attachment was ever done. (2) Other error code. Notes: Since this is a regular COM server interface function, we will in most cases return well known COM HRESULTs instead of WMI specific HRESULTs. However, this is no WMI specific, be prepared to see your result results in WMI specific HRESULTs. */ STDMETHODIMP CScePersistMgr::Save () { HRESULT hr = S_OK; if (m_srpObject == NULL) { return E_UNEXPECTED; } else if (FAILED(hr = m_srpObject->Validate())) { return hr; } DWORD dwDump; // // need to set the store path and we must have a store path. // CComBSTR bstrPersistPath; CComBSTR bstrExpandedPath; hr = m_srpObject->GetPersistPath(&bstrPersistPath); if (FAILED(hr)) { return hr; } // // Prepare a store (for persistence) for this store path (file) // CSceStore SceStore; hr = SceStore.SetPersistPath(bstrPersistPath); if (FAILED(hr)) { return hr; } // // For a new .inf file. Write an empty buffer to the file // will creates the file with right header/signature/unicode format // this is harmless for existing files. // For database store, this is a no-op. // hr = SceStore.WriteSecurityProfileInfo ( AreaBogus, (PSCE_PROFILE_INFO)&dwDump, NULL, false // not appending ); // // now, we need to form a section, which will be the class name // CComBSTR bstrSectionName; hr = GetSectionName(&bstrSectionName); if (FAILED(hr)) { return hr; } // // this is necessary for any extension class // SCE doesn't care about the value, so, we are using a static value here // hr = SceStore.WriteAttachmentSection(bstrSectionName, pszAttachSectionValue); if (FAILED(hr)) { return hr; } // // now we need to get all key property names to form a unique key for the section // DWORD dwCookie = INVALID_COOKIE; CComBSTR bstrCompoundKey; hr = GetCompoundKey(&bstrCompoundKey); if (FAILED(hr)) { return hr; } // // now create the current list of instances in this store for the class // so that we can add the new one (its cookie). // Extension classes are identified by their instance cookies. // CExtClassInstCookieList clsInstCookies; hr = clsInstCookies.Create(&SceStore, bstrSectionName, GetKeyPropertyNames(NULL, NULL)); if (SUCCEEDED(hr)) { // // add this class to the cookie list // // // We are adding the compound key and potentially requesting (by INVALID_COOKIE) a new cookie. // hr = clsInstCookies.AddCompKey(bstrCompoundKey, INVALID_COOKIE, &dwCookie); if (SUCCEEDED(hr)) { // // save the new list of instances. // key properties are save with cookie list. // hr = clsInstCookies.Save(&SceStore, bstrSectionName); } } // // non-key properties // if (SUCCEEDED(hr)) { hr = SaveProperties(&SceStore, dwCookie, bstrSectionName); } return hr; } /* Routine Description: Name: CScePersistMgr::Load Functionality: Loading instance(s) from a store. Depending on attached object (which may or may not have complete key information), this load may be a single instanace loading if complete key is available, or a multiple instance loading (if no complete key is available). Virtual: Yes. (function of IScePersistMgr) Arguments: bstrStorePath - the path to the store. pHandler - COM interface pointer to notify WMI if instance(s) are loaded. Return Value: Success: successful code (use SUCCEEDED(hr) to test). No guarantee the return result will be S_OK. Failure: (1) E_UNEXPECTED no successful attachment was ever done. (2) WBEM_E_NOT_FOUND if there is no such instance. (2) Other error code. Notes: Since this is a regular COM server interface function, we will in most cases return well known COM HRESULTs instead of WMI specific HRESULTs. However, this is no WMI specific, be prepared to see your result results in WMI specific HRESULTs. */ STDMETHODIMP CScePersistMgr::Load ( IN BSTR bstrStorePath, IN IWbemObjectSink * pHandler ) { HRESULT hr = S_OK; if (m_srpObject == NULL) { return E_UNEXPECTED; } else if (FAILED(hr = m_srpObject->Validate())) { return hr; } CComBSTR bstrSectionName; // // we have an attached object, we must also know the section // (actually, as it is now, it's the name of the class) // hr = GetSectionName(&bstrSectionName); if (FAILED(hr)) { return hr; } // // Prepare a store (for persistence) for this store path (file) // CSceStore SceStore; SceStore.SetPersistPath(bstrStorePath); // // Need to know what instances (their cookies) are present in the store for this class. // CExtClassInstCookieList clsInstCookies; hr = clsInstCookies.Create(&SceStore, bstrSectionName, GetKeyPropertyNames(NULL, NULL)); if (FAILED(hr)) { return hr; } // // get this attached object's cookie if possible (in non-querying loading) // DWORD dwCookie; CComBSTR bstrCompoundKey; // // it returns WBEM_S_FALSE if no compound key can be returned. // hr = GetCompoundKey(&bstrCompoundKey); // // if we can't create a complete compound cookie, then we are querying // if (hr == WBEM_S_FALSE) { // // we will track first error during querying // HRESULT hrFirstError = WBEM_NO_ERROR; DWORD dwResumeHandle = 0; CComBSTR bstrEachCompKey; // // will try to load everything. Enumerate through the cookies... // hr = clsInstCookies.Next(&bstrEachCompKey, &dwCookie, &dwResumeHandle); while (SUCCEEDED(hr) && hr != WBEM_S_NO_MORE_DATA) { // // as long as there is more item, keep looping // if (SUCCEEDED(hr) && hr != WBEM_S_NO_MORE_DATA) { CComPtr srpNewObj; // // LoadInstance will return WBEM_S_FALSE if there is no such instance. // hr = LoadInstance(&SceStore, bstrSectionName, bstrEachCompKey, dwCookie, &srpNewObj); if (SUCCEEDED(hr) && hr != WBEM_S_FALSE) { hr = pHandler->Indicate(1, &srpNewObj); } // // we will track first error during querying // if (SUCCEEDED(hrFirstError) && FAILED(hr)) { hrFirstError = hr; } } // // prepare to be re-used // bstrEachCompKey.Empty(); // // next cookie // hr = clsInstCookies.Next(&bstrEachCompKey, &dwCookie, &dwResumeHandle); } // // if error have happened, then we will pass that error back while trying our best to query // if (FAILED(hrFirstError)) { hr = hrFirstError; } } else if (SUCCEEDED(hr)) { // // unique instance load, we can get the cookie! // ExtClassCookieIterator it; dwCookie = clsInstCookies.GetCompKeyCookie(bstrCompoundKey, &it); // // We must have a cookie since this instance is unique // if (dwCookie != INVALID_COOKIE) { CComPtr srpNewObj; // // LoadInstance will return WBEM_S_FALSE if there is no such instance. // hr = LoadInstance(&SceStore, bstrSectionName, bstrCompoundKey, dwCookie, &srpNewObj); if (SUCCEEDED(hr) && hr != WBEM_S_FALSE) { hr = pHandler->Indicate(1, &srpNewObj); } else if ( hr == WBEM_S_FALSE) { hr = WBEM_E_NOT_FOUND; } } else { hr = WBEM_E_NOT_FOUND; } } return hr; } /* Routine Description: Name: CScePersistMgr::LoadInstance Functionality: Loading a single instanace with the given cookie. Virtual: No. Arguments: pSceStore - the store. pszSectionName - the section name. pszCompoundKey - the compound key. dwCookie - the cookie. ppObj - Receives the WMI object. Return Value: Success: (1) S_OK if instance is loaded. (2) WBEM_S_FALSE if no such instance is found. Failure: (1) various error codes. Notes: (1) This is a private helper, we don't check all validity of the attached object. (2) Since this is a regular COM server interface function, we will in most cases return well known COM HRESULTs instead of WMI specific HRESULTs. However, this is no WMI specific, be prepared to see your result results in WMI specific HRESULTs. (3) This routine can be enhanced to get rid of the compound key parameter since once a cookie is found all key property information is available via the use of CCompoundKey. */ HRESULT CScePersistMgr::LoadInstance ( IN CSceStore * pSceStore, IN LPCWSTR pszSectionName, IN LPCWSTR pszCompoundKey, IN DWORD dwCookie, OUT IWbemClassObject ** ppObj ) { if (ppObj == NULL || pszCompoundKey == NULL || *pszCompoundKey == L'\0') { return E_INVALIDARG; } CComPtr srpObj; HRESULT hr = m_srpObject->GetClassObject(&srpObj); // // spawn a new instance, we won't pass it back until everything is loaded successfully // so, this is a local object at this point. // CComPtr srpNewObj; hr = srpObj->SpawnInstance(0, &srpNewObj); DWORD dwLoadedProperties = 0; if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) { // // CScePropertyMgr helps us to access WMI object's properties // create an instance and attach the WMI object to it. // This will always succeed. // CScePropertyMgr ScePropMgr; ScePropMgr.Attach(srpNewObj); // // store path is the only property not managed by the ISceClassObject // hr = ScePropMgr.PutProperty(pStorePath, pSceStore->GetExpandedPath()); if (FAILED(hr)) { return hr; } // // pszCompoundKey == pszNullKey means loading singleton (foreign object) // or an abstract method call. So, pszNullKey means no need to populate // key properties. // if (_wcsicmp(pszCompoundKey, pszNullKey) != 0) { hr = ::PopulateKeyProperties(pszCompoundKey, &ScePropMgr); } if (FAILED(hr)) { return hr; } // // now read the non-key properties and set each one // DWORD dwCount = 0; m_srpObject->GetPropertyCount(SceProperty_NonKey, &dwCount); for (int i = 0; i < dwCount; i++) { CComBSTR bstrPropName; CComBSTR bstrKey; // // get i-th property name // hr = FormatNonKeyPropertyName(dwCookie, i, &bstrKey, &bstrPropName); if (FAILED(hr)) { break; } DWORD dwRead = 0; // // need to delete this memory // LPWSTR pszBuffer = NULL; // // the property may not be present in the store. // So, ignore the result since the property may be missing in the store // if (SUCCEEDED(pSceStore->GetPropertyFromStore(pszSectionName, bstrKey, &pszBuffer, &dwRead))) { // // translate the string to a variant and set the property // CComVariant var; hr = ::VariantFromFormattedString(pszBuffer, &var); if (SUCCEEDED(hr)) { ScePropMgr.PutProperty(bstrPropName, &var); } } delete [] pszBuffer; } if (SUCCEEDED(hr)) { // // give it to the out-bound parameter // *ppObj = srpNewObj.Detach(); hr = S_OK; } else { hr = WBEM_S_FALSE; // no instance loaded } } } return hr; } /* Routine Description: Name: CScePersistMgr::Delete Functionality: Delete an instance from the store. Virtual: Yes. (function of IScePersistMgr) Arguments: bstrStorePath - the store. pHandler - COM interface to notify WMI of successful operation. Return Value: Success: (1) Various success codes. Failure: (1) E_UNEXPECTED means no successfully attached object. (1) various other error codes. Notes: */ STDMETHODIMP CScePersistMgr::Delete ( IN BSTR bstrStorePath, IN IWbemObjectSink *pHandler ) { if (m_srpObject == NULL) { return E_UNEXPECTED; } if (bstrStorePath == NULL || pHandler == NULL) { return WBEM_E_INVALID_PARAMETER; } // // we have an attached object, we must also know the section // (actually, as it is now, it's the name of the class) // CComBSTR bstrSectionName; HRESULT hr = GetSectionName(&bstrSectionName); if (SUCCEEDED(hr)) { CComBSTR bstrCompoundKey; hr = GetCompoundKey(&bstrCompoundKey); // // Prepare a store (for persistence) for this store path (file) // CSceStore SceStore; SceStore.SetPersistPath(bstrStorePath); // // if we can't create a complete compound cookie, then we are deleting everything of the class // if (hr == WBEM_S_FALSE) { // // delete the whole section // SceStore.DeleteSectionFromStore(bstrSectionName); } else if (SUCCEEDED(hr)) { // // unique instance delete // now create the instance list (cookies are enough) for the class // CExtClassInstCookieList clsInstCookies; hr = clsInstCookies.Create(&SceStore, bstrSectionName, GetKeyPropertyNames(NULL, NULL)); if (SUCCEEDED(hr)) { // // see if we are really deleting the last one? // if yes, then we can simply delete the section. // ExtClassCookieIterator it; DWORD dwCookie = clsInstCookies.GetCompKeyCookie(bstrCompoundKey, &it); if (dwCookie != INVALID_COOKIE && clsInstCookies.GetCookieCount() == 1) { // // Yes, there is only one cookie and this is the one, so everything is gone // SceStore.DeleteSectionFromStore(bstrSectionName); } else { // // remove the instance's key properties from the in-memory cookie list // dwCookie = clsInstCookies.RemoveCompKey(&SceStore, bstrSectionName, bstrCompoundKey); // // save the new list, this will effectively remove the instance's key properties // from the store. Key properties are saved as part of the cookie list // hr = clsInstCookies.Save(&SceStore, bstrSectionName); // // now, delete non-key the properties // if (SUCCEEDED(hr)) { hr = DeleteAllNonKeyProperties(&SceStore, dwCookie, bstrSectionName); } } } } else { hr = WBEM_E_NOT_FOUND; } } // // Inform WMI that the action is complete // pHandler->SetStatus(WBEM_STATUS_COMPLETE, hr, NULL, NULL); return hr; } /* Routine Description: Name: CScePersistMgr::SaveProperties Functionality: Saves the non-key properties. Key properties are saved as part of the instance cookie list. Virtual: No. Arguments: pSceStore - the store. dwCookie - the instance cookie. pszSection - the section name. Return Value: Success: (1) Various success codes. Failure: (1) various error codes. Notes: This is private helper. No checking against attached object's validity. */ HRESULT CScePersistMgr::SaveProperties ( IN CSceStore * pSceStore, IN DWORD dwCookie, IN LPCWSTR pszSection ) { DWORD dwCount = 0; HRESULT hr = m_srpObject->GetPropertyCount(SceProperty_NonKey, &dwCount); if (SUCCEEDED(hr)) { for (DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++) { // // This is what is used to identify the property in the store. // When you save a property, use this name. // CComBSTR bstrStorePropName; // // real name of the property // CComBSTR bstrTrueName; // // get the dwIndex-th non-key property names // hr = FormatNonKeyPropertyName(dwCookie, dwIndex, &bstrStorePropName, &bstrTrueName); if (FAILED(hr)) { break; } BSTR bstrData = NULL; // // get the dwIndex-th non-key property value in string format! // hr = FormatPropertyValue(SceProperty_NonKey, dwIndex, &bstrData); // // if a property is not present, we are fine with that. // if (SUCCEEDED(hr) && bstrData != NULL) { hr = pSceStore->SavePropertyToStore(pszSection, bstrStorePropName, bstrData); ::SysFreeString(bstrData); } if (FAILED(hr)) { break; } } } return hr; } /* Routine Description: Name: CScePersistMgr::DeleteAllNonKeyProperties Functionality: Delete the non-key properties. Key properties are saved/delete as part of the instance cookie list. Virtual: No. Arguments: pSceStore - the store. dwCookie - the instance cookie. pszSection - the section name. Return Value: Success: (1) Various success codes. Failure: (1) various error codes. Notes: This is private helper. No checking against attached object's validity. */ HRESULT CScePersistMgr::DeleteAllNonKeyProperties ( IN CSceStore * pSceStore, IN DWORD dwCookie, IN LPCWSTR pszSection ) { DWORD dwCount = 0; HRESULT hr = m_srpObject->GetPropertyCount(SceProperty_NonKey, &dwCount); HRESULT hrDelete = WBEM_NO_ERROR; if (SUCCEEDED(hr)) { for (DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++) { // // This is what is used to identify the property in the store. // When you save a property, use this name. // CComBSTR bstrStorePropName; CComBSTR bstrTrueName; hr = FormatNonKeyPropertyName(dwCookie, dwIndex, &bstrStorePropName, &bstrTrueName); if (FAILED(hr)) { break; } // // we will delete the property. // In case of error, such deletion will continue, but the error will be reported // hr = pSceStore->DeletePropertyFromStore(pszSection, bstrStorePropName); if (FAILED(hr)) { hrDelete = hr; } } } return FAILED(hrDelete) ? hrDelete : hr; } /* Routine Description: Name: CScePersistMgr::FormatNonKeyPropertyName Functionality: Given a non-key property index, get the property's real name and its instance-specific name inside a store. Currently, its name inside a store has the cookie's number prefixed to guarantee its uniquess inside a section. This is a result of the .INF file format limitations. Virtual: No. Arguments: dwCookie - the instance cookie. dwIndex - the index of the property. pbstrStorePropName - The property's name inside the store for the instance pbstrTrueName - The real name of the property Return Value: Success: (1) Various success codes. Failure: (1) various error codes. Notes: This is private helper. No checking against attached object's validity. */ HRESULT CScePersistMgr::FormatNonKeyPropertyName ( IN DWORD dwCookie, IN DWORD dwIndex, OUT BSTR * pbstrStorePropName, OUT BSTR * pbstrTrueName ) { if (pbstrStorePropName == NULL || pbstrTrueName == NULL) { return WBEM_E_INVALID_PARAMETER; } *pbstrStorePropName = NULL; *pbstrTrueName = NULL; // // We are only interested in the name, not the value // HRESULT hr = m_srpObject->GetPropertyValue(SceProperty_NonKey, dwIndex, pbstrTrueName, NULL); if (SUCCEEDED(hr)) { int iNameLen = wcslen(*pbstrTrueName); *pbstrStorePropName = ::SysAllocStringLen(NULL, MAX_INT_LENGTH + iNameLen + 1); if (*pbstrStorePropName == NULL) { hr = WBEM_E_OUT_OF_MEMORY; } else { // // prefix the real name with the cookie number. // wsprintf(*pbstrStorePropName, L"%d%s", dwCookie, *pbstrTrueName); } } if (FAILED(hr)) { // // bstr might have been allocated. Free them // if (*pbstrTrueName != NULL) { ::SysFreeString(*pbstrTrueName); *pbstrTrueName = NULL; } if (*pbstrStorePropName != NULL) { ::SysFreeString(*pbstrStorePropName); *pbstrStorePropName = NULL; } } return hr; } /* Routine Description: Name: CScePersistMgr::FormatPropertyValue Functionality: Given the type (key or non-key) of and its index (of the property), get the property's value in string format. This works for both key and non-key properties. Virtual: No. Arguments: type - what type of property (key or non-key). dwIndex - the index of the property. pbstrValue - receives the value in string format Return Value: Success: (1) Various success codes. Failure: (1) various error codes. Notes: This is private helper. No checking against attached object's validity. */ HRESULT CScePersistMgr::FormatPropertyValue ( IN SceObjectPropertyType type, IN DWORD dwIndex, OUT BSTR * pbstrValue ) { CComBSTR bstrName; CComVariant varValue; HRESULT hr = m_srpObject->GetPropertyValue(type, dwIndex, &bstrName, &varValue); *pbstrValue = NULL; if (SUCCEEDED(hr) && (varValue.vt != VT_EMPTY && varValue.vt != VT_NULL)) { hr = ::FormatVariant(&varValue, pbstrValue); } return hr; } /* Routine Description: Name: CScePersistMgr::GetCompoundKey Functionality: Given the type (key or non-key) of and its index (of the property), get the property's value in string format. This works for both key and non-key properties. Virtual: No. Arguments: pbstrKey - receives compound key in string format. See header file for format explanation. Return Value: Success: (1) WBEM_S_FALSE if information doesn't give complete key properties. (2) WBEM_NO_ERROR if the compond key is successfully generated. Failure: (1) various error codes. Notes: This is private helper. No checking against attached object's validity. */ HRESULT CScePersistMgr::GetCompoundKey ( OUT BSTR* pbstrKey ) { if (pbstrKey == NULL) { return WBEM_E_INVALID_PARAMETER; } *pbstrKey = NULL; DWORD dwCount = 0; HRESULT hr = m_srpObject->GetPropertyCount(SceProperty_Key, &dwCount); if (SUCCEEDED(hr) && dwCount == 0) { // // no key, must be singleton/static, thus no compound key // *pbstrKey = ::SysAllocString(pszNullKey); return WBEM_S_FALSE; } if (SUCCEEDED(hr)) { // // these are the individual key property's format string // CComBSTR *pbstrKeyProperties = new CComBSTR[dwCount]; if (pbstrKeyProperties == NULL) { return WBEM_E_OUT_OF_MEMORY; } DWORD dwTotalLen = 0; // // for each key property, we will format the (prop, value) pair // into prop format // for (DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++) { CComVariant var; // // put the property name to pbstrKeyProperties[dwIndex] first. // hr = m_srpObject->GetPropertyValue(SceProperty_Key, dwIndex, &pbstrKeyProperties[dwIndex], &var); if (FAILED(hr) || var.vt == VT_NULL || var.vt == VT_EMPTY) { // // will quit because we don't have enough information, // but just that we can't find the key property value. // We'd like to be more specific about the error. However, we have observed that // WMI will return inconsistent code for not finding the property. Sometimes it // simply returns success but without a value in the variant. But other times, it // returns errors. We have to treat it as if the property is not present in the object. // hr = WBEM_S_FALSE; break; } // // put the variant's value in string format (like ) // CComBSTR bstrData; hr = ::FormatVariant(&var, &bstrData); if (SUCCEEDED(hr)) { // // append the value to pbstrKeyProperties[dwIndex] so that // pbstrKeyProperties[dwIndex] all have this format: PropName // pbstrKeyProperties[dwIndex] += bstrData; } else { break; } // // so that we can re-use it // bstrData.Empty(); // // keep track of total length // dwTotalLen += wcslen(pbstrKeyProperties[dwIndex]); } // // hr == WBEM_S_FALSE indicates no compound key // if (SUCCEEDED(hr) && hr != WBEM_S_FALSE) { // // now, we are ready to generate the final buffer for the caller // 1 for the '\0' terminator // *pbstrKey = ::SysAllocStringLen(NULL, dwTotalLen + 1); if (*pbstrKey != NULL) { // // pszCur is the current point to write // LPWSTR pszCur = *pbstrKey; DWORD dwLen; // // pack each pbstrKeyProperties[dwIndex] into the final bstr // for (dwIndex = 0; dwIndex < dwCount; dwIndex++) { dwLen = wcslen(pbstrKeyProperties[dwIndex]); // // Since each pbstrKeyProperties[dwIndex] is a CComBSTR, // do some casting to remove any ambiguity. // ::memcpy(pszCur, (const void*)(LPCWSTR)(pbstrKeyProperties[dwIndex]), dwLen * sizeof(WCHAR)); // // move the current write point // pszCur += dwLen; } (*pbstrKey)[dwTotalLen] = L'\0'; } else { hr = WBEM_E_OUT_OF_MEMORY; } } // // we are done with the individual parts // delete [] pbstrKeyProperties; } // // truly successfully generated the compound key // if (SUCCEEDED(hr) && hr != WBEM_S_FALSE) { hr = WBEM_NO_ERROR; } return hr; } /* Routine Description: Name: GetKeyPropertyNames Functionality: private helper. Will get this class's key property names vector Virtual: No. Arguments: pNamespace - The provider namespace. pCtx - The context pointer that we are given and pass around for WMI API's. Return Value: Success: Non-NULL. Failure: NULL. Notes: */ std::vector* CScePersistMgr::GetKeyPropertyNames ( IN IWbemServices * pNamespace, IN IWbemContext * pCtx ) { CComBSTR bstrClassName; HRESULT hr = GetClassName(&bstrClassName); if (FAILED(hr)) { return NULL; } const CForeignClassInfo* pFCInfo = NULL; if (SUCCEEDED(hr)) { pFCInfo = g_ExtClasses.GetForeignClassInfo(pNamespace, pCtx, bstrClassName); } if (pFCInfo == NULL) { return NULL; } else { return pFCInfo->m_pVecKeyPropNames; } } // // Implementing those global helper parsing related functions. // /* Routine Description: Name: FormatVariant Functionality: Given a variant, we will get our format of string representing the variant value. For example, if pVar is of VT_I4 type and value 12345, this function will return: < VT_I4 : 12345 >; We also support arrays. For example, a safearray of bstrs that has "This is " value at its first element and "Microsoft SCE" at its second value will receive: < VT_ARRAY(VT_BSTR) : "This is " , "Microsoft SCE" >; Virtual: N/A. Arguments: pVar - The variant. pbstrData - The string (in our format) receives the formatted string. Return Value: Success: WBEM_NO_ERROR. Failure: Various errors may occurs. Most obvious is WBEM_E_INVALID_SYNTAX, WBEM_E_OUT_OF_MEMORY, WBEM_E_NOT_SUPPORTED Notes: */ HRESULT FormatVariant ( IN VARIANT * pVar, OUT BSTR * pbstrData ) { if (pVar == NULL || pbstrData == NULL) { return WBEM_E_INVALID_PARAMETER; } HRESULT hr = WBEM_NO_ERROR; *pbstrData = NULL; // // if it is not a safearray // if ( (pVar->vt & VT_ARRAY) != VT_ARRAY) { CComBSTR bstrValue; hr = ::GetStringPresentation(pVar, &bstrValue); LPCWSTR pszType = gVtToStringMap.GetTypeString(pVar->vt); if (SUCCEEDED(hr) && pszType) { // // formatted string has both type and value information // ULONG uLen = wcslen(bstrValue); ULONG uTypeLen = wcslen(pszType); ULONG uTotalLen = uLen + uTypeLen + 4; // // don't be surprised that we do the formatting this way, it turns out that // wsprintf doesn't work for strings longer than 1K // *pbstrData = ::SysAllocStringLen(NULL, uTotalLen); if (*pbstrData != NULL) { // // this is the current point to write formatted string // LPWSTR pszCurDataPtr = *pbstrData; // // put the < character and move one index // pszCurDataPtr[0] = wchTypeValLeft; ++pszCurDataPtr; // // write the type string, like VT_BSTR, and move over that many characters // memcpy(pszCurDataPtr, pszType, uTypeLen * sizeof(WCHAR)); pszCurDataPtr += uTypeLen; // // put the type and value separater, and move one index // pszCurDataPtr[0] = wchTypeValSep; ++pszCurDataPtr; // // put the value and value separater, and move over that many characters // memcpy(pszCurDataPtr, bstrValue, uLen * sizeof(WCHAR)); pszCurDataPtr += uLen; // // put the < character and move one index // pszCurDataPtr[0] = wchTypeValRight; pszCurDataPtr[1] = L'\0'; } else { hr = WBEM_E_OUT_OF_MEMORY; } } else if (SUCCEEDED(hr)) { hr = WBEM_E_NOT_SUPPORTED; } } else { hr = ::FormatArray(pVar, pbstrData); } return hr; } /* Routine Description: Name: GetObjectPath Functionality: Given a store path and an instance's compound key, this will generate the instance's path. This is to reduce WMI traffic. WMI uses paths to execute method. But we don't have path until the object is avaiable. As a result, we need to read the object out (which may contain many properties), and then get the path and feed the path to WMI to execute a method. When WMI receives that request, it again ask us for the object (using the path we gave in the above sequence) and we have to load the instance again. This is obviously too much traffic. So, to reduce the traffic, we will create the path ourselves. For each class, we have a cookie list. For each instance, we have a cookie in the cookie list. For each cookie, we know the compound key very easily (key property counts are always small). This function creates a path based on the compound key. Virtual: N/A. Arguments: pSpawn - COM interface pointer that has all the class definition. We need this to for the path creation. pszStorePath - The store's path pszCompoundKey - The instance's compound key pbstrPath - receives the path. Return Value: Success: (1) various success codes. Failure: (1) various error codes. Notes: As usual, caller is responsible for freeing the bstr. */ HRESULT GetObjectPath ( IN IWbemClassObject * pSpawn, IN LPCWSTR pszStorePath, IN LPCWSTR pszCompoundKey, OUT BSTR * pbstrPath ) { if (pSpawn == NULL || pszCompoundKey == NULL || *pszCompoundKey == L'\0' || pbstrPath == NULL ) { return WBEM_E_INVALID_PARAMETER; } *pbstrPath = NULL; // // this instance is not going to live beyond the scope of the function // CComPtr srpTempObj; HRESULT hr = pSpawn->SpawnInstance(0, &srpTempObj); DWORD dwLoadedProperties = 0; // // We will populate the key properties into this temp object and ask it for the path // if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) { CScePropertyMgr ScePropMgr; ScePropMgr.Attach(srpTempObj); // // this is the only property not managed by the ISceClassObject // hr = ScePropMgr.PutProperty(pStorePath, pszStorePath); if (FAILED(hr)) { return hr; } if (_wcsicmp(pszCompoundKey, pszNullKey) != 0) { hr = PopulateKeyProperties(pszCompoundKey, &ScePropMgr); } } } if (SUCCEEDED(hr)) { CComVariant varPath; // // a fully populated (as far as key properties are concerned) will have a path // hr = srpTempObj->Get(L"__RelPath", 0, &varPath, NULL, NULL); if (SUCCEEDED(hr) && varPath.vt == VT_BSTR) { // // this is it. // *pbstrPath = varPath.bstrVal; // // since the bstr is now owned by the out parameter, we'd better stop the auto-destruction // by CComVariant // varPath.bstrVal = NULL; varPath.vt = VT_EMPTY; } } return hr; } /* Routine Description: Name: VariantFromFormattedString Functionality: Given a formatted string representing a variant value, transform it to a variant value. For example, VariantFromFormattedString(L"", &var); will assign VT_I4 value 12345 to var. We also support arrays. For example, VariantFromFormattedString(L"", &var); will assign VT_ARRAY | VT_BSTR to var and it contains a safearray of bstrs that has "This is " value at its first element and "Microsoft SCE" at its second value. Virtual: N/A. Arguments: pszString - The string (in our format) representing a value. pVar - receives the variant value. Return Value: Success: WBEM_NO_ERROR. Failure: Various errors may occurs. Most obvious is WBEM_E_INVALID_SYNTAX, WBEM_E_OUT_OF_MEMORY, WBEM_E_NOT_SUPPORTED Notes: */ HRESULT VariantFromFormattedString ( IN LPCWSTR pszString, OUT VARIANT* pVar ) { // USES_CONVERSION; if (pszString == NULL || pVar == NULL) { return WBEM_E_INVALID_PARAMETER; } ::VariantInit(pVar); // // the current point of parsing // LPCWSTR pCur = pszString; // // scan the string for ' // if (*pNext != wchTypeValRight) { return WBEM_E_INVALID_SYNTAX; } // // length of the found token // iTokenLen = pNext - pCur; LPWSTR pszValue = new WCHAR[iTokenLen + 1]; // need to release the memory if (pszValue == NULL) { return WBEM_E_OUT_OF_MEMORY; } // // copy the token, but trim the white space at both ends // ::TrimCopy(pszValue, pCur, iTokenLen); HRESULT hr = WBEM_NO_ERROR; // // arrays not more complicated to format // if ((varVT & VT_ARRAY) != VT_ARRAY) { hr = ::VariantFromStringValue(pszValue, varVT, pVar); } else { hr = ::ArrayFromFormatString(pszValue, varSubType, pVar); } delete [] pszValue; return hr; } /* Routine Description: Name: VariantFromStringValue Functionality: Given a formatted string representing a variant value, transform it to a variant value. For example, VariantFromStringValue(L"12345", VT_I4, &var); will assign VT_I4 value 12345 to var. We do not support array at this function. That is done ArrayFromFormatString. Virtual: N/A. Arguments: szValue - The string (in our format) representing a value. vt - VARTYPE pVar - receives the variant value. Return Value: Success: WBEM_NO_ERROR. Failure: Various errors may occurs. Most obvious is WBEM_E_INVALID_PARAMETER, WBEM_E_INVALID_SYNTAX, WBEM_E_OUT_OF_MEMORY, WBEM_E_NOT_SUPPORTED Notes: */ HRESULT VariantFromStringValue ( IN LPCWSTR szValue, IN VARTYPE vt, IN VARIANT * pVar ) { // // so that we can use those W2CA like macros // USES_CONVERSION; if (szValue == NULL || *szValue == L'\0' || pVar == NULL) { return WBEM_E_INVALID_PARAMETER; } ::VariantInit(pVar); pVar->vt = vt; HRESULT hr = WBEM_NO_ERROR; switch (vt) { case VT_BSTR: hr = ::DeEscapeStringData(szValue, &(pVar->bstrVal), true); break; case VT_BOOL: if (*szValue == L'0' || _wcsicmp(szValue, L"FALSE") == 0) { pVar->boolVal = VARIANT_FALSE; } else { pVar->boolVal = VARIANT_TRUE; } break; case VT_I2: pVar->iVal = (short)(_wtol(szValue)); break; case VT_UI1: pVar->bVal = (BYTE)(_wtol(szValue)); break; case VT_UI2: pVar->uiVal = (USHORT)(_wtol(szValue)); break; case VT_UI4: pVar->ulVal = (ULONG)(_wtol(szValue)); break; case VT_I4: pVar->lVal = (long)(_wtol(szValue)); break; case VT_DATE: case VT_R4: case VT_R8: { double fValue = atof(W2CA(szValue)); if (vt == VT_DATE) { pVar->date = fValue; } else if (vt == VT_R4) { pVar->fltVal = (float)fValue; } else { pVar->dblVal = fValue; } } break; case VT_CY: hr = ::CurrencyFromFormatString(szValue, pVar); break; default: hr = WBEM_E_NOT_SUPPORTED; break; } if (FAILED(hr)) { ::VariantInit(pVar); } return hr; } /* Routine Description: Name: CurrencyFromFormatString Functionality: Given a formatted string representing a currency value, translate into a variant. Virtual: N/A. Arguments: lpszFmtStr - The string (in our format) representing currency value. pVar - receives the variant value. Return Value: Success: WBEM_NO_ERROR. Failure: Various errors may occurs. Most obvious is WBEM_E_INVALID_SYNTAX, WBEM_E_INVALID_PARAMETER Notes: */ HRESULT CurrencyFromFormatString ( IN LPCWSTR lpszFmtStr, OUT VARIANT * pVar ) { if (lpszFmtStr == NULL || pVar == NULL) { return WBEM_E_INVALID_PARAMETER; } ::VariantInit(pVar); pVar->vt = VT_CY; LPCWSTR lpszDot = lpszFmtStr; while (*lpszDot != L'\0' && *lpszDot != wchCySeparator) { ++lpszDot; } if (*lpszDot != wchCySeparator) { return WBEM_E_INVALID_SYNTAX; } else { pVar->cyVal.Lo = (short)(_wtol(lpszDot + 1)); // // _wtol won't read past the dot // pVar->cyVal.Hi = (short)(_wtol(lpszFmtStr)); } return WBEM_NO_ERROR; } /* Routine Description: Name: ArrayFromFormatString Functionality: Given a formatted string representing array value, translate into a variant. Virtual: N/A. Arguments: lpszFmtStr - The string (in our format) representing array value. vt - sub type (the array's element type) pVar - receives the variant value. Return Value: Success: WBEM_NO_ERROR. Failure: Various errors may occurs. Most obvious is WBEM_E_INVALID_SYNTAX, WBEM_E_INVALID_PARAMETER Notes: */ HRESULT ArrayFromFormatString ( IN LPCWSTR lpszFmtStr, IN VARTYPE vt, OUT VARIANT * pVar ) { if (lpszFmtStr == NULL || pVar == NULL) { return WBEM_E_INVALID_PARAMETER; } ::VariantInit(pVar); // // need to find out how many are there in the array, we will just count the delimiter wchValueSep == ',' // LPCWSTR pszCur = lpszFmtStr; // // get the count // DWORD dwCount = 1; bool bEscaped; while (pszCur = EscSeekToChar(pszCur, wchValueSep, &bEscaped, false)) { ++dwCount; ++pszCur; // skip the separator } // // we know this is the type // pVar->vt = VT_ARRAY | vt; // // create a safearray // SAFEARRAYBOUND rgsabound[1]; rgsabound[0].lLbound = 0; rgsabound[0].cElements = dwCount; pVar->parray = ::SafeArrayCreate(vt, 1, rgsabound); HRESULT hr = WBEM_NO_ERROR; if (pVar->parray == NULL) { hr = WBEM_E_OUT_OF_MEMORY; } else { long lIndecies[1]; LPCWSTR pszNext = pszCur = lpszFmtStr; long lLength = 0; // // pszValue will be used to hold each individual value. This buffer // is generous enough. Need to free the memory. // LPWSTR pszValue = new WCHAR[wcslen(lpszFmtStr) + 1]; if (pszValue == NULL) { hr = WBEM_E_OUT_OF_MEMORY; } else { // // all values are separated by the character wchValueSep // We will loop through and get each value out and put them // into the safearray // for (DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++) { pszNext = ::EscSeekToChar(pszCur, wchValueSep, &bEscaped, true); if (pszNext == pszCur) { break; } lLength = pszNext - pszCur; lIndecies[0] = dwIndex; ::TrimCopy(pszValue, pszCur, lLength); VARIANT var; hr = ::VariantFromStringValue(pszValue, vt, &var); if (FAILED(hr)) { break; } if (vt == VT_BSTR) { hr = ::SafeArrayPutElement(pVar->parray, lIndecies, var.bstrVal); } else { hr = ::SafeArrayPutElement(pVar->parray, lIndecies, ::GetVoidPtrOfVariant(var.vt, &var)); } ::VariantClear(&var); if (FAILED(hr)) { break; } pszCur = pszNext; // // we won't quit until we see the end of the buffer. // if (*pszCur != L'\0') { ++pszCur; } else { break; } } delete [] pszValue; } } if (FAILED(hr)) { ::VariantClear(pVar); } return hr; } /* Routine Description: Name: FormatArray Functionality: Given a variant, get the formatted (our format) string representation. For example, given a variant of array (say, 3 elements) of VT_I4 of value 1, 2, 3, this function will give < VT_ARRAY(VT_I4) : 1, 2, 3, > For example, given a variant of array (say, 3 elements) VT_BSTR of value "Microsoft", "Security" "Configuration", this function will give < VT_ARRAY(VT_BSTR) : "Microsoft" , "Security", "Configuration" > Virtual: N/A. Arguments: pVar - The varaint to be formatted. Must be a safearray pbstrData - Receive the formatted string. Return Value: Success: WBEM_NO_ERROR. Failure: Various errors may occurs. Most obvious is WBEM_E_INVALID_SYNTAX, WBEM_E_INVALID_PARAMETER Notes: */ HRESULT FormatArray ( IN VARIANT * pVar, OUT BSTR * pbstrData ) { if (pVar == NULL || pbstrData == NULL || (pVar->vt & VT_ARRAY) != VT_ARRAY) { return WBEM_E_INVALID_PARAMETER; } *pbstrData = NULL; VARTYPE vtSub = ::GetSubType(pVar->vt); long lLowerBound, lUpperBound; HRESULT hr = ::SafeArrayGetLBound(pVar->parray, 1, &lLowerBound); if (FAILED(hr)) { return hr; } hr = ::SafeArrayGetUBound(pVar->parray, 1, &lUpperBound); if (FAILED(hr)) { return hr; } long lIndexes[1]; // // get lUpperBound - lLowerBound + 1 bstrs and set all to NULL // BSTR * pbstrArray = new BSTR[lUpperBound - lLowerBound + 1]; if (pbstrArray == NULL) { return WBEM_E_OUT_OF_MEMORY; } ::memset(pbstrArray, 0, sizeof(BSTR) * (lUpperBound - lLowerBound + 1)); // // this is the nightmare to deal with CComBSTR's appending, its += operator // doesn't work for loops! // long lTotalLen = 0; LPWSTR pszNextToWrite = NULL; for (long i = lLowerBound; i <= lUpperBound; i++) { CComBSTR bstrValue; CComVariant var; var.vt = vtSub; // // i-th element of the array // lIndexes[0] = i; void* pv = ::GetVoidPtrOfVariant(vtSub, &var); hr = ::SafeArrayGetElement(pVar->parray, lIndexes, pv); if (FAILED(hr)) { break; } hr = ::GetStringPresentation(&var, &bstrValue); if (FAILED(hr)) { break; } int iValueLen = wcslen(bstrValue); if (i == 0) { // // This is the start of the format, we need to get the VARTYPE string. // first, put the type string. // LPCWSTR pszType = gVtToStringMap.GetTypeString(VT_ARRAY, vtSub); if (pszType == NULL) { hr = WBEM_E_NOT_SUPPORTED; break; } int iTypeLen = wcslen(pszType); pbstrArray[i] = ::SysAllocStringLen(NULL, 1 + iTypeLen + 1 + iValueLen + 2); if (pbstrArray[i] == (LPCWSTR)NULL) { hr = WBEM_E_OUT_OF_MEMORY; break; } pszNextToWrite = pbstrArray[i]; // // put the separator, move over one wchar position // pszNextToWrite[0] = wchTypeValLeft; ++pszNextToWrite; ::memcpy(pszNextToWrite, pszType, iTypeLen * sizeof(WCHAR)); // // have copied over that many WCHAR, move that many positions. // pszNextToWrite += iTypeLen; // // put the separator, move over one wchar position // pszNextToWrite[0] = wchTypeValSep; ++pszNextToWrite; ::memcpy(pszNextToWrite, (void*)((LPCWSTR)bstrValue), iValueLen * sizeof(WCHAR)); // // have copied over that many WCHAR, move that many positions. // pszNextToWrite += iValueLen; } else { // // not the first value any more // pbstrArray[i] = ::SysAllocStringLen(NULL, 1 + iValueLen + 2); if (pbstrArray[i] == (LPCWSTR)NULL) { hr = WBEM_E_OUT_OF_MEMORY; break; } pszNextToWrite = pbstrArray[i]; // // put the separator, move over one wchar position // pszNextToWrite[0] = wchValueSep; ++pszNextToWrite; ::memcpy(pszNextToWrite, (void*)((LPCWSTR)bstrValue), iValueLen * sizeof(WCHAR)); // // have copied over that many WCHAR, move that many positions. // pszNextToWrite += iValueLen; } bstrValue.Empty(); // // enclosing > // if (i == pVar->parray->rgsabound[0].cElements - 1) { pszNextToWrite[0] = wchTypeValRight; ++pszNextToWrite; } pszNextToWrite[0] = L'\0'; lTotalLen += wcslen(pbstrArray[i]); } // // now if everything is fine, pack them into one single output parameter // if (SUCCEEDED(hr)) { // // this is what we will passback // *pbstrData = ::SysAllocStringLen(NULL, lTotalLen + 1); if (*pbstrData == NULL) { hr = WBEM_E_OUT_OF_MEMORY; } else { pszNextToWrite = *pbstrData; long lLen = 0; for (i = lLowerBound; i <= lUpperBound; i++) { lLen = wcslen(pbstrArray[i]); // // have copied over that many WCHAR, move that many positions. // ::memcpy(pszNextToWrite, (void*)((LPCWSTR)pbstrArray[i]), lLen * sizeof(WCHAR)); pszNextToWrite += lLen; } pszNextToWrite[0] = L'\0'; } } // // release the bstrs // for (long i = lLowerBound; i <= lUpperBound; i++) { if (pbstrArray[i] != NULL) { ::SysFreeString(pbstrArray[i]); } } delete [] pbstrArray; return hr; } /* Routine Description: Name: GetStringPresentation Functionality: Given a variant, get the formatted (our format) string representation. For example, given a variant of type VT_I4 of value 12345, this function will give < VT_I4 : 12345 > For example, given a variant of type VT_BSTR of value "Microsoft's SCE", this function will give < VT_BSTR : "Microsoft's SCE" > Virtual: N/A. Arguments: pVar - The varaint to be formatted. Must not be VT_ARRAY's variant. That is done by FormatArray function. pbstrData - Receive the formatted string. Return Value: Success: WBEM_NO_ERROR. Failure: Various errors may occurs. Most obvious is WBEM_E_INVALID_SYNTAX, WBEM_E_INVALID_PARAMETER Notes: */ HRESULT GetStringPresentation ( IN VARIANT * pVar, OUT BSTR * pbstrData ) { USES_CONVERSION; if (pVar == NULL || pbstrData == NULL) { return WBEM_E_INVALID_PARAMETER; } int iFormat = 0; DWORD dwValue; double dblValue = 0.0F; *pbstrData = NULL; HRESULT hr = WBEM_NO_ERROR; switch (pVar->vt) { case VT_BSTR: hr = ::EscapeStringData(pVar->bstrVal, pbstrData, true); // adding quote break; case VT_BOOL: dwValue = (pVar->boolVal == VARIANT_TRUE) ? 1 : 0; iFormat = iFormatIntegral; break; case VT_I2: dwValue = pVar->iVal; iFormat = iFormatIntegral; break; case VT_UI1: dwValue = pVar->bVal; iFormat = iFormatIntegral; break; case VT_UI2: dwValue = pVar->uiVal; iFormat = iFormatIntegral; break; case VT_UI4: dwValue = pVar->ulVal; iFormat = iFormatIntegral; break; case VT_I4: dwValue = pVar->lVal; iFormat = iFormatIntegral; break; //case VT_I8: // dwValue = pVar->hVal; // iFormat = iFormatInt8; // break; //case VT_UI8: // dwValue = pVar->uhVal; // iFormat = iFormatInt8; // break; case VT_DATE: dblValue = pVar->date; iFormat = iFormatFloat; break; case VT_R4: dblValue = pVar->fltVal; iFormat = iFormatFloat; break; case VT_R8: dblValue = pVar->dblVal; iFormat = iFormatFloat; break; case VT_CY: iFormat = iFormatCurrenty; break; default: hr = WBEM_E_NOT_SUPPORTED; break; } // // The the data type is a integral type // if (iFormat == iFormatIntegral) { *pbstrData = ::SysAllocStringLen(NULL, MAX_INT_LENGTH); if (*pbstrData != NULL) { wsprintf(*pbstrData, L"%d", dwValue); } else { hr = WBEM_E_OUT_OF_MEMORY; } } else if (iFormat == iFormatFloat) { // // there is no wsprintf for float data types! // char chData[MAX_DOUBLE_LENGTH]; ::sprintf(chData, "%f", dblValue); *pbstrData = ::SysAllocString(A2W(chData)); if (*pbstrData == NULL) { hr = WBEM_E_OUT_OF_MEMORY; } } else if (iFormat == iFormatCurrenty) { // // the data type is currency! // *pbstrData = ::SysAllocStringLen(NULL, MAX_DOUBLE_LENGTH); if (*pbstrData != NULL) { wsprintf(*pbstrData, L"%d%c%d", pVar->cyVal.Hi, wchCySeparator, pVar->cyVal.Lo); } else { hr = WBEM_E_OUT_OF_MEMORY; } } return hr; } /* Routine Description: Name: GetVoidPtrOfVariant Functionality: Helper. Will return the address of the data member of the variant for non array data type. Virtual: N/A. Arguments: vt - The type we want. Can't be VT_ARRAY, which is done separately pVar - The variant. This may be empty! Return Value: Success: Non-NULL void*. Failure: NULL. Either this type is not supportd or the variant is NULL. Notes: */ void* GetVoidPtrOfVariant ( VARTYPE vt, VARIANT* pVar ) { if (pVar == NULL) { return NULL; } switch (vt) { case VT_BSTR: return &(pVar->bstrVal); case VT_BOOL: return &(pVar->boolVal); case VT_I2: return &(pVar->iVal); case VT_UI1: return &(pVar->bVal); case VT_UI2: return &(pVar->uiVal); case VT_UI4: return &(pVar->ulVal); case VT_I4: return &(pVar->lVal); //case VT_I8: // return &(pVar->hVal); //case VT_UI8: // return &(pVar->uhVal); case VT_DATE: return &(pVar->date); case VT_R8: return &(pVar->dblVal); case VT_R4: return &(pVar->fltVal); case VT_CY: return &(pVar->cyVal); default: return NULL; } } /* Routine Description: Name: ParseCompoundKeyString Functionality: Will parse the compound key string to get one (name, value) pair and advance the pointer to the next position to continue the same parsing. This is designed to be called repeatedly in a loop. Virtual: N/A. Arguments: pszCurStart - Current position to start the parsing. ppszName - Receives the name. Optional. If caller is not interested in the name, it can simply pass in NULL. pVar - Receives the variant value. Optional. If caller is not interested in the value, it can simply pass in NULL. ppNext - Receives the pointer for next parsing step, must not be null. Return Value: Success: WBEM_S_FALSE if no parsing needs to be done. WBEM_NO_ERROR if results are returned. Failure: various error code. Notes: Caller is responsible for releasing the out parameters if function is successful. */ HRESULT ParseCompoundKeyString ( IN LPCWSTR pszCurStart, OUT LPWSTR* ppszName OPTIONAL, OUT VARIANT* pVar OPTIONAL, OUT LPCWSTR* ppNext ) { if (ppNext == NULL) { return WBEM_E_INVALID_PARAMETER; } if (ppszName != NULL) { *ppszName = NULL; } if (pVar != NULL) { ::VariantInit(pVar); } *ppNext = NULL; if (pszCurStart == NULL || *pszCurStart == L'\0') { return WBEM_S_FALSE; } // // we don't use this bEsc. // bool bEsc; // // this is our current position to start parsing // LPCWSTR pCur = pszCurStart; // // get the name, which is delimited by wchTypeValLeft ('<') // LPCWSTR pNext = ::EscSeekToChar(pCur, wchTypeValLeft, &bEsc, true); if (*pNext != wchTypeValLeft) { return WBEM_S_FALSE; } HRESULT hr = WBEM_S_FALSE; // // it wants the name // if (ppszName != NULL) { // // caller is responsible for releasing it. // *ppszName = new WCHAR[pNext - pCur + 1]; if (*ppszName == NULL) { return WBEM_E_OUT_OF_MEMORY; } // // *pNext == '<', thus from pCur to pNext is the name of the key property // ::TrimCopy(*ppszName, pCur, pNext - pCur); } // // ready for value // pCur = pNext; // pCur is pointing at '<' // // move pNext to '>' // pNext = ::EscSeekToChar(pCur, wchTypeValRight, &bEsc, true); // // if not see the wchTypeValRight, it's syntax error // if (*pNext != wchTypeValRight) { // // caller won't do the releasing in this case // if (ppszName) { delete [] *ppszName; *ppszName = NULL; } return WBEM_E_INVALID_SYNTAX; } ++pNext; // skip the '>' if (pVar != NULL) { // // from pCur to pNext is the encoded value . Copy this to szValue // LPWSTR pszValue = new WCHAR[pNext - pCur + 1]; if (pszValue != NULL) { ::TrimCopy(pszValue, pCur, pNext - pCur); hr = ::VariantFromFormattedString(pszValue, pVar); delete [] pszValue; } else { hr = WBEM_E_OUT_OF_MEMORY; } } if (SUCCEEDED(hr)) { *ppNext = pNext; hr = WBEM_NO_ERROR; } else { delete [] *ppszName; *ppszName = NULL; } return hr; } /* Routine Description: Name: PopulateKeyProperties Functionality: Will parse the compound key string and feed the property values to the property manager, which has been attached to the appropriate object. Virtual: N/A. Arguments: pszCompoundKey - The compound key string. pScePropMgr - The property manager where the parsed (key) properties will be put. It must be prepared correctly for the object to receive the properties. Return Value: Success: WBEM_S_FALSE if no thing was done. WBEM_NO_ERROR if results are returned. Failure: various error code. Notes: Partial properties might have been set to the object if the parsing fails at a later stage. We don't try to undo those partially filled properties. */ HRESULT PopulateKeyProperties ( IN LPCWSTR pszCompoundKey, IN CScePropertyMgr * pScePropMgr ) { if (pszCompoundKey == NULL || *pszCompoundKey == L'\0') { // // nothing to populate // return WBEM_S_FALSE; } LPWSTR pszName = NULL; VARIANT var; ::VariantInit(&var); LPCWSTR pszCur = pszCompoundKey; LPCWSTR pszNext; HRESULT hr = ::ParseCompoundKeyString(pszCur, &pszName, &var, &pszNext); bool bHasSetProperties = false; while (SUCCEEDED(hr) && hr != WBEM_S_FALSE) { hr = pScePropMgr->PutProperty(pszName, &var); if (SUCCEEDED(hr)) { bHasSetProperties = true; } delete [] pszName; ::VariantClear(&var); // // start our next round // pszCur = pszNext; hr = ::ParseCompoundKeyString(pszCur, &pszName, &var, &pszNext); } if (SUCCEEDED(hr) && bHasSetProperties) { hr = WBEM_NO_ERROR; } return hr; }