// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #ifdef AFXCTL_PROP_SEG #pragma code_seg(AFXCTL_PROP_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // CBlobProperty class CBlobProperty : public IPersistStream { public: CBlobProperty(HGLOBAL pBlob = NULL); HGLOBAL GetBlob(); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); STDMETHOD(QueryInterface)(REFIID, LPVOID*); STDMETHOD(GetClassID)(LPCLSID); STDMETHOD(IsDirty)(); STDMETHOD(Load)(LPSTREAM); STDMETHOD(Save)(LPSTREAM, BOOL); STDMETHOD(GetSizeMax)(ULARGE_INTEGER*); protected: long m_dwRef; HGLOBAL m_hBlob; }; CBlobProperty::CBlobProperty(HGLOBAL hBlob) : m_hBlob(hBlob), m_dwRef(1) { } HGLOBAL CBlobProperty::GetBlob() { return m_hBlob; } STDMETHODIMP_(ULONG) CBlobProperty::AddRef() { return InterlockedIncrement(&m_dwRef); } STDMETHODIMP_(ULONG) CBlobProperty::Release() { if (InterlockedDecrement(&m_dwRef) > 0) return m_dwRef; delete this; return 0; } STDMETHODIMP CBlobProperty::QueryInterface(REFIID riid, LPVOID* ppvObj) { if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IPersist) || IsEqualIID(riid, IID_IPersistStream)) { AddRef(); *ppvObj = this; return S_OK; } *ppvObj = NULL; return E_NOINTERFACE; } AFX_STATIC_DATA const CLSID _afx_CLSID_BlobProperty = { 0xf6f07540, 0x42ec, 0x11ce, { 0x81, 0x35, 0x0, 0xaa, 0x0, 0x4b, 0xb8, 0x51 } }; STDMETHODIMP CBlobProperty::GetClassID(LPCLSID pClsid) { *pClsid = _afx_CLSID_BlobProperty; return S_OK; } STDMETHODIMP CBlobProperty::IsDirty() { return S_OK; } STDMETHODIMP CBlobProperty::Load(LPSTREAM pStream) { ULONG cb; ULONG cbRead; HRESULT hr = pStream->Read(&cb, sizeof(ULONG), &cbRead); if (FAILED(hr)) return hr; if (sizeof(ULONG) != cbRead) return E_FAIL; HGLOBAL hBlobNew = GlobalAlloc(GMEM_MOVEABLE, sizeof(ULONG)+cb); if (hBlobNew == NULL) return E_OUTOFMEMORY; void* pBlobNew = GlobalLock(hBlobNew); *(ULONG*)pBlobNew = cb; hr = pStream->Read(((ULONG*)pBlobNew)+1, cb, &cbRead); GlobalUnlock(hBlobNew); if (FAILED(hr)) { GlobalFree(hBlobNew); return hr; } if (cb != cbRead) { GlobalFree(hBlobNew); return E_FAIL; } GlobalFree(m_hBlob); m_hBlob = hBlobNew; return S_OK; } STDMETHODIMP CBlobProperty::Save(LPSTREAM pStream, BOOL) { void* pBlob = GlobalLock(m_hBlob); if (pBlob == NULL) return E_OUTOFMEMORY; ULONG cb = sizeof(ULONG) + *(ULONG*)pBlob; ULONG cbWritten; HRESULT hr = pStream->Write(pBlob, cb, &cbWritten); GlobalUnlock(m_hBlob); if (FAILED(hr)) return hr; if (cb != cbWritten) return E_FAIL; return S_OK; } STDMETHODIMP CBlobProperty::GetSizeMax(ULARGE_INTEGER* pcbSize) { void* pBlob = GlobalLock(m_hBlob); if (pBlob == NULL) return E_OUTOFMEMORY; pcbSize->HighPart = 0; pcbSize->LowPart = sizeof(ULONG) + *(ULONG*)pBlob; GlobalUnlock(m_hBlob); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // CPropbagPropExchange class CPropbagPropExchange : public CPropExchange { public: CPropbagPropExchange(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog, BOOL bLoading, BOOL bSaveAllProperties=FALSE); ~CPropbagPropExchange(); // Operations virtual BOOL ExchangeProp(LPCTSTR pszPropName, VARTYPE vtProp, void* pvProp, const void* pvDefault = NULL); virtual BOOL ExchangeBlobProp(LPCTSTR pszPropName, HGLOBAL* phBlob, HGLOBAL hBlobDefault = NULL); virtual BOOL ExchangeFontProp(LPCTSTR pszPropName, CFontHolder& font, const FONTDESC* pFontDesc, LPFONTDISP pFontDispAmbient); virtual BOOL ExchangePersistentProp(LPCTSTR pszPropName, LPUNKNOWN* ppUnk, REFIID iid, LPUNKNOWN pUnkDefault); // Implementation LPPROPERTYBAG m_pPropBag; LPERRORLOG m_pErrorLog; BOOL m_bSaveAllProperties; }; CPropbagPropExchange::CPropbagPropExchange(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog, BOOL bLoading, BOOL bSaveAllProperties) : m_pPropBag(pPropBag), m_pErrorLog(pErrorLog), m_bSaveAllProperties(bSaveAllProperties) { m_bLoading = bLoading; if (pPropBag != NULL) pPropBag->AddRef(); if (pErrorLog != NULL) pErrorLog->AddRef(); } CPropbagPropExchange::~CPropbagPropExchange() { RELEASE(m_pPropBag); RELEASE(m_pErrorLog); } BOOL CPropbagPropExchange::ExchangeProp(LPCTSTR pszPropName, VARTYPE vtProp, void* pvProp, const void* pvDefault) { USES_CONVERSION; ASSERT_POINTER(m_pPropBag, IPropertyBag); ASSERT(AfxIsValidString(pszPropName)); ASSERT(AfxIsValidAddress(pvProp, 1, FALSE)); ASSERT((pvDefault == NULL) || AfxIsValidAddress(pvDefault, 1, FALSE)); if (m_pPropBag == NULL) return FALSE; BOOL bSuccess = TRUE; VARIANT var; AfxVariantInit(&var); V_VT(&var) = vtProp; if (vtProp == VT_LPSTR) V_VT(&var) = VT_BSTR; if (m_bLoading) { if (FAILED(m_pPropBag->Read(T2COLE(pszPropName), &var, m_pErrorLog))) return _AfxCopyPropValue(vtProp, pvProp, pvDefault); switch (vtProp) { case VT_UI1: *(BYTE*)pvProp = V_UI1(&var); break; case VT_I2: *(short*)pvProp = V_I2(&var); break; case VT_I4: *(long*)pvProp = V_I4(&var); break; case VT_BOOL: *(BOOL*)pvProp = (BOOL)V_BOOL(&var); break; case VT_LPSTR: case VT_BSTR: *(CString*)pvProp = OLE2CT(V_BSTR(&var)); break; case VT_CY: *(CY*)pvProp = V_CY(&var); break; case VT_R4: memcpy(pvProp, &V_R4(&var), sizeof(float)); break; case VT_R8: memcpy(pvProp, &V_R8(&var), sizeof(double)); break; default: bSuccess = FALSE; } } else { if (m_bSaveAllProperties || !_AfxIsSamePropValue(vtProp, pvProp, pvDefault)) { switch (vtProp) { case VT_UI1: V_UI1(&var) = *(BYTE*)pvProp; break; case VT_I2: V_I2(&var) = *(short*)pvProp; break; case VT_I4: V_I4(&var) = *(long*)pvProp; break; case VT_BOOL: V_BOOL(&var) = (VARIANT_BOOL)*(BOOL*)pvProp; break; case VT_LPSTR: case VT_BSTR: V_BSTR(&var) = SysAllocString(T2COLE(*(CString*)pvProp)); break; case VT_CY: V_CY(&var) = *(CY*)pvProp; break; case VT_R4: memcpy(&V_R4(&var), pvProp, sizeof(float)); break; case VT_R8: memcpy(&V_R8(&var), pvProp, sizeof(double)); break; default: return FALSE; } bSuccess = SUCCEEDED(m_pPropBag->Write(T2COLE(pszPropName), &var)); } } VariantClear(&var); return bSuccess; } BOOL CPropbagPropExchange::ExchangeBlobProp(LPCTSTR pszPropName, HGLOBAL* phBlob, HGLOBAL hBlobDefault) { USES_CONVERSION; ASSERT_POINTER(m_pPropBag, IPropertyBag); ASSERT(AfxIsValidString(pszPropName)); ASSERT_POINTER(phBlob, HGLOBAL); BOOL bSuccess = FALSE; VARIANT var; AfxVariantInit(&var); V_VT(&var) = VT_UNKNOWN; if (m_bLoading) { if (*phBlob != NULL) { GlobalFree(*phBlob); *phBlob = NULL; } CBlobProperty* pBlobProp = new CBlobProperty; V_UNKNOWN(&var) = pBlobProp; if (SUCCEEDED(m_pPropBag->Read(T2COLE(pszPropName), &var, m_pErrorLog))) { *phBlob = pBlobProp->GetBlob(); bSuccess = TRUE; } else { if (hBlobDefault != NULL) bSuccess = _AfxCopyBlob(phBlob, hBlobDefault); } pBlobProp->Release(); } else { CBlobProperty* pBlobProp = new CBlobProperty(*phBlob); V_UNKNOWN(&var) = pBlobProp; bSuccess = SUCCEEDED(m_pPropBag->Write(T2COLE(pszPropName), &var)); pBlobProp->Release(); } return bSuccess; } BOOL CPropbagPropExchange::ExchangeFontProp(LPCTSTR pszPropName, CFontHolder& font, const FONTDESC* pFontDesc, LPFONTDISP pFontDispAmbient) { USES_CONVERSION; ASSERT_POINTER(m_pPropBag, IPropertyBag); ASSERT(AfxIsValidString(pszPropName)); ASSERT_POINTER(&font, CFontHolder); ASSERT_NULL_OR_POINTER(pFontDesc, FONTDESC); ASSERT_NULL_OR_POINTER(pFontDispAmbient, IFontDisp); BOOL bSuccess = FALSE; VARIANT var; AfxVariantInit(&var); V_VT(&var) = VT_UNKNOWN; if (m_bLoading) { LPFONT pFont = NULL; bSuccess = SUCCEEDED(m_pPropBag->Read(T2COLE(pszPropName), &var, m_pErrorLog)) && SUCCEEDED(V_UNKNOWN(&var)->QueryInterface(IID_IFont, (LPVOID*)&pFont)); if (bSuccess) { ASSERT_POINTER(pFont, IFont); font.SetFont(pFont); } else { // Initialize font to its default state font.InitializeFont(pFontDesc, pFontDispAmbient); } VariantClear(&var); } else { if ((font.m_pFont == NULL) || (_AfxIsSameFont(font, pFontDesc, pFontDispAmbient) && !m_bSaveAllProperties)) { bSuccess = TRUE; } else { V_UNKNOWN(&var) = font.m_pFont; bSuccess = SUCCEEDED(m_pPropBag->Write(T2COLE(pszPropName), &var)); } } return bSuccess; } BOOL CPropbagPropExchange::ExchangePersistentProp(LPCTSTR pszPropName, LPUNKNOWN* ppUnk, REFIID iid, LPUNKNOWN pUnkDefault) { USES_CONVERSION; ASSERT_POINTER(m_pPropBag, IPropertyBag); ASSERT(AfxIsValidString(pszPropName)); ASSERT_POINTER(ppUnk, LPUNKNOWN); ASSERT_NULL_OR_POINTER(pUnkDefault, IUnknown); BOOL bSuccess = FALSE; VARIANT var; AfxVariantInit(&var); V_VT(&var) = VT_UNKNOWN; if (m_bLoading) { RELEASE(*ppUnk); *ppUnk = NULL; bSuccess = SUCCEEDED(m_pPropBag->Read(T2COLE(pszPropName), &var, m_pErrorLog)) && SUCCEEDED(V_UNKNOWN(&var)->QueryInterface(iid, (LPVOID*)ppUnk)); if (!bSuccess) { // Use default value. if (pUnkDefault != NULL) { bSuccess = SUCCEEDED(pUnkDefault->QueryInterface(iid, (LPVOID*)ppUnk)); } else { bSuccess = TRUE; } } VariantClear(&var); } else { if ((*ppUnk == NULL) || (_AfxIsSameUnknownObject(iid, *ppUnk, pUnkDefault) && !m_bSaveAllProperties)) { bSuccess = TRUE; } else { V_UNKNOWN(&var) = *ppUnk; bSuccess = SUCCEEDED(m_pPropBag->Write(T2COLE(pszPropName), &var)); } } return bSuccess; } ///////////////////////////////////////////////////////////////////////////// // COleControl::XPersistPropertyBag STDMETHODIMP_(ULONG) COleControl::XPersistPropertyBag::AddRef() { METHOD_PROLOGUE_EX_(COleControl, PersistPropertyBag) return (ULONG)pThis->ExternalAddRef(); } STDMETHODIMP_(ULONG) COleControl::XPersistPropertyBag::Release() { METHOD_PROLOGUE_EX_(COleControl, PersistPropertyBag) return (ULONG)pThis->ExternalRelease(); } STDMETHODIMP COleControl::XPersistPropertyBag::QueryInterface( REFIID iid, LPVOID* ppvObj) { METHOD_PROLOGUE_EX_(COleControl, PersistPropertyBag) return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj); } STDMETHODIMP COleControl::XPersistPropertyBag::GetClassID(LPCLSID lpClassID) { METHOD_PROLOGUE_EX_(COleControl, PersistPropertyBag) return pThis->GetClassID(lpClassID); } STDMETHODIMP COleControl::XPersistPropertyBag::InitNew() { METHOD_PROLOGUE_EX(COleControl, PersistPropertyBag) // Delegate to OnResetState. pThis->OnResetState(); // Unless IOleObject::SetClientSite is called after this, we can // count on ambient properties being available while loading. pThis->m_bCountOnAmbients = TRUE; // Properties have been initialized pThis->m_bInitialized = TRUE; // Uncache cached ambient properties _afxAmbientCache->Cache(NULL); return S_OK; } STDMETHODIMP COleControl::XPersistPropertyBag::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog) { METHOD_PROLOGUE_EX(COleControl, PersistPropertyBag) HRESULT hr; TRY { CPropbagPropExchange px(pPropBag, pErrorLog, TRUE); pThis->DoPropExchange(&px); hr = S_OK; } CATCH_ALL(e) { hr = E_FAIL; DELETE_EXCEPTION(e); } END_CATCH_ALL // Properties have probably changed pThis->BoundPropertyChanged(DISPID_UNKNOWN); pThis->InvalidateControl(); // Clear the modified flag. pThis->m_bModified = FALSE; // Properties have been initialized pThis->m_bInitialized = TRUE; // Uncache cached ambient properties _afxAmbientCache->Cache(NULL); return hr; } STDMETHODIMP COleControl::XPersistPropertyBag::Save(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties) { METHOD_PROLOGUE_EX(COleControl, PersistPropertyBag) HRESULT hr; TRY { CPropbagPropExchange px(pPropBag, NULL, FALSE, fSaveAllProperties); pThis->DoPropExchange(&px); hr = S_OK; } CATCH_ALL(e) { hr = E_FAIL; DELETE_EXCEPTION(e); } END_CATCH_ALL // Bookkeeping: Clear the dirty flag, if requested. if (fClearDirty) pThis->m_bModified = FALSE; return hr; }