///////////////////////////////////////////////////////////////////////////// // TCPropBagOnRegKey.cpp | Implementation of the CTCPropBagOnRegKey // class, which implements the CLSID_TCPropBagOnRegKey component object. // #include "pch.h" #include "TCPropBagOnRegKey.h" #include <..\TCAtl\ObjectMap.h> ///////////////////////////////////////////////////////////////////////////// // CTCPropBagOnRegKey TC_OBJECT_EXTERN_IMPL(CTCPropBagOnRegKey) ///////////////////////////////////////////////////////////////////////////// // Static Initialization const _bstr_t CTCPropBagOnRegKey::m_szVariantType (OLESTR(".vt")); const _bstr_t CTCPropBagOnRegKey::m_szServer (OLESTR(".server")); const _bstr_t CTCPropBagOnRegKey::m_szProgID (OLESTR(".progid")); const _bstr_t CTCPropBagOnRegKey::m_szCLSID (OLESTR(".clsid")); const _bstr_t CTCPropBagOnRegKey::m_szLowerBound (OLESTR(".lBound")); const _bstr_t CTCPropBagOnRegKey::m_szElementCount (OLESTR(".elements")); const _bstr_t CTCPropBagOnRegKey::m_szElementFmt (OLESTR("%d")); const USHORT CTCPropBagOnRegKey::m_wChangeTypeFlags = VARIANT_NOVALUEPROP | VARIANT_ALPHABOOL | VARIANT_LOCALBOOL; ///////////////////////////////////////////////////////////////////////////// // Construction / Destruction CTCPropBagOnRegKey::CTCPropBagOnRegKey() : m_pParent(NULL), m_bOwnKey(false) { } void CTCPropBagOnRegKey::FinalRelease() { ClosePrevious(); } ///////////////////////////////////////////////////////////////////////////// // Attributes _bstr_t CTCPropBagOnRegKey::_GetSubkey() { // Practice safe threading XLock lock(this); // Get the parent's subkey, if any _bstr_t str; if (m_pParent) { str = m_pParent->_GetSubkey(); if (str.length()) str += L"\\"; } // Concatenate this object's subkey return str += m_strSubkey; } _bstr_t CTCPropBagOnRegKey::_GetServer(const _bstr_t& strName) { // Open the subkey with the specified name CRegKey subkey; if (!strName.length()) subkey = m_key; else if (!m_key.Exists(strName) || !subkey.Open(m_key, strName)) return _bstr_t(); // Read the Server of the object saved on the specified subkey _bstr_t strServer; if (!subkey._GetString(m_szServer, strServer)) strServer.Empty(); return strServer; } HRESULT CTCPropBagOnRegKey::_SetServer(const _bstr_t& strName, const _bstr_t& strServer) { // Open the subkey with the specified name CRegKey subkey; if (!strName.length()) subkey = m_key; else if (!m_key.Exists(strName) || !subkey.Open(m_key, strName)) return HRESULT_FROM_WIN32(GetLastError()); // Write the Server of the object saved on the specified subkey subkey.WriteString(m_szServer, strServer); // Indicate success return S_OK; } HRESULT CTCPropBagOnRegKey::_GetObjectCLSID(const _bstr_t& strName, CLSID& clsid) { // Read the CLSID of the object saved on the specified subkey CRegKey subkey; _bstr_t strObjectID; return _GetObjectCLSID(strName, clsid, subkey, strObjectID, NULL); } ///////////////////////////////////////////////////////////////////////////// // Operations HRESULT CTCPropBagOnRegKey::_CreateObject(const _bstr_t& strName, IUnknown** ppunkObj, IErrorLog* pErrorLog, IUnknown* punkOuter) { // Get the Server of the object saved on the specified subkey _bstr_t strServer(GetServer(strName)); if (!strServer.length()) return CreateLocalObject(strName, ppunkObj, pErrorLog, punkOuter); return CreateRemoteObject(strServer, strName, ppunkObj, pErrorLog); } HRESULT CTCPropBagOnRegKey::_CreateLocalObject(const _bstr_t& strName, IUnknown** ppunkObj, IErrorLog* pErrorLog, IUnknown* punkOuter) { // Read the CLSID of the object saved on the specified subkey CLSID clsid; CRegKey subkey; _bstr_t strObjectID; HRESULT hr = GetObjectCLSID(strName, clsid, subkey, strObjectID, pErrorLog); if (FAILED(hr)) return hr; // Create the object DWORD dwCtx = CLSCTX_ALL | CLSCTX_REMOTE_SERVER; IPersistPropertyBagPtr pppb; void** ppv = reinterpret_cast(&pppb); hr = CoCreateInstance(clsid, punkOuter, dwCtx, __uuidof(pppb), ppv); if (E_INVALIDARG == hr) { dwCtx &= ~CLSCTX_REMOTE_SERVER; hr = CoCreateInstance(clsid, punkOuter, dwCtx, __uuidof(pppb), ppv); } if (FAILED(hr)) { // Use local resources MCLibRes res; // Format a description string _bstr_t strDesc; strDesc.Format(IDS_FMT_FAIL_CREATE_CLSID, strObjectID); // Log the error USES_CONVERSION; return LogError("CreateObject", strDesc, hr, T2COLE(strName), pErrorLog); } // Load the object's persistent properties CComObjectStack bag; bag.Init(subkey, strName, this); hr = pppb->Load(&bag, pErrorLog); if (FAILED(hr)) return hr; // Detach the IUnknown* and copy to the [out] parameter, don't Release it *ppunkObj = pppb.Detach(); // Indicate success return S_OK; } HRESULT CTCPropBagOnRegKey::_CreateRemoteObject(const _bstr_t& strServer, const _bstr_t& strName, IUnknown** ppunkObj, IErrorLog* pErrorLog) { // If DCOM is not installed, take appropriate action MCOle32_dll& libOle32 = GetOle32Lib(); if (!libOle32.Exists_CoCreateInstanceEx()) return !strServer.length() ? CreateLocalObject(strName, ppunkObj, pErrorLog) : E_UNEXPECTED; // Read the CLSID of the object saved on the specified subkey CLSID clsid; CRegKey subkey; _bstr_t strObjectID; HRESULT hr = GetObjectCLSID(strName, clsid, subkey, strObjectID, pErrorLog); if (FAILED(hr)) return hr; // Create the object USES_CONVERSION; _bstr_t bstrServer(strServer); COSERVERINFO csi = {0, bstrServer, NULL, 0}; MULTI_QI mqi = {&IID_IPersistPropertyBag, NULL, 0}; DWORD dwCtx = CLSCTX_ALL | CLSCTX_REMOTE_SERVER; hr = libOle32.CoCreateInstanceEx(clsid, NULL, dwCtx, &csi, 1, &mqi); if (FAILED(hr)) { // Use local resources MCLibRes res; // Format a description string _bstr_t strDesc; strDesc.Format(IDS_FMT_FAIL_CREATE_CLSID, strObjectID); // Log the error USES_CONVERSION; return LogError("CreateObject", strDesc, hr, T2COLE(strName), pErrorLog); } // Attach the interface pointer to the smart pointer IPersistPropertyBagPtr pppb((IPersistPropertyBag*)mqi.pItf, false); // Load the object's persistent properties CComObjectStack bag; bag.Init(subkey, strName, this); hr = pppb->Load(&bag, pErrorLog); if (FAILED(hr)) return hr; // Detach the IUnknown* and copy to the [out] parameter, don't Release it *ppunkObj = pppb.Detach(); // Indicate success return S_OK; } HRESULT CTCPropBagOnRegKey::_LoadObject(const _bstr_t& strName, IUnknown* punkObj, IErrorLog* pErrorLog) { // Validate the specified parameters if (IsBadReadPtr(punkObj)) return E_POINTER; if (NULL != pErrorLog && IsBadReadPtr(pErrorLog)) return E_POINTER; // Open the subkey with the specified name CRegKey subkey; if (!strName.length()) subkey = m_key; else if (!m_key.Exists(strName) || !subkey.Open(m_key, strName)) return HRESULT_FROM_WIN32(GetLastError()); // QueryInterface for IPersistPropertyBag IPersistPropertyBagPtr pppb; HRESULT hr = punkObj->QueryInterface(__uuidof(pppb), (void**)&pppb); if (FAILED(hr)) return hr; // Load the object's persistent properties CComObjectStack bag; bag.Init(subkey, strName, this); hr = pppb->Load(&bag, pErrorLog); // Return the last HRESULT return hr; } HRESULT CTCPropBagOnRegKey::_SaveObject(const _bstr_t& strName, IUnknown* punkObj, BOOL bClearDirty, BOOL bSaveAllProperties) { // Validate the specified parameters if (IsBadReadPtr(punkObj)) return E_POINTER; // Create a subkey with the specified name CRegKey subkey; if (!strName.length()) subkey = m_key; else { m_key.DeleteValue(strName); m_key.RecurseDeleteKey(strName); if (!subkey.Open(m_key, strName)) return HRESULT_FROM_WIN32(GetLastError()); } // QueryInterface for IPersistPropertyBag IPersistPropertyBagPtr pppb; HRESULT hr = punkObj->QueryInterface(__uuidof(pppb), (void**)&pppb); if (FAILED(hr)) return hr; // Get the object's CLSID CLSID clsid; hr = pppb->GetClassID(&clsid); if (FAILED(hr)) return hr; // Attempt first to convert the object's CLSID to a ProgID LPOLESTR pszProgID = NULL; if (SUCCEEDED(ProgIDFromCLSID(clsid, &pszProgID))) { subkey.WriteString(m_szProgID, pszProgID); CoTaskMemFree(pszProgID); } else { // Convert the object's CLSID to a string OLECHAR szClassID[64]; StringFromGUID2(clsid, szClassID, sizeofArray(szClassID)); subkey.WriteString(m_szCLSID, szClassID); } // Write the variant type value subkey.WriteDWord(m_szVariantType, DWORD(VarTypeFromUnknown(punkObj))); // Save the persistent properties of the object CComObjectStack bag; bag.Init(subkey, strName, this); hr = pppb->Save(&bag, bClearDirty, bSaveAllProperties); // Return the last HRESULT return hr; } ///////////////////////////////////////////////////////////////////////////// // Implementation MCOle32_dll& CTCPropBagOnRegKey::_GetOle32Lib() { static MCOle32_dll libOle32; return libOle32; } HRESULT CTCPropBagOnRegKey::_GetObjectCLSID(const _bstr_t& strName, CLSID& clsid, CRegKey& subkey, _bstr_t& strObjectID, IErrorLog* pErrorLog) { USES_CONVERSION; // Clear the specified clsid, subkey, and strObjectID parameters clsid = CLSID_NULL; subkey.Close(); strObjectID.Empty(); // Open the subkey with the specified name if (!strName.length()) subkey = m_key; else if (!m_key.Exists(strName) || !subkey.Open(m_key, strName)) return HRESULT_FROM_WIN32(GetLastError()); // Use local resources for string loading MCLibRes res; // Read the object's CLSID string value HRESULT hr; _bstr_t strDesc; if (subkey.GetString(m_szCLSID, strObjectID)) { // Convert the object's CLSID string to a CLSID LPOLESTR pszObjectID = const_cast(T2COLE(strObjectID)); hr = CLSIDFromString(pszObjectID, &clsid); if (SUCCEEDED(hr)) return hr; // Format an error description string strDesc.Format(IDS_FMT_FAIL_CONVERT_CLSID, strObjectID); } else if (subkey.GetString(m_szProgID, strObjectID)) { // Convert the object's ProgID string to a CLSID hr = CLSIDFromProgID(T2COLE(strObjectID), &clsid); if (SUCCEEDED(hr)) return hr; // Format an error description string strDesc.Format(IDS_FMT_FAIL_CONVERT_CLSID, strObjectID); } else { // Save the last error hr = HRESULT_FROM_WIN32(GetLastError()); // Format an error description string strDesc.LoadString(IDS_FAIL_READ_CLSID); } // Log the error and return return LogError("GetObjectCLSID", strDesc, hr, T2COLE(strName), pErrorLog); } HRESULT CTCPropBagOnRegKey::_ReadVariantFromValue(CRegKey& key, const _bstr_t& strPropName, DWORD nType, int cbData, CComVariant& v, IErrorLog* pErrorLog) { // Read the value from the registry based on the value type switch (nType) { case REG_DWORD: { key.GetDWord(strPropName, (DWORD&)V_UI4(&v)); v.vt = VT_UI4; break; } case REG_SZ: { _bstr_t strValue; key.GetString(strPropName, strValue); v = strValue; break; } case REG_BINARY: { BYTE* pData = new BYTE[cbData]; key.GetBinary(strPropName, pData, cbData); CMemFile file(pData, cbData); CArchive ar(&file, CArchive::load, 0); ar >> v; ar.Close(); delete [] file.Detach(); break; } default: { // Use local resources MCLibRes res; // Format a description string _bstr_t strDesc; strDesc.Format(IDS_FMT_UNSUPPORTED_REGTYPE, nType); // Log the error USES_CONVERSION; return LogError("ReadVariantFromValue", strDesc, E_FAIL, T2COLE(strPropName), pErrorLog); } } // Indicate success return S_OK; } HRESULT CTCPropBagOnRegKey::_LogError(const _bstr_t& strFn, const _bstr_t& strDesc, HRESULT hr, LPCOLESTR pszPropName, IErrorLog* pErrorLog) { // Create the source string _bstr_t strSrc, strSubkey(GetSubkey()); if (!strSubkey.length()) strSrc.Format(_T("CTCPropBagOnRegKey::_%s(\"%ls\")"), strFn, pszPropName); else strSrc.Format(_T("CTCPropBagOnRegKey::_%s(\"%s\\%ls\")"), strFn, strSubkey, pszPropName); // Output a trace message under _DEBUG builds TRACE2("%s: %s\n", strSrc, strDesc); // Log an error, if an IErrorLog interface is specified if (NULL != pErrorLog) { // Allocate BSTR's from the formatted strings BSTR bstrSrc = strSrc.AllocSysString(); BSTR bstrDesc = strDesc.AllocSysString(); // Create an EXCEPINFO structure and log the error EXCEPINFO ei = {0, 0, bstrSrc, bstrDesc, NULL, 0, NULL, NULL, hr}; HRESULT hrAdd = pErrorLog->AddError(pszPropName, &ei); // Free the allocated BSTR's SysFreeString(bstrDesc); SysFreeString(bstrSrc); // Handle an unsuccessful IErrorLog::AddError call if (FAILED(hrAdd)) { TRACE1("%s: pErrorLog->AddError() failed!\n", strSrc); AfxThrowOleException(hrAdd); } } // Indicate failure return hr; } VARTYPE CTCPropBagOnRegKey::_GetSubkeyVarType(CRegKey& key, const _bstr_t& strPropName) { ASSERT(key.Exists(strPropName)); // Open the subkey with the specified name CRegKey subkey; if (!subkey.Open(key, strPropName)) return VT_EMPTY; // Read the subkey's variant type value DWORD dwVT; if (!subkey.GetDWord(m_szVariantType, dwVT)) return VT_EMPTY; // Return the subkey's variant type value return VARTYPE(dwVT); } HRESULT CTCPropBagOnRegKey::_ReadSafeArray(CRegKey& key, const _bstr_t& strPropName, VARIANT* pVar, IErrorLog* pErrorLog) { // Open the subkey with the specified name CRegKey subkey; VERIFY(subkey.Open(key, strPropName)); // Read the variant type of the registry value VARTYPE vt = GetSubkeyVarType(key, strPropName); ASSERT(vt & VT_ARRAY); // Remove the VT_ARRAY bit flag from the variant type vt &= ~VT_ARRAY; // Check for supported variant types switch (vt) { case VT_BOOL: case VT_I1: case VT_I2: case VT_I4: case VT_UI1: case VT_UI2: case VT_UI4: case VT_ERROR: case VT_R4: case VT_R8: case VT_DECIMAL: case VT_CY: case VT_DATE: case VT_BSTR: case VT_UNKNOWN: case VT_DISPATCH: case VT_VARIANT: break; default: { // Use local resources MCLibRes res; // Format a description string _bstr_t strDesc; strDesc.Format(IDS_FMT_UNSUP_ARRAY_VARTYPE, strPropName, vt, vt); // Log the error USES_CONVERSION; return LogError("ReadSafeArray", strDesc, E_UNEXPECTED, T2COLE(strPropName), pErrorLog); } } // Read the element count of the safe array DWORD dwElements = 0; if (!subkey.GetDWord(m_szElementCount, dwElements)) { // Use local resources MCLibRes res; // Format a description string _bstr_t strDesc; strDesc.Format(IDS_FMT_VALUE_NOT_EXIST, m_szElementCount, strPropName); // Log the error USES_CONVERSION; return LogError("ReadSafeArray", strDesc, E_UNEXPECTED, T2COLE(strPropName), pErrorLog); } // Read the lower bound of the safe array, defaults to zero LONG lLBound = 0; subkey.GetDWord(m_szLowerBound, (DWORD&)lLBound); // Read each array element into a temporary array _bstr_t strText; CComVariant var; CArray arrayTemp; for (DWORD i = 0; i < dwElements; i++) { // Prepare the variant V_VT(&var) = vt; // Format the value name strText.Format(m_szElementFmt, i); // Read the variant HRESULT hr = ReadVariant(subkey, strText, var, pErrorLog); if (FAILED(hr)) return hr; // Add the variant to the temporary array arrayTemp.Add(var); } // Create a safe array and copy the temporary array elements SAFEARRAY* psa = SafeArrayCreateVector(vt, lLBound, dwElements); if (NULL == psa) { // Use local resources MCLibRes res; // Format a description string _bstr_t strDesc(LPCSTR(IDS_FAIL_SAFEARRAY_CREATE)); // Log the error USES_CONVERSION; return LogError("ReadSafeArray", strDesc, E_OUTOFMEMORY, T2COLE(strPropName), pErrorLog); } // Copy the temporary array elements into the safe array for (long iElement = 0; iElement < arrayTemp.GetSize(); iElement++) { CComVariant& v = arrayTemp[iElement]; void* pvData; switch (vt) { case VT_VARIANT: pvData = &v; break; case VT_UNKNOWN: case VT_DISPATCH: case VT_BSTR: pvData = V_BSTR(&v); break; default: pvData = &V_NONE(&v); } HRESULT hr = SafeArrayPutElement(psa, &iElement, pvData); if (FAILED(hr)) { // Use local resources MCLibRes res; // Format a description string _bstr_t strDesc(LPCSTR(IDS_FAIL_SAFEARRAY_PUT)); // Log the error USES_CONVERSION; return LogError("ReadSafeArray", strDesc, hr, T2COLE(strPropName), pErrorLog); } } // Put the safe array into the specified variant V_VT(pVar) = vt | VT_ARRAY; V_ARRAY(pVar) = psa; // Indicate success return S_OK; } HRESULT CTCPropBagOnRegKey::_WriteSafeArray(CRegKey& key, const _bstr_t& strPropName, VARIANT* pVar) { ASSERT(V_ISARRAY(pVar)); ASSERT(lstrlen(strPropName)); // Get the SAFEARRAY pointer from the variant SAFEARRAY* psa = V_ARRAY(pVar); if (IsBadReadPtr(psa)) return E_POINTER; // Only support 1-dimensional arrays (currently) if (1 != SafeArrayGetDim(psa)) return E_INVALIDARG; // Get the element size of the safe array UINT cbElement = SafeArrayGetElemsize(psa); // Get the safe array type from the variant VARTYPE vt = V_VT(pVar) & ~VT_ARRAY; // Check for supported types and validate the element size switch (vt) { case VT_BOOL: if (sizeof(V_BOOL(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_I1: if (sizeof(V_I1(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_I2: if (sizeof(V_I2(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_I4: if (sizeof(V_I4(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_UI1: if (sizeof(V_UI1(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_UI2: if (sizeof(V_UI2(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_UI4: if (sizeof(V_UI4(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_ERROR: if (sizeof(V_ERROR(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_R4: if (sizeof(V_R4(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_R8: if (sizeof(V_R8(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_DECIMAL: if (sizeof(V_DECIMAL(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_CY: if (sizeof(V_CY(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_DATE: if (sizeof(V_DATE(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_BSTR: if (sizeof(V_BSTR(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_UNKNOWN: if (sizeof(V_UNKNOWN(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_DISPATCH: if (sizeof(V_DISPATCH(pVar)) != cbElement) return E_UNEXPECTED; break; case VT_VARIANT: if (sizeof(V_VARIANTREF(pVar)) != cbElement) return E_UNEXPECTED; break; default: return E_UNEXPECTED; } // Get the upper and lower bounds of the safe array HRESULT hr; LONG lUBound = 0, lLBound = 0; if (FAILED(hr = SafeArrayGetUBound(psa, 1, &lUBound))) return hr; if (FAILED(hr = SafeArrayGetLBound(psa, 1, &lLBound))) return hr; UINT nElements = lUBound - lLBound + 1; // Create a subkey with the specified name key.DeleteValue(strPropName); key.RecurseDeleteKey(strPropName); CRegKey subkey; if (!subkey.Open(key, strPropName)) return HRESULT_FROM_WIN32(GetLastError()); // Get access to the safe array data BYTE* pElement = NULL; if (FAILED(hr = SafeArrayAccessData(psa, (void**)&pElement))) return hr; // Write the variant type value subkey.WriteDWord(m_szVariantType, DWORD(V_VT(pVar))); // Write the element count value subkey.WriteDWord(m_szElementCount, DWORD(nElements)); // Write the lower bound value, if not 0 if (lLBound) subkey.WriteDWord(m_szLowerBound, DWORD(lLBound)); // Special handling for arrays of variants _bstr_t strText; if (VT_VARIANT == vt) { // Write each variant array element to the registry for (UINT i = 0; i < nElements; i++, pElement += cbElement) { // Format the value name strText.Format(m_szElementFmt, i); // Write the variant array element to the registry subkey if (FAILED(hr = WriteVariant(subkey, strText, (VARIANT*)pElement))) { TRACE1("CTCPropBagOnRegKey::_WriteSafeArray(\"%s\", pVar): ", strPropName); TRACE2("WriteVariant(subkey, \"%s\", &var) returned 0x%08X\n", strText, hr); } } } else { // Write each array element to the registry VARIANT var; V_VT(&var) = vt; for (UINT i = 0; i < nElements; i++, pElement += cbElement) { // Copy the array element to the data portion of the VARIANT memcpy(&V_NONE(&var), pElement, cbElement); // Format the value name strText.Format(m_szElementFmt, i); // Write the variant to the registry subkey if (FAILED(hr = WriteVariant(subkey, strText, &var))) { TRACE1("CTCPropBagOnRegKey::_WriteSafeArray(\"%s\", pVar): ", strPropName); TRACE2("WriteVariant(subkey, \"%s\", &var) returned 0x%08X\n", strText, hr); } } } // Release access to the safe array data VERIFY(SUCCEEDED(SafeArrayUnaccessData(psa))); // Indicate success return S_OK; } HRESULT CTCPropBagOnRegKey::_ReadVariant(CRegKey& key, const _bstr_t& strValueName, VARIANT* pVar, IErrorLog* pErrorLog) { // Create an empty CComVariant object CComVariant v; // Read the specified property from the registry DWORD nType = 0; int cbData = 0; if (key.QueryValue(strValueName, nType, cbData)) // Name is a value? { // Read property in registry value as a VARIANT HRESULT hr = _ReadVariantFromValue(key, strValueName, nType, cbData, v, pErrorLog); if (FAILED(hr)) return hr; } else if (key.Exists(strValueName)) // Name is a subkey? { // Read property on subkey as specified by subkey's VariantType value HRESULT hr = E_FAIL; if (IsSubkeySafeArray(key, strValueName)) hr = ReadSafeArray(key, strValueName, pVar, pErrorLog); else if (IsSubkeyObject(key, strValueName)) { // Read property on subkey as an object CComObjectStack bag; bag.Init(key, _bstr_t(), this); V_VT(&v) = VT_UNKNOWN; hr = bag._CreateObject(strValueName, &V_UNKNOWN(&v), pErrorLog); } if (FAILED(hr)) return hr; } else { // Specified name does not exist TRACE1("CTCPropBagOnRegKey::_ReadVariant: Property \"%s\" does not exist\n", strValueName); return E_INVALIDARG; } // Coerce the value to the specified variant type switch (V_VT(pVar)) { // Leave value as-is if no VARIANT type was specified case VT_EMPTY: VariantCopy(pVar, v); break; // Coerce supported VARIANT types case VT_BOOL: case VT_I1: case VT_I2: case VT_I4: case VT_UI1: case VT_UI2: case VT_UI4: case VT_ERROR: case VT_R4: case VT_R8: case VT_DECIMAL: case VT_CY: case VT_DATE: case VT_BSTR: case VT_UNKNOWN: case VT_DISPATCH: case VT_ARRAY | VT_BOOL: case VT_ARRAY | VT_I1: case VT_ARRAY | VT_I2: case VT_ARRAY | VT_I4: case VT_ARRAY | VT_UI1: case VT_ARRAY | VT_UI2: case VT_ARRAY | VT_UI4: case VT_ARRAY | VT_ERROR: case VT_ARRAY | VT_R4: case VT_ARRAY | VT_R8: case VT_ARRAY | VT_DECIMAL: case VT_ARRAY | VT_CY: case VT_ARRAY | VT_DATE: case VT_ARRAY | VT_BSTR: case VT_ARRAY | VT_UNKNOWN: case VT_ARRAY | VT_DISPATCH: case VT_ARRAY | VT_VARIANT: { // Coerce the value to the specified VARIANT type VariantChangeTypeEx(pVar, v, GetThreadLocale(), m_wChangeTypeFlags, V_VT(pVar)); break; } // All other VARIANT types are unsupported default: { // Use local resources MCLibRes res; // Format a description string _bstr_t strDesc; strDesc.Format(IDS_FMT_UNSUPPORTED_VT, UINT(V_VT(pVar)), UINT(V_VT(pVar))); // Log the error USES_CONVERSION; return LogError("ReadVariant", strDesc, E_FAIL, T2COLE(strValueName), pErrorLog); } } // Indicate success return S_OK; } HRESULT CTCPropBagOnRegKey::_WriteVariant(CRegKey& key, const _bstr_t& strValueName, VARIANT* pVar) { // Check for an array if (V_ISARRAY(pVar)) return WriteSafeArray(key, strValueName, pVar); // Write the value to the registry based on the VARIANT type switch (V_VT(pVar)) { // Empty variant is written as nothing case VT_EMPTY: key.DeleteValue(strValueName); key.RecurseDeleteKey(strValueName); break; // Integer types are written as a REG_DWORD value case VT_I1: case VT_I2: case VT_I4: case VT_UI1: case VT_UI2: case VT_ERROR: { // Coerce the value to a VT_UI4 VariantChangeTypeEx(pVar, pVar, GetThreadLocale(), m_wChangeTypeFlags, VT_UI4); // Fall thru to next case } case VT_UI4: { // Write the REG_DWORD value to the registry key.RecurseDeleteKey(strValueName); key.WriteDWord(strValueName, V_UI4(pVar)); break; } // BOOL's, float types and strings are written as a REG_SZ value case VT_R4: case VT_R8: case VT_CY: case VT_DATE: case VT_DECIMAL: case VT_BOOL: { // Coerce the value to a VT_BSTR VariantChangeTypeEx(pVar, pVar, GetThreadLocale(), m_wChangeTypeFlags, VT_BSTR); // Fall thru to next case } case VT_BSTR: { // Write the REG_SZ value to the registry key.RecurseDeleteKey(strValueName); key.WriteString(strValueName, V_BSTR(pVar)); break; } // Objects written as REG_BINARY, if they don't support IPersistPropertyBag case VT_UNKNOWN: case VT_DISPATCH: { // Attempt first to save the object property using IPersistPropertyBag key.DeleteValue(strValueName); key.RecurseDeleteKey(strValueName); CComObjectStack bag; bag.Init(key, _bstr_t(), this); HRESULT hr = bag.SaveObject(strValueName, V_UNKNOWN(pVar), FALSE, TRUE); if (FAILED(hr)) { TRACE1("CTCPropBagOnRegKey::_WriteVariant: Saving object property \"%s\" as a binary value\n", strValueName); // Create a CArchive on a CMemFile CMemFile file; CArchive ar(&file, CArchive::store, 0); // Archive the variant to the CArchive and close it CComVariant v(pVar); ar << v; ar.Close(); // Write the REG_BINARY value to the registry int cbData = file.GetLength(); BYTE* pData = file.Detach(); key.RecurseDeleteKey(strValueName); key.WriteBinary(strValueName, pData, cbData); file.Attach(pData, cbData); } break; } default: TRACE1("CTCPropBagOnRegKey::_WriteVariant(\"%ls\"): ", strValueName); TRACE2("Unsupported variant type 0x%02X (%d)\n", UINT(V_VT(pVar)), UINT(V_VT(pVar))); return E_FAIL; } // Indicate success return S_OK; } VARTYPE CTCPropBagOnRegKey::_VarTypeFromUnknown(IUnknown* punk) { try { VARTYPE vt = VT_UNKNOWN; IDispatch* pdisp = NULL; if (SUCCEEDED(punk->QueryInterface(IID_IDispatch, (void**)&pdisp))) { if (pdisp == punk) vt = VT_DISPATCH; pdisp->Release(); } return vt; } catch (...) { return VT_EMPTY; } } HKEY CTCPropBagOnRegKey::RootKeyFromString(BSTR bstrRegKey, DWORD* cchEaten) { // Define a static lookup table struct XRootKeys { LPCOLESTR pszName; DWORD cchName; HKEY hkey; }; const static XRootKeys s_table[] = { {L"HKCR\\" , 5, HKEY_CLASSES_ROOT }, {L"HKCU\\" , 5, HKEY_CURRENT_USER }, {L"HKLM\\" , 5, HKEY_LOCAL_MACHINE }, {L"HKCC\\" , 5, HKEY_CURRENT_CONFIG }, {L"HKPD\\" , 5, HKEY_PERFORMANCE_DATA}, {L"HKDD\\" , 5, HKEY_DYN_DATA }, {L"HKEY_CLASSES_ROOT\\" , 18, HKEY_CLASSES_ROOT }, {L"HKEY_CURRENT_USER\\" , 18, HKEY_CURRENT_USER }, {L"HKEY_LOCAL_MACHINE\\" , 19, HKEY_LOCAL_MACHINE }, {L"HKEY_CURRENT_CONFIG\\" , 20, HKEY_CURRENT_CONFIG }, {L"HKEY_PERFORMANCE_DATA\\", 22, HKEY_PERFORMANCE_DATA}, {L"HKEY_DYN_DATA\\" , 14, HKEY_DYN_DATA }, }; const static long s_cEntries = sizeofArray(s_table); // Search for an allowed root key name if (BSTRLen(bstrRegKey)) { for (long i = 0; i < s_cEntries; ++i) { if (!wcsnicmp(s_table[i].pszName, bstrRegKey, s_table[i].cchName)) { *cchName = s_table[i].cchName; return s_table[i].hkey; } } } // Could not find the specified string *cchName = 0; return NULL; } LONG CTCPropBagOnRegKey::KeyExists(HKEY hkey, const _bstr_t& strKeyName) { HKEY hkeyTemp; LONG lr = IsWinNT() ? RegOpenKeyExW(hkey, strKeyName, 0, KEY_READ, &hkeyTemp) : RegOpenKeyExA(hkey, strKeyName, 0, KEY_READ, &hkeyTemp); if (ERROR_SUCCESS == lr) { RegCloseKey(hkeyTemp); return true; } return false; } LONG CTCPropBagOnRegKey::QueryString(HKEY hkey, const _bstr_t& strValueName, _bstr_t& strOut) { bool bIsWinNT = IsWinNT(); DWORD cbData; LONG lr = bIsWinNT ? RegQueryValueExW(hkey, strValueName, NULL, NULL, NULL, &cbData) : RegQueryValueExA(hkey, strValueName, NULL, NULL, NULL, &cbData); // Allocate a buffer LP } ///////////////////////////////////////////////////////////////////////////// // ITCPropBagOnRegKey Interface Methods STDMETHODIMP CTCPropBagOnRegKey::CreateKey(BSTR bstrRegKey, VARIANT_BOOL bReadOnly) { // Interpret the first part of the specified registry key string DWORD cchEaten; HKEY hkeyRoot = RootKeyFromString(bstrRegKey, &cchEaten); if (!hkeyRoot) { ZError("CTCPropBagOnRegKey::CreateKey(): Invalid registry key name specified."); return E_INVALIDARG; } // Increment the string past the root key string bstrRegKey += cchEaten; // Determine the specified access permission REGSAM regsam = bReadOnly ? KEY_READ : KEY_ALL_ACCESS; // Attempt to create/open the specified subkey of the specified key long lr; HKEY hkeyOpen = NULL; DWORD dw; if (IsWinNT()) // Take advantage of Unicode BSTR's under WinNT { lr = RegCreateKeyExW(hkeyRoot, bstrRegKey, 0, REG_NONE, REG_OPTION_NON_VOLATILE, regsam, NULL, &hkeyOpen, &dw); } else { USES_CONVERSION; lr = RegCreateKeyEx(hkeyRoot, OLE2CT(bstrRegKey), 0, REG_NONE, REG_OPTION_NON_VOLATILE, regsam, NULL, &hkeyOpen, &dw); } if (ERROR_SUCCESS != lr) return HRESULT_FROM_WIN32(lr); // Save the new open registry key XLock lock(this); ClosePrevious(); m_key.Attach(hkeyOpen); m_bOwnKey = true; // Indicate success return S_OK; } STDMETHODIMP CTCPropBagOnRegKey::OpenKey(BSTR bstrRegKey, VARIANT_BOOL bReadOnly) { // Interpret the first part of the specified registry key string DWORD cchEaten; HKEY hkeyRoot = RootKeyFromString(bstrRegKey, &cchEaten); if (!hkeyRoot) { ZError("CTCPropBagOnRegKey::CreateKey(): Invalid registry key name specified."); return E_INVALIDARG; } // Increment the string past the root key string bstrRegKey += cchEaten; // Determine the specified access permission REGSAM regsam = bReadOnly ? KEY_READ : KEY_ALL_ACCESS; // Attempt to create/open the specified subkey of the specified key long lr; HKEY hkeyOpen = NULL; DWORD dw; if (IsWinNT()) // Take advantage of Unicode BSTR's under WinNT { lr = RegOpenKeyExW(hkeyRoot, bstrRegKey, 0, regsam, &hkeyOpen); } else { USES_CONVERSION; lr = RegOpenKeyEx(hkeyRoot, OLE2CT(bstrRegKey), 0, regsam, &hkeyOpen); } if (ERROR_SUCCESS != lr) return HRESULT_FROM_WIN32(lr); // Save the new open registry key XLock lock(this); ClosePrevious(); m_key.Attach(hkeyOpen); m_bOwnKey = true; // Indicate success return S_OK; } STDMETHODIMP CTCPropBagOnRegKey::CreateObject(IUnknown** ppunkObj) { try { // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object return m_pBag->CreateObject(_bstr_t(), ppunkObj); } TC_CATCH_ALL("CTCPropBagOnRegKey::CreateObject()", RPC_E_SERVERFAULT) } STDMETHODIMP CTCPropBagOnRegKey::CreateLocalObject(IUnknown** ppunkObj) { try { // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object return m_pBag->CreateLocalObject(_bstr_t(), ppunkObj); } TC_CATCH_ALL("CTCPropBagOnRegKey::CreateLocalObject()", RPC_E_SERVERFAULT) } STDMETHODIMP CTCPropBagOnRegKey::CreateRemoteObject(BSTR bstrServer, IUnknown** ppunkObj) { try { // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object return m_pBag->CreateRemoteObject(_bstr_t(bstrServer), _bstr_t(), ppunkObj); } TC_CATCH_ALL("CTCPropBagOnRegKey::CreateRemoteObject()", RPC_E_SERVERFAULT) } STDMETHODIMP CTCPropBagOnRegKey::LoadObject(IUnknown* punkObj) { try { // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object return m_pBag->LoadObject(_bstr_t(), punkObj); } TC_CATCH_ALL("CTCPropBagOnRegKey::LoadObject()", RPC_E_SERVERFAULT) } STDMETHODIMP CTCPropBagOnRegKey::SaveObject(IUnknown* punkObj, VARIANT_BOOL bClearDirty, VARIANT_BOOL bSaveAllProperties) { try { // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object return m_pBag->SaveObject(_bstr_t(), punkObj); } TC_CATCH_ALL("CTCPropBagOnRegKey::SaveObject()", RPC_E_SERVERFAULT) } STDMETHODIMP CTCPropBagOnRegKey::put_Server(BSTR bstrServer) { try { // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object return m_pBag->SetServer(_bstr_t(), _bstr_t(bstrServer)); } TC_CATCH_ALL("CTCPropBagOnRegKey::put_Server()", RPC_E_SERVERFAULT) } STDMETHODIMP CTCPropBagOnRegKey::get_Server(BSTR* pbstrServer) { try { // Initialize the [out] parameter CLEAROUT(pbstrServer, (BSTR)NULL); // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object *pbstrServer = m_pBag->GetServer(_bstr_t()).AllocSysString(); // Indicate success return S_OK; } TC_CATCH_ALL("CTCPropBagOnRegKey::get_Server()", RPC_E_SERVERFAULT) } STDMETHODIMP CTCPropBagOnRegKey::get_ObjectCLSID(BSTR* pbstrCLSID) { try { // Initialize the [out] parameter CLEAROUT(pbstrCLSID, (BSTR)NULL); // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object CLSID clsid; RETURN_FAILED(m_pBag->GetObjectCLSID(_bstr_t(), clsid)); // Convert the CLSID to a string *pbstrCLSID = BSTRFromGUID(clsid).copy(); // Indicate success return S_OK; } TC_CATCH_ALL("CTCPropBagOnRegKey::get_ObjectCLSID()", RPC_E_SERVERFAULT) } ///////////////////////////////////////////////////////////////////////////// // IPropertyBag Interface Methods STDMETHODIMP CTCPropBagOnRegKey::Read(LPCOLESTR pszPropName, VARIANT* pVar, IErrorLog* pErrorLog) { try { // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object return ((IPropertyBag*)m_pBag)->Read(pszPropName, pVar, pErrorLog); } TC_CATCH_ALL("CTCPropBagOnRegKey::Read()", RPC_E_SERVERFAULT) } STDMETHODIMP CTCPropBagOnRegKey::Write(LPCOLESTR pszPropName, VARIANT* pVar) { try { // Lock the object XLock lock(this); // Ensure that an internal property bag object has been created if (!m_pBag) return E_UNEXPECTED; // Forward the call to the internal property bag object return ((IPropertyBag*)m_pBag)->Write(pszPropName, pVar); } TC_CATCH_ALL("CTCPropBagOnRegKey::Write()", RPC_E_SERVERFAULT) }