/*=================================================================== Microsoft Denali Microsoft Confidential. Copyright 1997 Microsoft Corporation. All Rights Reserved. Component: MetaUtil object File: PropCol.cpp Owner: t-BrianM This file contains implementation of the property collection and property object. ===================================================================*/ #include "stdafx.h" #include "MetaUtil.h" #include "MUtilObj.h" #include "PropCol.h" /*------------------------------------------------------------------ * C P r o p e r t y C o l l e c t i o n */ /*=================================================================== CPropertyCollection::CPropertyCollection Constructor Parameters: None Returns: Nothing ===================================================================*/ CPropertyCollection::CPropertyCollection() : m_pCSchemaTable(NULL), m_tszKey(NULL) { } /*=================================================================== CPropertyCollection::Init Constructor Parameters: pIMeta ATL Smart pointer to the metabase tszKey Name of key to enumerate properties of Returns: E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ HRESULT CPropertyCollection::Init(const CComPtr &pIMeta, CMetaSchemaTable *pCSchemaTable, LPTSTR tszKey) { ASSERT(pIMeta.p != NULL); ASSERT_NULL_OR_STRING(tszKey); USES_CONVERSION; HRESULT hr; m_pIMeta = pIMeta; m_pCSchemaTable = pCSchemaTable; m_pCSchemaTable->AddRef(); // Copy tszKey to m_tszKey if (tszKey == NULL) { // Key is root m_tszKey = NULL; } else { // Allocate and copy the passed string to the member string m_tszKey = new TCHAR[_tcslen(tszKey) + 1]; if (m_tszKey == NULL) { return ::ReportError(E_OUTOFMEMORY); } _tcscpy(m_tszKey, tszKey); CannonizeKey(m_tszKey); // Make sure the key exists by opening and closing it METADATA_HANDLE hMDKey; hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszKey), METADATA_PERMISSION_READ, MUTIL_OPEN_KEY_TIMEOUT, &hMDKey); if (FAILED(hr)) { return ::ReportError(hr); } m_pIMeta->CloseKey(hMDKey); } return S_OK; } /*=================================================================== CPropertyCollection::~CPropertyCollection Destructor Parameters: None Returns: Nothing ===================================================================*/ CPropertyCollection::~CPropertyCollection() { m_pCSchemaTable->Release(); if (m_tszKey != NULL) { delete m_tszKey; } } /*=================================================================== CPropertyCollection::InterfaceSupportsErrorInfo Standard ATL implementation ===================================================================*/ STDMETHODIMP CPropertyCollection::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IPropertyCollection, }; for (int i=0;iEnumData(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszKey), &mdr, *plReturn, &dwReqDataLen); if (FAILED(hr)) { if (HRESULT_CODE(hr) == ERROR_NO_MORE_ITEMS) { // Done, cleanup and return the result delete pbData; return S_OK; } else if (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER) { // Make a bigger buffer and try again delete pbData; dwDataLen = dwReqDataLen; pbData = new BYTE[dwDataLen]; if (pbData == NULL) { return ::ReportError(E_OUTOFMEMORY); } } else { delete pbData; return ::ReportError(hr); } } else { // SUCCEEDED(hr) // Count it (*plReturn)++; } } } /*=================================================================== CPropertyCollection::get_Item Get method for Item property. Returns a key given its index. Parameters: varId [in] 1 based index or Name of the property to get ppIReturn [out, retval] Interface for the property object Returns: E_INVALIDARG if ppIReturn == NULL or lIndex <= 0 E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ STDMETHODIMP CPropertyCollection::get_Item(long lIndex, LPDISPATCH * ppIReturn) { TRACE0("MetaUtil: CPropertyCollection::get_Item\n"); ASSERT_NULL_OR_POINTER(ppIReturn, LPDISPATCH); if ((ppIReturn == NULL) || (lIndex <= 0)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; HRESULT hr; // Get the requested property METADATA_RECORD mdr; BYTE *pbData; DWORD dwReqDataLen; pbData = new BYTE[1024]; if (pbData == NULL) { return ::ReportError(E_OUTOFMEMORY); } mdr.dwMDIdentifier = 0; mdr.dwMDAttributes = 0; mdr.dwMDUserType = ALL_METADATA; mdr.dwMDDataType = ALL_METADATA; mdr.pbMDData = pbData; mdr.dwMDDataLen = 1024; mdr.dwMDDataTag = 0; hr = m_pIMeta->EnumData(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszKey), &mdr, lIndex - 1, &dwReqDataLen); // If the buffer was too small, try again with a bigger one if (FAILED(hr) && (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER)) { delete pbData; pbData = new BYTE[dwReqDataLen]; if (pbData == NULL) { return ::ReportError(hr); } mdr.dwMDIdentifier = 0; mdr.dwMDAttributes = 0; mdr.dwMDUserType = ALL_METADATA; mdr.dwMDDataType = ALL_METADATA; mdr.pbMDData = pbData; mdr.dwMDDataLen = dwReqDataLen; mdr.dwMDDataTag = 0; hr = m_pIMeta->EnumData(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszKey), &mdr, lIndex - 1, &dwReqDataLen); } // If we got it create a properties object if (SUCCEEDED(hr)) { // Create the property object CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { delete pbData; return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_pCSchemaTable, m_tszKey, &mdr); if (FAILED(hr)) { delete pbData; return ::ReportError(hr); } // Set the interface to IDispatch hr = pObj->QueryInterface(IID_IDispatch, (void **) ppIReturn); if (FAILED(hr)) { delete pbData; return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); } else { // FAILED(hr) delete pbData; return ::ReportError(hr); } delete pbData; return S_OK; } /*=================================================================== CPropertyCollection::get__NewEnum Get method for _NewEnum property. Returns an enumeration object for the properties. Parameters: ppIReturn [out, retval] Interface for the enumeration object Returns: E_INVALIDARG if ppIReturn == NULL E_OUTOFMEMORY if allocation failed S_OK on success ===================================================================*/ STDMETHODIMP CPropertyCollection::get__NewEnum(LPUNKNOWN * ppIReturn) { TRACE0("MetaUtil: CPropertyCollection::get__NewEnum\n"); ASSERT_NULL_OR_POINTER(ppIReturn, LPUNKNOWN); if (ppIReturn == NULL) { return ::ReportError(E_INVALIDARG); } HRESULT hr; // Create the property enumeration CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_pCSchemaTable, m_tszKey, 0); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IUnknown hr = pObj->QueryInterface(IID_IUnknown, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); return S_OK; } /*=================================================================== CPropertyCollection::Get Get a property object from the base key of the collection. Parameters: varId [in] Identifier of property to get. Either the Id (number) or Name (string). ppIReturn [out, retval] Interface for the property object Returns: E_INVALIDARG if ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CPropertyCollection::Get(VARIANT varId, IProperty **ppIReturn) { TRACE0("MetaUtil: CPropertyCollection::Get\n"); ASSERT_NULL_OR_POINTER(ppIReturn, IProperty *); if (ppIReturn == NULL) { return E_INVALIDARG; } return ::GetProperty(m_pIMeta, m_pCSchemaTable, m_tszKey, varId, ppIReturn); } /*=================================================================== CPropertyCollection::Add Add a property object to the base key of the collection. Parameters: varId [in] Identifier of property to get. Either the Id (number) or Name (string). ppIReturn [out, retval] Interface for the property object Returns: E_INVALIDARG if ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CPropertyCollection::Add(VARIANT varId, IProperty **ppIReturn) { TRACE0("MetaUtil: CPropertyCollection::Add\n"); ASSERT_NULL_OR_POINTER(ppIReturn, IProperty *); if (ppIReturn == NULL) { return E_INVALIDARG; } return ::CreateProperty(m_pIMeta, m_pCSchemaTable, m_tszKey, varId, ppIReturn); } /*=================================================================== CPropertyCollection::Remove Remove a property from the base key of the collection. Parameters: varId [in] Identifier of property to remove. Either the Id (number) or Name (string). Returns: S_OK on success ===================================================================*/ STDMETHODIMP CPropertyCollection::Remove(VARIANT varId) { TRACE0("MetaUtil: CPropertyCollection::Remove\n"); return ::DeleteProperty(m_pIMeta, m_pCSchemaTable, m_tszKey, varId); } /*------------------------------------------------------------------ * C P r o p e r t y E n u m */ /*=================================================================== CPropertyEnum::CPropertyEnum Constructor Parameters: None Returns: Nothing ===================================================================*/ CPropertyEnum::CPropertyEnum() : m_pCSchemaTable(NULL), m_tszKey(NULL), m_iIndex(0) { } /*=================================================================== CPropertyEnum::Init Constructor Parameters: pIMeta ATL Smart pointer to the metabase tszKey Name of key to enumerate properties of iIndex Index of next element in enumeration Returns: E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ HRESULT CPropertyEnum::Init(const CComPtr &pIMeta, CMetaSchemaTable *pCSchemaTable, LPCTSTR tszKey, int iIndex) { ASSERT(pIMeta.p != NULL); ASSERT_NULL_OR_STRING(tszKey); ASSERT(iIndex >= 0); m_pIMeta = pIMeta; m_pCSchemaTable = pCSchemaTable; m_pCSchemaTable->AddRef(); // Copy m_tszKey if (tszKey == NULL) { // Key is root m_tszKey = NULL; } else { // Allocate and copy the passed string to the member string m_tszKey = new TCHAR[_tcslen(tszKey) + 1]; if (m_tszKey == NULL) { return ::ReportError(E_OUTOFMEMORY); } _tcscpy(m_tszKey, tszKey); CannonizeKey(m_tszKey); } m_iIndex = iIndex; return S_OK; } /*=================================================================== CPropertyEnum::~CPropertyEnum Destructor Parameters: None Returns: Nothing ===================================================================*/ CPropertyEnum::~CPropertyEnum() { m_pCSchemaTable->Release(); if (m_tszKey != NULL) { delete m_tszKey; } } /*=================================================================== CPropertyEnum::Next Gets the next n items from the enumberation. Parameters: ulNumToGet [in] Number of elements to get rgvarDest [out] Array to put them in pulNumGot [out] If not NULL, number of elements rgvarDest got Returns: E_INVALIDARG if rgvarDest == NULL E_OUTOFMEMORY if allocation failed S_OK if outputs ulNumToGet items S_FALSE if outputs less than ulNumToGet items ===================================================================*/ STDMETHODIMP CPropertyEnum::Next(unsigned long ulNumToGet, VARIANT FAR* rgvarDest, unsigned long FAR* pulNumGot) { TRACE0("MetaUtil: CPropertyEnum::Next\n"); ASSERT_NULL_OR_POINTER(pulNumGot, unsigned long); // Make sure the array is big enough and we can write to it ASSERT((rgvarDest == NULL) || IsValidAddress(rgvarDest, ulNumToGet * sizeof(VARIANT), TRUE)); if (rgvarDest == NULL) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; HRESULT hr; METADATA_RECORD mdr; BYTE *pbData; DWORD dwDataLen; DWORD dwReqDataLen; unsigned int uiDestIndex; IDispatch *pIDispatch; dwDataLen = 1024; pbData = new BYTE[dwDataLen]; if (pbData == NULL) { return ::ReportError(E_OUTOFMEMORY); } // For each property to get uiDestIndex = 0; while (uiDestIndex < ulNumToGet) { // Get a property mdr.dwMDIdentifier = 0; mdr.dwMDAttributes = 0; mdr.dwMDUserType = ALL_METADATA; mdr.dwMDDataType = ALL_METADATA; mdr.pbMDData = pbData; mdr.dwMDDataLen = dwDataLen; mdr.dwMDDataTag = 0; hr = m_pIMeta->EnumData(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszKey), &mdr, m_iIndex, &dwReqDataLen); if (FAILED(hr)) { if (HRESULT_CODE(hr) == ERROR_NO_MORE_ITEMS) { // Done, cleanup and return the result if (pulNumGot != NULL) { *pulNumGot = uiDestIndex; } delete pbData; return S_FALSE; } else if (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER) { // Try again with a bigger buffer delete pbData; dwDataLen = dwReqDataLen; pbData = new BYTE[dwDataLen]; if (pbData == NULL) { return ::ReportError(E_OUTOFMEMORY); } } else { delete pbData; return ::ReportError(hr); } } else { // SUCCEEDED(hr) // Create the property object CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { delete pbData; return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_pCSchemaTable, m_tszKey, &mdr); if (FAILED(hr)) { delete pbData; return ::ReportError(hr); } // Set the interface to IDispatch hr = pObj->QueryInterface(IID_IDispatch, (void **) &pIDispatch); if (FAILED(hr)) { delete pbData; return ::ReportError(hr); } ASSERT(pIDispatch != NULL); // Put it in the output array VariantInit(&(rgvarDest[uiDestIndex])); rgvarDest[uiDestIndex].vt = VT_DISPATCH; rgvarDest[uiDestIndex].pdispVal = pIDispatch; // Next element m_iIndex++; uiDestIndex++; } } delete pbData; if (pulNumGot != NULL) { *pulNumGot = uiDestIndex; } return S_OK; } /*=================================================================== CPropertyEnum::Skip Skips the next n items in an enumeration Parameters: ulNumToSkip [in] Number of elements to skip Returns: S_OK always ===================================================================*/ STDMETHODIMP CPropertyEnum::Skip(unsigned long ulNumToSkip) { TRACE0("MetaUtil: CPropertyEnum::Skip\n"); m_iIndex += ulNumToSkip; return S_OK; } /*=================================================================== CPropertyEnum::Reset Rests the enumeration to the first item Parameters: None Returns: S_OK always ===================================================================*/ STDMETHODIMP CPropertyEnum::Reset() { TRACE0("MetaUtil: CPropertyEnum::Reset\n"); m_iIndex = 0; return S_OK; } /*=================================================================== CPropertyEnum::Clone Gets an interface pointer to a copy of the enumeration at its current state. Parameters: ppIReturn [out] Pointer to interface for copy Returns: E_INVALIDARG if ppIReturn == NULL E_OUTOFMEMORY if not enough memory to create clone S_OK on success ===================================================================*/ STDMETHODIMP CPropertyEnum::Clone(IEnumVARIANT FAR* FAR* ppIReturn) { TRACE0("MetaUtil: CPropertyEnum::Clone\n"); ASSERT_NULL_OR_POINTER(ppIReturn, LPUNKNOWN); if (ppIReturn == NULL) { return ::ReportError(E_INVALIDARG); } HRESULT hr; // Create a copy of the enumeration CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_pCSchemaTable, m_tszKey, m_iIndex); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IEnumVARIANT hr = pObj->QueryInterface(IID_IEnumVARIANT, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); return S_OK; } /*------------------------------------------------------------------ * C P r o p e r t y */ /*=================================================================== CProperty::CProperty Constructor Parameters: None Returns: Nothing ===================================================================*/ CProperty::CProperty() : m_pCSchemaTable(NULL), m_tszKey(NULL), m_dwId(0), m_dwAttributes(0), m_dwUserType(0), m_dwDataType(0) { VariantInit(&m_varData); } /*=================================================================== CProperty::Init Constructor Parameters: tszKey Name of key where the property is located dwId Id of property bCreate TRUE if this property can be created (does not have to exist) Returns: E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ HRESULT CProperty::Init(const CComPtr &pIMeta, CMetaSchemaTable *pCSchemaTable, LPCTSTR tszKey, DWORD dwId, BOOL bCreate) { ASSERT(pIMeta.p != NULL); ASSERT_NULL_OR_STRING(tszKey); USES_CONVERSION; HRESULT hr; m_pIMeta = pIMeta; m_pCSchemaTable = pCSchemaTable; m_pCSchemaTable->AddRef(); // Set the Key and Id members if (tszKey == NULL) { m_tszKey = NULL; } else { m_tszKey = new TCHAR[_tcslen(tszKey) + 1]; if (m_tszKey == NULL) { return ::ReportError(E_OUTOFMEMORY); } _tcscpy(m_tszKey, tszKey); CannonizeKey(m_tszKey); } m_dwId = dwId; // Open the key (to be sure it exists) METADATA_HANDLE hMDKey; hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszKey), METADATA_PERMISSION_READ, MUTIL_OPEN_KEY_TIMEOUT, &hMDKey); if (FAILED(hr)) { return ::ReportError(hr); } // Get the property METADATA_RECORD mdr; BYTE *pbData; DWORD dwReqLen; pbData = new BYTE[1024]; if (pbData == NULL) { m_pIMeta->CloseKey(hMDKey); return ::ReportError(hr); } mdr.dwMDIdentifier = m_dwId; mdr.dwMDAttributes = 0; mdr.dwMDUserType = ALL_METADATA; mdr.dwMDDataType = ALL_METADATA; mdr.pbMDData = pbData; mdr.dwMDDataLen = 1024; mdr.dwMDDataTag = 0; hr = m_pIMeta->GetData(hMDKey, NULL, &mdr, &dwReqLen); // If the buffer was too small, try again with a bigger one if (FAILED(hr) && (HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER)) { delete pbData; pbData = new BYTE[dwReqLen]; if (pbData == NULL) { m_pIMeta->CloseKey(hMDKey); return ::ReportError(hr); } mdr.dwMDIdentifier = m_dwId; mdr.dwMDAttributes = 0; mdr.dwMDUserType = ALL_METADATA; mdr.dwMDDataType = ALL_METADATA; mdr.pbMDData = pbData; mdr.dwMDDataLen = dwReqLen; mdr.dwMDDataTag = 0; hr = m_pIMeta->GetData(hMDKey, NULL, &mdr, &dwReqLen); } // If we got it fill in the fields if (SUCCEEDED(hr)) { m_dwAttributes = mdr.dwMDAttributes; m_dwUserType = mdr.dwMDUserType; m_dwDataType = mdr.dwMDDataType; SetDataToVar(mdr.pbMDData, mdr.dwMDDataLen); } // If the property doesn't exist and we're creating, set defaults else if ((bCreate) && (hr == MD_ERROR_DATA_NOT_FOUND)) { m_dwAttributes = 0; m_dwUserType = 0; m_dwDataType = 0; VariantClear(&m_varData); } else { //(FAILED(hr)) delete pbData; m_pIMeta->CloseKey(hMDKey); return ::ReportError(hr); } delete pbData; // Close the key m_pIMeta->CloseKey(hMDKey); return S_OK; } /*=================================================================== CProperty::Init Constructor Parameters: tszKey Name of key where property is located mdr METADATA_RECORD containing the current property info Returns: E_OUTOFMEMORY if allocation fails S_OK on success ===================================================================*/ HRESULT CProperty::Init(const CComPtr &pIMeta, CMetaSchemaTable *pCSchemaTable, LPCTSTR tszKey, METADATA_RECORD *mdr) { ASSERT(pIMeta.p != NULL); ASSERT_NULL_OR_STRING(tszKey); HRESULT hr; m_pIMeta = pIMeta; m_pCSchemaTable = pCSchemaTable; m_pCSchemaTable->AddRef(); // Set the Key member if (tszKey == NULL) { m_tszKey = NULL; } else { m_tszKey = new TCHAR[_tcslen(tszKey) + 1]; if (m_tszKey == NULL) { return ::ReportError(E_OUTOFMEMORY); } _tcscpy(m_tszKey, tszKey); } // Use mdr to set the rest m_dwId = mdr->dwMDIdentifier; m_dwAttributes = mdr->dwMDAttributes; m_dwUserType = mdr->dwMDUserType; m_dwDataType = mdr->dwMDDataType; hr = SetDataToVar(mdr->pbMDData, mdr->dwMDDataLen); if (FAILED(hr)) { ::ReportError(hr); } return S_OK; } /*=================================================================== CProperty::~CProperty Destructor Parameters: None Returns: Nothing ===================================================================*/ CProperty::~CProperty() { m_pCSchemaTable->Release(); if (m_tszKey != NULL) { delete m_tszKey; } VariantClear(&m_varData); } /*=================================================================== CProperty::InterfaceSupportsErrorInfo Standard ATL implementation ===================================================================*/ STDMETHODIMP CProperty::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IProperty, }; for (int i=0;iGetPropInfo(m_tszKey, m_dwId); // Did we find it? Is there a name entry if ((pCPropInfo == NULL) || (pCPropInfo->GetName() == NULL)) { // No, return "" *pbstrName = T2BSTR(_T("")); } else { // Yes, return the name *pbstrName = T2BSTR(pCPropInfo->GetName()); } return S_OK; } /*=================================================================== CProperty::get_Attributes Get method for Attributes property. Gets the attribute flags for this property. Parameters: plAttributes [out, retval] Value to return to client. Returns: E_INVALIDARG if pulAttributes == NULL S_OK on success ===================================================================*/ STDMETHODIMP CProperty::get_Attributes(long *plAttributes) { //TRACE0("MetaUtil: CProperty::get_Attributes\n"); ASSERT_NULL_OR_POINTER(plAttributes, long); if (plAttributes == NULL) { return ::ReportError(E_INVALIDARG); } *plAttributes = (long) m_dwAttributes; return S_OK; } /*=================================================================== CProperty::put_Attributes Put method for Attributes property. Sets the attribute flags for this property. Parameters: lAttributes [in] New value for attributes. Returns: S_OK always ===================================================================*/ STDMETHODIMP CProperty::put_Attributes(long lAttributes) { TRACE0("MetaUtil: CProperty::put_Attributes\n"); m_dwAttributes = (DWORD) lAttributes; return S_OK; } /*=================================================================== CProperty::get_UserType Get method for UserType property. Gets the User Type for this metabase property. Parameters: plUserType [out, retval] Value to return to client. Returns: E_INVALIDARG if pulUserType == NULL S_OK on success ===================================================================*/ STDMETHODIMP CProperty::get_UserType(long *plUserType) { //TRACE0("MetaUtil: CProperty::get_UserType\n"); ASSERT_NULL_OR_POINTER(plUserType, long); if (plUserType == NULL) { return ::ReportError(E_INVALIDARG); } *plUserType = (long) m_dwUserType; return S_OK; } /*=================================================================== CProperty::put_UserType Put method for UserType property. Sets the user type Parameters: lUserType [in] New value for user type. Returns: S_OK always ===================================================================*/ STDMETHODIMP CProperty::put_UserType(long lUserType) { TRACE0("MetaUtil: CProperty::put_UserType\n"); m_dwUserType = (DWORD) lUserType; return S_OK; } /*=================================================================== CProperty::get_DataType Get method for DataType property. Gets the type of data stored in the metabase property. Parameters: plDataType [out, retval] Value to return to client. Returns: E_INVALIDARG if pulDataType == NULL S_OK on success ===================================================================*/ STDMETHODIMP CProperty::get_DataType(long *plDataType) { //TRACE0("MetaUtil: CProperty::get_DataType\n"); ASSERT_NULL_OR_POINTER(plDataType, long); if (plDataType == NULL) { return ::ReportError(E_INVALIDARG); } *plDataType = (long) m_dwDataType; return S_OK; } /*=================================================================== CProperty::put_DataType Put method for DataType property. Sets the data type Parameters: lDataType [in] New value for data type. Returns: S_OK always ===================================================================*/ STDMETHODIMP CProperty::put_DataType(long lDataType) { TRACE0("MetaUtil: CProperty::put_DataType\n"); m_dwDataType = (DWORD) lDataType; return S_OK; } /*=================================================================== CProperty::get_Data Get method for Data property. Gets the data for this metabase property. Parameters: pvarData [out, retval] Value to return to client. Returns: E_INVALIDARG if pvarData == NULL S_OK on success ===================================================================*/ STDMETHODIMP CProperty::get_Data(VARIANT *pvarData) { //TRACE0("MetaUtil: CProperty::get_Data\n"); ASSERT_NULL_OR_POINTER(pvarData, VARIANT); if (pvarData == NULL) { return E_INVALIDARG; } HRESULT hr; hr = VariantCopy(pvarData, &m_varData); if (FAILED(hr)) { return ::ReportError(hr); } return S_OK; } /*=================================================================== CProperty::put_Data Put method for Data property. Sets the data Parameters: varData [in] New value for data ===================================================================*/ STDMETHODIMP CProperty::put_Data(VARIANT varData) { TRACE0("MetaUtil: CProperty::put_Data\n"); HRESULT hr; hr = VariantCopy(&m_varData, &varData); if (FAILED(hr)) { return ::ReportError(hr); } return S_OK; } /*=================================================================== CProperty::Write Writes changes made to this object to the metabase Parameters: None Returns: S_OK on success ===================================================================*/ STDMETHODIMP CProperty::Write() { USES_CONVERSION; TRACE0("MetaUtil: CProperty::Write\n"); HRESULT hr; // Open the key for write access METADATA_HANDLE hMDKey; hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(m_tszKey), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, MUTIL_OPEN_KEY_TIMEOUT, &hMDKey); if (FAILED(hr)) { return ::ReportError(hr); } // Create the data record METADATA_RECORD mdr; mdr.dwMDIdentifier = m_dwId; mdr.dwMDAttributes = m_dwAttributes; mdr.dwMDUserType = m_dwUserType; mdr.dwMDDataType = m_dwDataType; hr = GetDataFromVar(mdr.pbMDData, mdr.dwMDDataLen); if (FAILED(hr)) { m_pIMeta->CloseKey(hMDKey); return ::ReportError(hr); } mdr.dwMDDataTag = 0; // Set the data hr = m_pIMeta->SetData(hMDKey, L"", &mdr); if (FAILED(hr)) { m_pIMeta->CloseKey(hMDKey); delete mdr.pbMDData; return ::ReportError(hr); } // Close the key m_pIMeta->CloseKey(hMDKey); delete mdr.pbMDData; return S_OK; } /*=================================================================== CProperty::SetDataToVar Private function to save property data from its raw form to the variant data member. Parameters: pbData Raw property data to convert to variant dwDataLen Length of property data Returns: ERROR_INVALID_DATA if m_dwDataType is not recognized E_OUTOFMEMORY if allocation failed S_OK on success ===================================================================*/ HRESULT CProperty::SetDataToVar(BYTE *pbData, DWORD dwDataLen) { ASSERT((pbData == NULL) || IsValidAddress(pbData, dwDataLen, FALSE)); HRESULT hr; hr = VariantClear(&m_varData); if (FAILED(hr)) { return ::ReportError(hr); } switch(m_dwDataType) { case DWORD_METADATA: // I4 subtype V_VT(&m_varData) = VT_I4; V_I4(&m_varData) = *(reinterpret_cast (pbData)); break; case STRING_METADATA: case EXPANDSZ_METADATA: // BSTR subtype V_VT(&m_varData) = VT_BSTR; V_BSTR(&m_varData) = W2BSTR(reinterpret_cast (pbData)); break; case MULTISZ_METADATA: { ULONG cStrings = 0; // Metabase string are Unicode LPCWSTR pwsz = reinterpret_cast (pbData); LPCWSTR pwszEnd = reinterpret_cast (pbData + dwDataLen); // Data is a series of null-terminated strings terminated by two nulls. // Figure out how many values we have while ((*pwsz != L'\0') && (pwsz < pwszEnd)) { cStrings++; pwsz += wcslen(pwsz) + 1; // skip string and trailing \0 } // Create a SAFEARRAY to hold the return result. The array // has to be of VARIANTs, not BSTRs, as you might expect, because // VBScript will not accept an array of BSTRs (although VB5 will). SAFEARRAYBOUND rgsabound[1] = {{cStrings, 0L}}; SAFEARRAY* psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound); if (psa == NULL) return ::ReportError(E_OUTOFMEMORY); // now stuff the values into the array LONG i = 0; pwsz = reinterpret_cast (pbData); while ((*pwsz != L'\0') && (pwsz < pwszEnd)) { // Stuff the string into a BSTR VARIANT CComVariant vt = W2BSTR(pwsz); ASSERT(V_VT(&vt) == VT_BSTR); HRESULT hr = SafeArrayPutElement(psa, &i, (void*) &vt); if (FAILED(hr)) ::ReportError(hr); i++; pwsz += wcslen(pwsz) + 1; // skip string and trailing \0 } V_VT(&m_varData) = VT_ARRAY | VT_VARIANT; V_ARRAY(&m_varData) = psa; break; } case BINARY_METADATA: // BSTR of byte data subtype V_VT(&m_varData) = VT_BSTR; V_BSTR(&m_varData) = SysAllocStringByteLen((char *) pbData, dwDataLen); break; default: // Unknown data type return ::ReportError(ERROR_INVALID_DATA); } return S_OK; } /*=================================================================== CProperty::GetDataFromVar Private function to get data from the variant data member to its raw form. Supported SubTypes: DWORD_METADATA: I1, I2, I4, I8, UI1, UI2, UI4, UI8 STRING_METADATA and EXPANDSZ_METADATA: BSTR MULTISZ_METADATA VT_ARRAY | VT_VARIANT (1 Dimension, stops on NULL or EMPTY) VT_ARRAY | VT_BSTR (1 Dimension) BINARY_METADATA BSTR Parameters: pbData Pointer to output buffer (allocated by this function) dwDataLen Length of data in output buffer Returns: ERROR_INVALID_DATA if m_dwDataType is not recognized or does not match the expected variant subtype. E_OUTOFMEMORY on allocation failure S_OK on succes Notes: Case statements are used for each dwMDDataType value to facilitate adding support for additional VariantSubType to Data conversions. MULTISZ_METADATA with VT_ARRAY | VT_VARIANT stops at a NULL or EMPTY entry because it is easy to allocate an array one bigger than you need in VBScript. Instead of erroring in this case, I stop when I hit such an entry. This also allows a larger array to be allocated that is terminated by NULL or EMPTY. ===================================================================*/ HRESULT CProperty::GetDataFromVar(BYTE * &pbData, DWORD &dwDataLen) { USES_CONVERSION; HRESULT hr; // Cleanup any IDispatch or byref stuff CComVariant varData; hr = VariantResolveDispatch(&m_varData, &varData); if (FAILED(hr)) { return hr; } switch(m_dwDataType) { case DWORD_METADATA: // I4 subtype switch (V_VT(&varData)) { case VT_I1: case VT_I2: case VT_I4: case VT_I8: case VT_UI1: case VT_UI2: case VT_UI8: // Coerce all integral types to VT_UI4, which is the same as DWORD_METADATA if (FAILED(hr = VariantChangeType(&varData, &varData, 0, VT_UI4))) return ::ReportError(hr); // fallthru to VT_UI4 case VT_UI4: dwDataLen = sizeof(DWORD); pbData = reinterpret_cast (new DWORD); if (pbData == NULL) { return ::ReportError(E_OUTOFMEMORY); } *(reinterpret_cast (pbData)) = V_UI4(&varData); break; default: // Unexpected data type return ::ReportError(ERROR_INVALID_DATA); } break; case STRING_METADATA: case EXPANDSZ_METADATA: // BSTR subtype switch (V_VT(&varData)) { case VT_BSTR: // Ignores the length field, terminate at the first NULL dwDataLen = (wcslen(OLE2W(V_BSTR(&varData))) + 1) * sizeof(wchar_t); pbData = new BYTE[dwDataLen]; if( pbData == NULL ) { return ::ReportError(E_OUTOFMEMORY); } memcpy(pbData, OLE2W(V_BSTR(&varData)), dwDataLen); default: // Unexpected data type return ::ReportError(ERROR_INVALID_DATA); } break; case MULTISZ_METADATA: // ARRAY of BSTR subtype // if it's a 1 Dimentional Array subtype if (((V_VT(&varData) & VT_ARRAY) == VT_ARRAY) && (SafeArrayGetDim(V_ARRAY(&varData)) == 1) ) { // Get Array Bounds long lLBound; long lUBound; long lNumElements; hr = SafeArrayGetLBound(V_ARRAY(&varData), 1, &lLBound); if (FAILED(hr)) { return ::ReportError(hr); } hr = SafeArrayGetUBound(V_ARRAY(&varData), 1, &lUBound); if (FAILED(hr)) { return ::ReportError(hr); } lNumElements = lUBound - lLBound + 1; // Process the element types switch (V_VT(&varData)) { case VT_ARRAY | VT_VARIANT : { VARIANT *rgvarRaw; // Before resolveIDispatch CComVariant *rgvar; // After resolveIDispatch LPWSTR wszIndex; int i; int iStrLen; rgvar = new CComVariant[lUBound - lLBound + 1]; if (rgvar == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = SafeArrayAccessData(V_ARRAY(&varData), (void **) &rgvarRaw); if (FAILED(hr)) { return ::ReportError(hr); } // Pass 1, resolve IDispatch, check types and figure out how much memory is needed dwDataLen = 0; for (i = 0; i < lNumElements; i++) { hr = VariantResolveDispatch(&(rgvarRaw[i]), &(rgvar[i])); if (FAILED(hr)) { return hr; } if (V_VT(&(rgvar[i])) != VT_BSTR) { if ((V_VT(&(rgvar[i])) == VT_EMPTY) || (V_VT(&(rgvar[i])) == VT_NULL)) { // NULL or EMPTY, Stop Here lNumElements = i; break; } else { SafeArrayUnaccessData(V_ARRAY(&varData)); return ::ReportError(ERROR_INVALID_DATA); } } dwDataLen += (wcslen(OLE2W(V_BSTR(&(rgvar[i])))) + 1) * sizeof(wchar_t); } dwDataLen += sizeof(wchar_t); // Allocate pbData = new BYTE[dwDataLen]; if (pbData == NULL) { SafeArrayUnaccessData(V_ARRAY(&varData)); return ::ReportError(E_OUTOFMEMORY); } // Pass 2, copy to desination wszIndex = reinterpret_cast (pbData); for (i = 0; i < lNumElements; i++) { iStrLen = (wcslen(OLE2W(V_BSTR(&(rgvar[i])))) + 1); memcpy(wszIndex, OLE2W(V_BSTR(&(rgvar[i]))), iStrLen * sizeof(wchar_t)); wszIndex += iStrLen; } *wszIndex = L'\0'; SafeArrayUnaccessData(V_ARRAY(&varData)); break; } case VT_ARRAY | VT_BSTR : { BSTR *rgbstr; LPWSTR wszIndex; int i; int iStrLen; hr = SafeArrayAccessData(V_ARRAY(&varData), (void **) &rgbstr); if (FAILED(hr)) { return ::ReportError(hr); } // Pass 1, figure out how much memory is needed dwDataLen = 0; for (i = 0; i < lNumElements; i++) { dwDataLen += (wcslen(OLE2W(rgbstr[i])) + 1) * sizeof(wchar_t); } dwDataLen += sizeof(wchar_t); // Allocate pbData = new BYTE[dwDataLen]; if (pbData == NULL) { SafeArrayUnaccessData(V_ARRAY(&varData)); return ::ReportError(E_OUTOFMEMORY); } // Pass 2, copy to desination wszIndex = reinterpret_cast (pbData); for (i = 0; i < lNumElements; i++) { iStrLen = (wcslen(OLE2W(rgbstr[i])) + 1); memcpy(wszIndex, OLE2W(rgbstr[i]), iStrLen * sizeof(wchar_t)); wszIndex += iStrLen; } *wszIndex = L'\0'; SafeArrayUnaccessData(V_ARRAY(&varData)); break; } default: // Unexpected data type return ::ReportError(ERROR_INVALID_DATA); } } else { // Array is not one dimensional // Unexpected data type return ::ReportError(ERROR_INVALID_DATA); } break; case BINARY_METADATA: // BSTR of bytes subtype switch (V_VT(&varData)) { case VT_BSTR: // Use the length field, since NULL values are allowed dwDataLen = SysStringByteLen(V_BSTR(&varData)); pbData = new BYTE[dwDataLen]; if( pbData == NULL ) { return ::ReportError(E_OUTOFMEMORY); } memcpy(pbData, V_BSTR(&varData), dwDataLen); default: // Unexpected data type return ::ReportError(ERROR_INVALID_DATA); } break; default: // Unknown metabase data type return ::ReportError(ERROR_INVALID_DATA); } return S_OK; }