/*=================================================================== Microsoft Denali Microsoft Confidential. Copyright 1997 Microsoft Corporation. All Rights Reserved. Component: MetaUtil object File: MUtilObj.cpp Owner: t-BrianM This file contains implementation of the main MetaUtil class. Except CheckSchema is in ChkSchm.cpp and CheckKey is in ChkKey.cpp ===================================================================*/ #include "stdafx.h" #include "MetaUtil.h" #include "MUtilObj.h" #include "keycol.h" /*------------------------------------------------------------------ * C M e t a U t i l (edit and general portions) */ /*=================================================================== CMetaUtil::CMetaUtil Constructor Parameters: None Returns: Nothing ===================================================================*/ CMetaUtil::CMetaUtil() : m_dwMaxPropSize(10 * 1024), // 10k m_dwMaxKeySize(100 * 1024), // 100k m_dwMaxNumErrors(100) { } /*=================================================================== CMetaUtil::FinalConstruct Constructor Parameters: None Returns: Nothing ===================================================================*/ HRESULT CMetaUtil::FinalConstruct() { HRESULT hr; // Create the metabase admin base object hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, (void **)&m_pIMeta); if (FAILED(hr)) { return ::ReportError(hr); } // Create a schema table m_pCSchemaTable = new CMetaSchemaTable; if (m_pCSchemaTable == NULL) { return ::ReportError(E_OUTOFMEMORY); } return S_OK; } /*=================================================================== CMetaUtil::FinalRelease Destructor Parameters: None Returns: Nothing ===================================================================*/ void CMetaUtil::FinalRelease() { m_pIMeta = NULL; if (m_pCSchemaTable != NULL) m_pCSchemaTable->Release(); } /*=================================================================== CMetaUtil::InterfaceSupportsErrorInfo Standard ATL implementation ===================================================================*/ STDMETHODIMP CMetaUtil::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IMetaUtil, }; for (int i=0;i *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, OLE2T(bstrBaseKey)); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IKeyCollection hr = pObj->QueryInterface(IID_IKeyCollection, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(ppIReturn != NULL); return S_OK; } /*=================================================================== CMetaUtil::EnumAllKeys Do a deep (recursive) enumeration of subkeys Parameters: bstrBaseKey [in] Key to enumerate the subkeys of ppIReturn [out, retval] interface for the ouput key collection Returns: E_OUTOFMEMORY if allocation fails. E_INVALIDARG if ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::EnumAllKeys(BSTR bstrBaseKey, IKeyCollection ** ppIReturn) { TRACE0("MetaUtil: CMetaUtil::EnumAllKeys\n"); ASSERT_NULL_OR_POINTER(bstrBaseKey, OLECHAR); ASSERT_NULL_OR_POINTER(ppIReturn, IKeyCollection *); if (ppIReturn == NULL) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; HRESULT hr; // Create the Flat Keys Collection CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, OLE2T(bstrBaseKey)); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IKeyCollection hr = pObj->QueryInterface(IID_IKeyCollection, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(ppIReturn != NULL); return S_OK; } /*=================================================================== CMetaUtil::EnumProperties Do an enumeration of properties Parameters: bstrBaseKey [in] Key to enumerate the properties of ppIReturn [out, retval] interface for the ouput property collection Returns: E_OUTOFMEMORY if allocation fails. E_INVALIDARG if ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::EnumProperties(BSTR bstrKey, IPropertyCollection **ppIReturn) { TRACE0("MetaUtil: CMetaUtil::EnumProperties\n"); ASSERT_NULL_OR_POINTER(bstrKey, OLECHAR); ASSERT_NULL_OR_POINTER(ppIReturn, IKeyCollection *); if (ppIReturn == NULL) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; HRESULT hr; // Create the Flat Keys Collection CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(m_pIMeta, m_pCSchemaTable, OLE2T(bstrKey)); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IPropertyCollection hr = pObj->QueryInterface(IID_IPropertyCollection, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(ppIReturn != NULL); return S_OK; } /*=================================================================== CMetaUtil::CreateKey Create a new key Parameters: bstrKey [in] Key to create Returns: E_INVALIDARG if bstrKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::CreateKey(BSTR bstrKey) { TRACE0("MetaUtil: CMetaUtil::CreateKey\n"); ASSERT_NULL_OR_POINTER(bstrKey, OLECHAR); if (bstrKey == NULL) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszKey[ADMINDATA_MAX_NAME_LEN]; _tcscpy(tszKey,OLE2T(bstrKey)); CannonizeKey(tszKey); return ::CreateKey(m_pIMeta, tszKey); } /*=================================================================== CMetaUtil::DeleteKey Delete a key Parameters: bstrKey [in] Key to delete Returns: E_INVALIDARG if bstrKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::DeleteKey(BSTR bstrKey) { TRACE0("MetaUtil: CMetaUtil::DeleteKey\n"); ASSERT_NULL_OR_POINTER(bstrKey, OLECHAR); if (bstrKey == NULL) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszKey[ADMINDATA_MAX_NAME_LEN]; _tcscpy(tszKey,OLE2T(bstrKey)); CannonizeKey(tszKey); return ::DeleteKey(m_pIMeta, tszKey); } /*=================================================================== CMetaUtil::RenameKey Rename a key Parameters: bstrOldName [in] Original Key Name bstrNewName [in] New key name Returns: E_INVALIDARG if bstrOldName == NULL OR bstrNewName == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::RenameKey(BSTR bstrOldName, BSTR bstrNewName) { TRACE0("MetaUtil: CMetaUtil::RenameKey\n"); ASSERT_NULL_OR_POINTER(bstrOldName, OLECHAR); ASSERT_NULL_OR_POINTER(bstrNewName, OLECHAR); if ((bstrOldName == NULL) || (bstrNewName == NULL)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; HRESULT hr; TCHAR tszOldName[ADMINDATA_MAX_NAME_LEN]; TCHAR tszNewName[ADMINDATA_MAX_NAME_LEN]; _tcscpy(tszOldName, OLE2T(bstrOldName)); CannonizeKey(tszOldName); _tcscpy(tszNewName, OLE2T(bstrNewName)); CannonizeKey(tszNewName); // Figure out the key's common root TCHAR tszParent[ADMINDATA_MAX_NAME_LEN]; int i; i = 0; while ((tszOldName[i] != _T('\0')) && (tszNewName[i] != _T('\0')) && (tszOldName[i] == tszNewName[i])) { tszParent[i] = tszOldName[i]; i++; } if (i == 0) { // Nothing in common tszParent[i] = _T('\0'); } else { // Back up to the first slash while ((i > 0) && (tszParent[i] != _T('/'))) { i--; } // Cut it off at the slash tszParent[i] = _T('\0'); } int iParentKeyLen; iParentKeyLen = _tcslen(tszParent); LPTSTR tszRelOldName; LPTSTR tszRelNewName; // Figure out the relative new and old names tszRelOldName = tszOldName + iParentKeyLen; if (*tszRelOldName == _T('/')) { tszRelOldName++; } tszRelNewName = tszNewName + iParentKeyLen; if (*tszRelNewName == _T('/')) { tszRelNewName++; } // Open the parent METADATA_HANDLE hMDParentKey; hr = m_pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(tszParent), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, MUTIL_OPEN_KEY_TIMEOUT, &hMDParentKey); if (FAILED(hr)) { return ::ReportError(hr); } // Rename the key hr = m_pIMeta->RenameKey(hMDParentKey, T2W(tszRelOldName), T2W(tszRelNewName)); if (FAILED(hr)) { m_pIMeta->CloseKey(hMDParentKey); return ::ReportError(hr); } // Close the parent m_pIMeta->CloseKey(hMDParentKey); return S_OK; } /*=================================================================== CMetaUtil::CopyKey Copy a key Parameters: bstrSrcKey [in] Source Key Name bstrDestKey [in] Destination key name fOverwrite [in] If true then already existing properties at destination are overwritten. Returns: E_INVALIDARG if bstrSrcKey == NULL OR bstrDestKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::CopyKey(BSTR bstrSrcKey, BSTR bstrDestKey, BOOL fOverwrite) { TRACE0("MetaUtil: CMetaUtil::CopyKey\n"); ASSERT_NULL_OR_POINTER(bstrSrcKey, OLECHAR); ASSERT_NULL_OR_POINTER(bstrDestKey, OLECHAR); if ((bstrSrcKey == NULL) || (bstrDestKey == NULL)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszSrcKey[ADMINDATA_MAX_NAME_LEN]; TCHAR tszDestKey[ADMINDATA_MAX_NAME_LEN]; _tcscpy(tszSrcKey, OLE2T(bstrSrcKey)); CannonizeKey(tszSrcKey); _tcscpy(tszDestKey, OLE2T(bstrDestKey)); CannonizeKey(tszDestKey); return ::CopyKey(m_pIMeta, tszSrcKey, tszDestKey, fOverwrite, TRUE); } /*=================================================================== CMetaUtil::MoveKey Move a key Parameters: bstrSrcKey [in] Source Key Name bstrDestKey [in] Destination key name fOverwrite [in] If true then already existing properties at destination are overwritten. Returns: E_INVALIDARG if bstrSrcKey == NULL OR bstrDestKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::MoveKey(BSTR bstrSrcKey, BSTR bstrDestKey, BOOL fOverwrite) { TRACE0("MetaUtil: CMetaUtil::MoveKey\n"); ASSERT_NULL_OR_POINTER(bstrSrcKey, OLECHAR); ASSERT_NULL_OR_POINTER(bstrDestKey, OLECHAR); if ((bstrSrcKey == NULL) || (bstrDestKey == NULL)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszSrcKey[ADMINDATA_MAX_NAME_LEN]; TCHAR tszDestKey[ADMINDATA_MAX_NAME_LEN]; _tcscpy(tszSrcKey, OLE2T(bstrSrcKey)); CannonizeKey(tszSrcKey); _tcscpy(tszDestKey, OLE2T(bstrDestKey)); CannonizeKey(tszDestKey); return ::CopyKey(m_pIMeta, tszSrcKey, tszDestKey, fOverwrite, FALSE); } /*=================================================================== CMetaUtil::GetProperty Gets a property object from the metabase. Parameters: bstrKey [in] Key containing property to get varId [in] Identifier of property to get. Either the Id (number) or Name (string). ppIReturn [out, retval] Interface for retreived property. Returns: E_INVALIDARG if bstrKey == NULL or ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::GetProperty(BSTR bstrKey, VARIANT varId, IProperty **ppIReturn) { TRACE0("MetaUtil: CMetaUtil::GetProperty\n"); ASSERT_NULL_OR_POINTER(bstrKey, OLECHAR); ASSERT_NULL_OR_POINTER(ppIReturn, IProperty *); if ((bstrKey == NULL) || (ppIReturn == NULL)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszKey[ADMINDATA_MAX_NAME_LEN]; _tcscpy(tszKey,OLE2T(bstrKey)); CannonizeKey(tszKey); return ::GetProperty(m_pIMeta, m_pCSchemaTable, tszKey, varId, ppIReturn); } /*=================================================================== CMetaUtil::CreateProperty Creates a property object that can be written to the Metbase or retreives the property if it already exists. Parameters: bstrKey [in] Key containing property to get varId [in] Identifier of property to get. Either the Id (number) or Name (string). ppIReturn [out, retval] Interface for retreived property. Returns: E_INVALIDARG if bstrKey == NULL or ppIReturn == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::CreateProperty(BSTR bstrKey, VARIANT varId, IProperty **ppIReturn) { TRACE0("MetaUtil: CMetaUtil::CreateProperty\n"); ASSERT_NULL_OR_POINTER(bstrKey, OLECHAR); ASSERT_NULL_OR_POINTER(ppIReturn, IProperty *); if ((bstrKey == NULL) || (ppIReturn == NULL)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszKey[ADMINDATA_MAX_NAME_LEN]; _tcscpy(tszKey,OLE2T(bstrKey)); CannonizeKey(tszKey); return ::CreateProperty(m_pIMeta, m_pCSchemaTable, tszKey, varId, ppIReturn); } /*=================================================================== CMetaUtil::DeleteProperty Deletes a property from the metabase. Parameters: bstrKey [in] Key containing property to get varId [in] Identifier of property to get. Either the Id (number) or Name (string). Returns: E_INVALIDARG if bstrKey == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::DeleteProperty(BSTR bstrKey, VARIANT varId) { TRACE0("MetaUtil: CMetaUtil::DeleteProperty\n"); ASSERT_NULL_OR_POINTER(bstrKey, OLECHAR); if (bstrKey == NULL) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszKey[ADMINDATA_MAX_NAME_LEN]; _tcscpy(tszKey,OLE2T(bstrKey)); CannonizeKey(tszKey); return ::DeleteProperty(m_pIMeta, m_pCSchemaTable, tszKey, varId); } /*=================================================================== CMetaUtil::ExpandString Expands a string with environment variables. Maximum output is 1024 bytes. Parameters: bstrIn [in] String to expand pbstrRet [out, retval] Expanded string Returns: E_INVALIDARG if bstrIn == NULL or pbstrRet == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::ExpandString(BSTR bstrIn, BSTR *pbstrRet) { ASSERT_POINTER(bstrIn, OLECHAR); ASSERT_NULL_OR_POINTER(pbstrRet, BSTR); if ((bstrIn == NULL) || (pbstrRet == NULL)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszRet[1024]; int iRet; iRet = ExpandEnvironmentStrings(OLE2T(bstrIn), tszRet, 1024); if (iRet == 0) { ::ReportError(GetLastError()); } *pbstrRet = T2BSTR(tszRet); return S_OK; } /*=================================================================== MetaUtil::PropIdToName Converts a property Id to its name, as listed in _Machine_/Schema/Properties/Names Parameters: bstrKey [in] Approximate key where property is located, needed to determine what schema to use. lId [in] Id of property pbstrName [out, retval] Output name of property Returns: E_INVALIDARG if bstrKey == NULL or pbstrName == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::PropIdToName(BSTR bstrKey, long lId, BSTR *pbstrName) { ASSERT_NULL_OR_POINTER(bstrKey, OLECHAR); ASSERT_NULL_OR_POINTER(pbstrName, BSTR); if ((bstrKey == NULL) || (pbstrName == NULL)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszKey[ADMINDATA_MAX_NAME_LEN]; CPropInfo *pCPropInfo; // Convert the base key to cannonical form _tcscpy(tszKey, OLE2T(bstrKey)); CannonizeKey(tszKey); // Get the property info from the Schema Table pCPropInfo = m_pCSchemaTable->GetPropInfo(tszKey, lId); // 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; } /*=================================================================== MetaUtil::PropNameToId Converts a property name to its id, as listed in _Machine_/Schema/Properties/Names Parameters: bstrKey [in] Approximate key where property is located, needed to determine what schema to use. pbstrName [in] Name of property lId [out, retval] Output id of property Returns: E_INVALIDARG if bstrKey == NULL OR bstrName == NULL OR plId == NULL S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::PropNameToId(BSTR bstrKey, BSTR bstrName, long *plId) { ASSERT_NULL_OR_POINTER(bstrKey, OLECHAR); ASSERT_NULL_OR_POINTER(bstrName, OLECHAR); ASSERT_NULL_OR_POINTER(plId, long); if ((bstrKey == NULL) || (bstrName == NULL) || (plId == NULL)) { return ::ReportError(E_INVALIDARG); } USES_CONVERSION; TCHAR tszKey[ADMINDATA_MAX_NAME_LEN]; CPropInfo *pCPropInfo; // Convert the base key to cannonical form _tcscpy(tszKey, OLE2T(bstrKey)); CannonizeKey(tszKey); // Get the property info from the Schema Table pCPropInfo = m_pCSchemaTable->GetPropInfo(tszKey, OLE2T(bstrName)); // Did we find it? if (pCPropInfo == NULL) { // No, return 0 *plId = 0; } else { // Yes, return the id *plId = pCPropInfo->GetId(); } return S_OK; } /*=================================================================== MetaUtil::get_Config Gets the value of a configuration setting. Valid Settings: MaxPropertySize MaxKeySize MaxNumberOfErrors Parameters: bstrSetting [in] Name of the setting pvarValue [out, retval] Value of the setting Returns: E_INVALIDARG if bstrSettting doesn't match any known settings S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::get_Config(BSTR bstrSetting, VARIANT *pvarValue) { ASSERT_POINTER(bstrSetting, OLECHAR); ASSERT_POINTER(pvarValue, VARIANT); USES_CONVERSION; LPTSTR tszSetting; if( !bstrSetting ) { return ::ReportError(E_INVALIDARG); } VariantInit(pvarValue); tszSetting = OLE2T(bstrSetting); if (_tcsicmp(tszSetting, _T("MaxPropertySize")) == 0) { V_VT(pvarValue) = VT_I4; V_I4(pvarValue) = m_dwMaxPropSize; } else if (_tcsicmp(tszSetting, _T("MaxKeySize")) == 0) { V_VT(pvarValue) = VT_I4; V_I4(pvarValue) = m_dwMaxKeySize; } else if (_tcsicmp(tszSetting, _T("MaxNumberOfErrors")) == 0) { V_VT(pvarValue) = VT_I4; V_I4(pvarValue) = m_dwMaxNumErrors; } else { return ::ReportError(E_INVALIDARG); } return S_OK; } /*=================================================================== MetaUtil::put_Config Sets the value of a configuration setting. Valid Settings: MaxPropertySize MaxKeySize MaxNumberOfErrors Parameters: bstrSetting [in] Name of the setting varValue [out, retval] New value of the setting Returns: E_INVALIDARG if bstrSettting doesn't match any known settings or if varValue is of an unexpected subtype. S_OK on success ===================================================================*/ STDMETHODIMP CMetaUtil::put_Config(BSTR bstrSetting, VARIANT varValue) { ASSERT_POINTER(bstrSetting, OLECHAR); USES_CONVERSION; HRESULT hr; LPTSTR tszSetting; tszSetting = OLE2T(bstrSetting); // Cleanup any IDispatch or byref stuff CComVariant varValue2; hr = VariantResolveDispatch(&varValue, &varValue2); if (FAILED(hr)) { return hr; } if (_tcsicmp(tszSetting, _T("MaxPropertySize")) == 0) { // Set Maximum Property Size switch (V_VT(&varValue2)) { 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 if (FAILED(hr = VariantChangeType(&varValue2, &varValue2, 0, VT_UI4))) { return ::ReportError(hr); } // fallthru to VT_UI4 case VT_UI4: m_dwMaxPropSize = V_UI4(&varValue2); break; default: // Unexpected data type return ::ReportError(E_INVALIDARG); } } else if (_tcsicmp(tszSetting, _T("MaxKeySize")) == 0) { // Set Maximum Key Size switch (V_VT(&varValue2)) { 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 if (FAILED(hr = VariantChangeType(&varValue2, &varValue2, 0, VT_UI4))) { return ::ReportError(hr); } // fallthru to VT_UI4 case VT_UI4: m_dwMaxKeySize = V_UI4(&varValue2); break; default: // Unexpected data type return ::ReportError(E_INVALIDARG); } } else if (_tcsicmp(tszSetting, _T("MaxNumberOfErrors")) == 0) { // Set Maximum Number of Errors switch (V_VT(&varValue2)) { 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 if (FAILED(hr = VariantChangeType(&varValue2, &varValue2, 0, VT_UI4))) { return ::ReportError(hr); } // fallthru to VT_UI4 case VT_UI4: m_dwMaxNumErrors = V_UI4(&varValue2); break; default: // Unexpected data type return ::ReportError(E_INVALIDARG); } } else { return ::ReportError(E_INVALIDARG); } return S_OK; } /*------------------------------------------------------------------ * Methods also supported by the collections * * Actual implementation here to avoid redundant code */ /*=================================================================== CreateKey Create a new key Parameters: pIMeta [in] Smart pointer to metabase, passed by reference to avoid the copy and unneeded AddRef/Release. Would have used const, however the '->' operator would not work. tszKey [in] Key to create Returns: E_INVALIDARG if bstrKey == NULL S_OK on success ===================================================================*/ HRESULT CreateKey(CComPtr &pIMeta, LPCTSTR tszKey) { ASSERT(pIMeta.p != NULL); ASSERT_STRING(tszKey); USES_CONVERSION; HRESULT hr; TCHAR tszParent[ADMINDATA_MAX_NAME_LEN]; TCHAR tszChild[ADMINDATA_MAX_NAME_LEN]; ::SplitKey(tszKey, tszParent, tszChild); // Open the parent key METADATA_HANDLE hMDParent; hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(tszParent), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, MUTIL_OPEN_KEY_TIMEOUT, &hMDParent); if (FAILED(hr)) { return ::ReportError(hr); } // Create the child hr = pIMeta->AddKey(hMDParent, T2W(tszChild)); if (FAILED(hr)) { pIMeta->CloseKey(hMDParent); return ::ReportError(hr); } // Close the parent key pIMeta->CloseKey(hMDParent); return S_OK; } /*=================================================================== DeleteKey Delete a key Parameters: pIMeta [in] Smart pointer to metabase, passed by reference to avoid the copy and unneeded AddRef/Release. Would have used const, however the '->' operator would not work. tszKey [in] Key to delete Returns: E_INVALIDARG if pbSuccess == NULL S_OK on success ===================================================================*/ HRESULT DeleteKey(CComPtr &pIMeta, LPCTSTR tszKey) { ASSERT(pIMeta.p != NULL); ASSERT_STRING(tszKey); USES_CONVERSION; HRESULT hr; TCHAR tszParent[ADMINDATA_MAX_NAME_LEN]; TCHAR tszChild[ADMINDATA_MAX_NAME_LEN]; ::SplitKey(tszKey, tszParent, tszChild); // Open the parent key METADATA_HANDLE hMDParent; hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(tszParent), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, MUTIL_OPEN_KEY_TIMEOUT, &hMDParent); if (FAILED(hr)) { return ::ReportError(hr); } // Delete the child hr = pIMeta->DeleteKey(hMDParent, T2W(tszChild)); if (FAILED(hr)) { pIMeta->CloseKey(hMDParent); return ::ReportError(hr); } // Close the parent key pIMeta->CloseKey(hMDParent); return S_OK; } /*=================================================================== CMetaUtil::CopyKey Copy or move a key Parameters: bstrSrcKey [in] Source Key Name bstrDestKey [in] Destination key name fOverwrite [in] If true then already existing properties at destination are overwritten. fCopy [in] If true than copy the key, else move it Returns: S_OK on success ===================================================================*/ HRESULT CopyKey(CComPtr &pIMeta, LPTSTR tszSrcKey, LPTSTR tszDestKey, BOOL fOverwrite, BOOL fCopy) { ASSERT(pIMeta.p != NULL); ASSERT_STRING(tszSrcKey); ASSERT_STRING(tszDestKey); USES_CONVERSION; HRESULT hr; // Check for overlap TCHAR tszParent[ADMINDATA_MAX_NAME_LEN]; int i; i = 0; while ((tszSrcKey[i] != _T('\0')) && (tszDestKey[i] != _T('\0')) && (tszSrcKey[i] == tszDestKey[i])) { tszParent[i] = tszSrcKey[i]; i++; } // Terminate tszParent tszParent[i] = _T('\0'); if (i == 0) { // Nothing in common TCHAR tszSrcParent[ADMINDATA_MAX_NAME_LEN]; TCHAR tszSrcChild[ADMINDATA_MAX_NAME_LEN]; TCHAR tszDestParent[ADMINDATA_MAX_NAME_LEN]; TCHAR tszDestChild[ADMINDATA_MAX_NAME_LEN]; ::SplitKey(tszSrcKey, tszSrcParent, tszSrcChild); ::SplitKey(tszDestKey, tszDestParent, tszDestChild); // Open the parent source key METADATA_HANDLE hMDSrcParent; hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(tszSrcParent), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, MUTIL_OPEN_KEY_TIMEOUT, &hMDSrcParent); if (FAILED(hr)) { return ::ReportError(hr); } // Open the parent dest key METADATA_HANDLE hMDDestParent; hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(tszDestParent), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, MUTIL_OPEN_KEY_TIMEOUT, &hMDDestParent); if (FAILED(hr)) { return ::ReportError(hr); } // Copy the children hr = pIMeta->CopyKey(hMDSrcParent, T2W(tszSrcChild), hMDDestParent, T2W(tszDestChild), fOverwrite, fCopy); if (FAILED(hr)) { pIMeta->CloseKey(hMDSrcParent); pIMeta->CloseKey(hMDDestParent); return ::ReportError(hr); } // Close the parents pIMeta->CloseKey(hMDSrcParent); pIMeta->CloseKey(hMDDestParent); } else { // Something in common // Back up to the first slash while ((i > 0) && (tszParent[i] != _T('/'))) { i--; } // Cut it off at the slash tszParent[i] = _T('\0'); int iParentKeyLen; iParentKeyLen = _tcslen(tszParent); LPTSTR tszSrcChild; LPTSTR tszDestChild; // Figure out the relative new and old names tszSrcChild = tszSrcKey + iParentKeyLen; if (*tszSrcChild == _T('/')) { tszSrcChild++; } tszDestChild = tszDestKey + iParentKeyLen; if (*tszDestChild == _T('/')) { tszDestChild++; } // Open the parent key METADATA_HANDLE hMDParent; hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(tszParent), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, MUTIL_OPEN_KEY_TIMEOUT, &hMDParent); if (FAILED(hr)) { return ::ReportError(hr); } // Copy the children hr = pIMeta->CopyKey(hMDParent, T2W(tszSrcChild), hMDParent, T2W(tszDestChild), fOverwrite, fCopy); if (FAILED(hr)) { pIMeta->CloseKey(hMDParent); return ::ReportError(hr); } // Close the parent pIMeta->CloseKey(hMDParent); } return S_OK; } /*=================================================================== GetProperty Gets a property object from the metabase. Parameters: pIMeta [in] Smart pointer to metabase, passed by reference to avoid the copy and unneeded AddRef/Release. Would have used const, however the '->' operator would not work. pCSchemaTable [in] Metabase schema table to use to look up property names tszKey [in] Key containing property to get varId [in] Identifier of property to get. Either the Id (number) or Name (string). ppIReturn [out, retval] Interface for retreived property. Returns: S_OK on success ===================================================================*/ HRESULT GetProperty(CComPtr &pIMeta, CMetaSchemaTable *pCSchemaTable, LPCTSTR tszKey, VARIANT varId, IProperty **ppIReturn) { ASSERT(pIMeta != NULL); ASSERT_STRING(tszKey); ASSERT_POINTER(ppIReturn, IProperty *); HRESULT hr; DWORD dwId; // Figure out the property id hr = ::VarToMetaId(pCSchemaTable, tszKey, varId, &dwId); if (FAILED(hr)) { return ::ReportError(hr); } // Create the property object CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(pIMeta, pCSchemaTable, tszKey, dwId, FALSE); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IProperty hr = pObj->QueryInterface(IID_IProperty, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); return S_OK; } /*=================================================================== CreateProperty Creates a property object that can be written to the Metbase or retreives the property if it already exists. Parameters: pIMeta [in] Smart pointer to metabase, passed by reference to avoid the copy and unneeded AddRef/Release. Would have used const, however the '->' operator would not work. pCSchemaTable [in] Metabase schema table to use to look up property names tszKey [in] Key containing property to get varId [in] Identifier of property to get. Either the Id (number) or Name (string). ppIReturn [out, retval] Interface for retreived property. Returns: S_OK on success ===================================================================*/ HRESULT CreateProperty(CComPtr &pIMeta, CMetaSchemaTable *pCSchemaTable, LPCTSTR tszKey, VARIANT varId, IProperty **ppIReturn) { ASSERT(pIMeta.p != NULL); ASSERT_STRING(tszKey); ASSERT_POINTER(ppIReturn, IProperty *); HRESULT hr; DWORD dwId; // Figure out the property id hr = ::VarToMetaId(pCSchemaTable, tszKey, varId, &dwId); if (FAILED(hr)) { return ::ReportError(hr); } // Create the property object CComObject *pObj = NULL; ATLTRY(pObj = new CComObject); if (pObj == NULL) { return ::ReportError(E_OUTOFMEMORY); } hr = pObj->Init(pIMeta, pCSchemaTable, tszKey, dwId, TRUE); if (FAILED(hr)) { return ::ReportError(hr); } // Set the interface to IProperty hr = pObj->QueryInterface(IID_IProperty, (void **) ppIReturn); if (FAILED(hr)) { return ::ReportError(hr); } ASSERT(*ppIReturn != NULL); return S_OK; } /*=================================================================== DeleteProperty Deletes a property from the metabase. Parameters: pIMeta [in] Smart pointer to metabase, passed by reference to avoid the copy and unneeded AddRef/Release. Would have used const, however the '->' operator would not work. pCSchemaTable [in] Metabase schema table to use to look up property names tszKey [in] Key containing property to get varId [in] Identifier of property to get. Either the Id (number) or Name (string). Returns: S_OK on success ===================================================================*/ HRESULT DeleteProperty(CComPtr &pIMeta, CMetaSchemaTable *pCSchemaTable, LPTSTR tszKey, VARIANT varId) { ASSERT(pIMeta.p != NULL); ASSERT_STRING(tszKey); USES_CONVERSION; HRESULT hr; DWORD dwId; hr = ::VarToMetaId(pCSchemaTable, tszKey, varId, &dwId); if (FAILED(hr)) { return ::ReportError(hr); } // Open the key METADATA_HANDLE hMDKey; hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, T2W(tszKey), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, MUTIL_OPEN_KEY_TIMEOUT, &hMDKey); if (FAILED(hr)) { return ::ReportError(hr); } // Delete the property hr = pIMeta->DeleteData(hMDKey, NULL, dwId, ALL_METADATA); if (FAILED(hr)) { pIMeta->CloseKey(hMDKey); return ::ReportError(hr); } // Close the key pIMeta->CloseKey(hMDKey); return S_OK; } /*=================================================================== VarToMetaId Converts a variant to a metabase property id. IDispatch is resolved, strings are looked up in the schema property list and integers are converted to a DWORD. Parameters: pCSchemaTable [in] Metabase schema table to use to look up property names tszKey [in] Key the property is under (needed to get the right schema) varId [in] Variant to resolve pdwId [out] Metabase property Id that varId resolved to Returns: E_INVALIDARG if varId subtype isn't an integer or string ERROR_FILE_NOT_FOUND if varId is a BSTR that doesn't match any property names. S_OK on success ===================================================================*/ HRESULT VarToMetaId(CMetaSchemaTable *pCSchemaTable, LPCTSTR tszKey, VARIANT varId, DWORD *pdwId) { ASSERT_STRING(tszKey); ASSERT_POINTER(pdwId, DWORD); USES_CONVERSION; HRESULT hr; CComVariant varId2; CPropInfo *pCPropInfo; // VBScript can call us with a VARIANT that isn't a simple type, // such as VT_VARIANT|VT_BYREF. This resolves it to a simple type. if (FAILED(hr = VariantResolveDispatch(&varId, &varId2))) return hr; switch (V_VT(&varId2)) { case VT_BSTR: // Look up the property name pCPropInfo = pCSchemaTable->GetPropInfo(tszKey, OLE2T(V_BSTR(&varId2))); if (pCPropInfo == NULL) { return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } *pdwId = pCPropInfo->GetId(); return S_OK; break; 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 REG_DWORD if (FAILED(hr = VariantChangeType(&varId2, &varId2, 0, VT_UI4))) return hr; // fallthru to VT_UI4 case VT_UI4: *pdwId = V_UI4(&varId2); break; default: return E_INVALIDARG; // Cannot handle this data type } return S_OK; }