windows-nt/Source/XPSP1/NT/ds/security/services/scerpc/escprov/persistmgr.cpp
2020-09-26 16:20:57 +08:00

6333 lines
128 KiB
C++

// 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 <wininet.h>
#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;
//
// 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;
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;
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;
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<IWbemClassObject> 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<IWbemClassObject> 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<IWbemClassObject> 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<IWbemClassObject> 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<vt:value> 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 <VT_I4 : 123456>)
//
CComBSTR bstrData;
hr = ::FormatVariant(&var, &bstrData);
if (SUCCEEDED(hr))
{
//
// append the value to pbstrKeyProperties[dwIndex] so that
// pbstrKeyProperties[dwIndex] all have this format: PropName<VT_TYPE : value>
//
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<LPWSTR>*
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<IWbemClassObject> 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"<VT_I4 : 12345>", &var);
will assign VT_I4 value 12345 to var.
We also support arrays. For example,
VariantFromFormattedString(L"<VT_ARRAY(VT_BSTR) : "This is " , "Microsoft SCE" >", &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 <xxx:
// the last parameter true indicates that we want the see to move to end
// if the wanted char was not found.
//
bool bEscaped = false;
pCur = ::EscSeekToChar(pCur, wchTypeValLeft, &bEscaped, true);
if (*pCur == L'\0')
{
//
// no value is encoded
//
return WBEM_NO_ERROR;
}
//
// pCur points to '<'. skip the <
//
++pCur;
//
// seek till ':'
//
LPCWSTR pNext = ::EscSeekToChar(pCur, wchTypeValSep, &bEscaped, true);
//
// must contain something before ':'
//
if (*pNext != wchTypeValSep || pCur == pNext)
{
return WBEM_E_INVALID_SYNTAX;
}
//
// length of the found token
//
int iTokenLen = pNext - pCur;
LPWSTR pszType = new WCHAR[iTokenLen + 1]; // need to release the memory
if (pszType == NULL)
{
return WBEM_E_OUT_OF_MEMORY;
}
//
// copy the token, but trim the white space at both ends
//
::TrimCopy(pszType, pCur, iTokenLen);
VARTYPE varSubType;
VARTYPE varVT = gStringToVtMap.GetType(pszType, &varSubType);
//
// done with the type string
//
delete [] pszType;
if (varVT == VT_EMPTY)
{
return WBEM_E_NOT_SUPPORTED;
}
//
// must have more data
//
pCur = ++pNext; // pCur point to char after ':'
if (*pCur == L'\0')
{
return WBEM_E_INVALID_SYNTAX;
}
pNext = ::EscSeekToChar(pCur, wchTypeValRight, &bEscaped, true); // move to end if not found
//
// must see '>'
//
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 <xxx:yyyyyyy>. 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;
}