// PPPBag.cpp : Implementation of CPropertyPagePropertyBag #include "stdafx.h" #include "WizChain.h" #include "PPPBag.h" #include "PropItem.h" ///////////////////////////////////////////////////////////////////////////// // CPropertyPagePropertyBag STDMETHODIMP CPropertyPagePropertyBag::GetProperty( BSTR szGUID, VARIANT* pvar, PPPBAG_TYPE* pdwFlags, BOOL* pbIsOwner ) { if ( !szGUID || !pvar || !pdwFlags || !pbIsOwner ) return E_POINTER; CLSID clsid; HRESULT hr = S_OK; RPC_STATUS rpcs = UuidFromString( (LPOLESTR)szGUID, &clsid ); if( RPC_S_OK != rpcs ) { return HRESULT_FROM_WIN32(rpcs); } VariantInit( pvar ); *pdwFlags = PPPBAG_TYPE_UNINITIALIZED; // no need to make sure szGUID is a valid guid // since it won't be in the map std::map::iterator mapiter; mapiter = m_map.find( szGUID ); if( mapiter != m_map.end() ) { CBagEntry * pBE = mapiter->second; if( pBE ) { if( SUCCEEDED(VariantCopy( pvar, pBE->GetVariant() )) ) { *pdwFlags = pBE->GetFlags( ); *pbIsOwner = (pBE->GetOwner( ) == m_dwOwner); return S_OK; } else { return E_FAIL; } } } return E_INVALIDARG; } // a couple of helper functions HRESULT HelperDelete(std::map& map, BSTR szGUID, DWORD dwOwner ); HRESULT HelperAdd (std::map& map, BSTR szGUID, CBagEntry* pBE); STDMETHODIMP CPropertyPagePropertyBag::SetProperty( BSTR szGUID, VARIANT* pvar, PPPBAG_TYPE dwFlags ) { // validate parameters if( !szGUID || !pvar ) return E_POINTER; CLSID clsid; RPC_STATUS rpcs = UuidFromString( (LPOLESTR)szGUID, &clsid ); if( RPC_S_OK != rpcs ) { return HRESULT_FROM_WIN32(rpcs); } switch( dwFlags ) { case PPPBAG_TYPE_READWRITE: case PPPBAG_TYPE_READONLY: case PPPBAG_TYPE_ADDITIVE: case PPPBAG_TYPE_DELETION: { break; } default: { return E_INVALIDARG; } } if( m_bReadOnly != FALSE ) { return E_UNEXPECTED; } switch( dwFlags ) { case PPPBAG_TYPE_DELETION: { return HelperDelete( m_map, szGUID, m_dwOwner ); } case PPPBAG_TYPE_READWRITE: { // anyone can write to one of these HelperDelete( m_map, szGUID, m_dwOwner ); CBagEntry* pBagEntry = new CBagEntry( pvar, dwFlags, m_dwOwner ); if( !pBagEntry ) return E_OUTOFMEMORY; return HelperAdd( m_map, szGUID, pBagEntry ); } case PPPBAG_TYPE_READONLY: { // only allow owner to write to this kind of entry HRESULT hr = HelperDelete( m_map, szGUID, m_dwOwner ); if( hr == S_OK ) { CBagEntry* pBagEntry = new CBagEntry( pvar, dwFlags, m_dwOwner ); if( !pBagEntry ) return E_OUTOFMEMORY; hr = HelperAdd( m_map, szGUID, pBagEntry ); } return hr; } case PPPBAG_TYPE_ADDITIVE: { // TODO: add code so that additive properties work correctly return E_NOTIMPL; } } return S_OK; } HRESULT HelperDelete( std::map & map, BSTR szGUID, DWORD dwOwner ) { std::map::iterator mapiter; mapiter = map.find( szGUID ); if( mapiter != map.end( ) ) { CBagEntry * pBE = mapiter->second; if( pBE ) { if( pBE->GetFlags( ) == PPPBAG_TYPE_READONLY ) { if( pBE->GetOwner( ) != dwOwner ) // component's trying to delete { return E_UNEXPECTED; // an entry that doesn't belong to it } } } SysFreeString( mapiter->first ); delete mapiter->second; map.erase( mapiter ); } return S_OK; } HRESULT HelperAdd( std::map& map, BSTR szGUID, CBagEntry* pBE ) { std::map::iterator mapiter = map.find( szGUID ); if( mapiter != map.end() ) { assert( 0 && "this should have been deleted by now!" ); SysFreeString( mapiter->first ); delete mapiter->second; map.erase( mapiter ); } BSTR bstr = SysAllocString( (LPOLESTR)szGUID ); if( !bstr ) { return E_OUTOFMEMORY; } map[bstr] = pBE; return S_OK; } HRESULT CPropertyPagePropertyBag::Enumerate( long index, BSTR* pbstr, VARIANT* pvar, PPPBAG_TYPE* pdwFlags, BOOL* pbIsOwner, BOOL* pbInRange ) { if( !pbstr || !pvar|| !pdwFlags || !pbIsOwner || !pbInRange ) return E_POINTER; if( index >= 0 ) { long i = 0; std::map::iterator mapiter = m_map.begin( ); while( mapiter != m_map.end() ) { if( i == index ) { *pbstr = SysAllocString( mapiter->first ); CBagEntry* pBE = mapiter->second; assert( pBE != NULL ); if( SUCCEEDED(VariantCopy( pvar, pBE->GetVariant() )) ) { *pdwFlags = pBE->GetFlags(); *pbIsOwner = (pBE->GetOwner() == m_dwOwner); *pbInRange = TRUE; return S_OK; } return E_FAIL; } mapiter++; i++; } } // out of range *pdwFlags = PPPBAG_TYPE_UNINITIALIZED; *pbInRange = FALSE; return S_FALSE; } // helper IDispatch* CreateItem( BSTR bstrGuid, VARIANT* var, PPPBAG_TYPE dwFlags ) { // create the item and initialize it CComObject* pPI = NULL; CComObject::CreateInstance( &pPI ); if( !pPI ) return NULL; pPI->Initialize( bstrGuid, var, dwFlags ); // Initialize the Property Item // return back an IDispatch * IDispatch* pDisp = NULL; pPI->AddRef( ); pPI->QueryInterface( IID_IDispatch, (void**)&pDisp ); // can't fail pPI->Release( ); assert( pDisp != NULL ); return pDisp; } class CEnumVariant : public IEnumVARIANT { private: ULONG m_refs; ULONG m_index; CPropertyPagePropertyBag* m_pPPPBag; // addref'd ! CEnumVariant( CPropertyPagePropertyBag* pPPPBag ) { assert( pPPPBag != NULL ); m_refs = 0; m_index = 0; m_pPPPBag = pPPPBag; m_pPPPBag->AddRef(); } ~CEnumVariant () { m_pPPPBag->Release(); } public: static IEnumVARIANT* CreateInstance( CPropertyPagePropertyBag* pPPPBag ) { assert( pPPPBag != NULL ); CEnumVariant* pCEV = new CEnumVariant( pPPPBag ); if( !pCEV ) { return NULL; } IEnumVARIANT* pIEV = NULL; pCEV->AddRef( ); pCEV->QueryInterface( IID_IEnumVARIANT, (void**)&pIEV ); pCEV->Release( ); return pIEV; } // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void** ppvObject ) { HRESULT hr = S_OK; if ((riid == IID_IUnknown) || (riid == IID_IEnumVARIANT) ) { AddRef(); *ppvObject = (void*)this; } else { hr = E_NOINTERFACE; } return hr; } virtual ULONG STDMETHODCALLTYPE AddRef( ) { InterlockedIncrement( (PLONG)&m_refs ); return m_refs; } virtual ULONG STDMETHODCALLTYPE Release( ) { InterlockedDecrement( (PLONG)&m_refs ); ULONG l = m_refs; if( m_refs == 0 ) { delete this; } return l; } // IEnumVARIANT virtual HRESULT STDMETHODCALLTYPE Next( /*[in]*/ ULONG celt, /*[out, size_is(celt), length_is(*pCeltFetched)]*/ VARIANT* rgVar, /*[out]*/ ULONG* pCeltFetched ) { // clear stuff being passed in (just in case) if( pCeltFetched ) { *pCeltFetched = 0; } for( ULONG i = 0; i < celt; i++ ) { VariantInit( &rgVar[i] ); } // get the next celt elements for( i = 0; i < celt; i++ ) { BSTR bstr = NULL; VARIANT var; VariantInit( &var ); PPPBAG_TYPE dwFlags = PPPBAG_TYPE_UNINITIALIZED; BOOL bIsOwner = FALSE; BOOL bInRange = FALSE; m_pPPPBag->Enumerate( (long)m_index++, &bstr, &var, &dwFlags, &bIsOwner, &bInRange ); if( bInRange == FALSE ) { break; } IDispatch* pDisp = CreateItem( bstr, &var, dwFlags ); VariantClear( &var ); if( pDisp == NULL ) { return E_OUTOFMEMORY; } rgVar[i].vt = VT_DISPATCH; rgVar[i].pdispVal = pDisp; } // fill out how many we're returning if( pCeltFetched ) { *pCeltFetched = i; } return ( (i < celt) ? S_FALSE : S_OK); } virtual HRESULT STDMETHODCALLTYPE Skip( /*[in]*/ ULONG celt ) { long count; m_pPPPBag->get_Count( &count ); if( (celt + m_index) > (ULONG)count ) { return S_FALSE; } m_index += celt; return S_OK; } virtual HRESULT STDMETHODCALLTYPE Reset( ) { m_index = 0; return S_OK; } virtual HRESULT STDMETHODCALLTYPE Clone( /*[out]*/ IEnumVARIANT** ppEnum ) { if( !(*ppEnum = CreateInstance( m_pPPPBag )) ) { return E_OUTOFMEMORY; } return S_OK; } }; // CPropertyCollection STDMETHODIMP CPropertyPagePropertyBag::get__NewEnum( IUnknown** pVal ) { if( !pVal ) return E_POINTER; IEnumVARIANT* pEV = CEnumVariant::CreateInstance( this ); if( !pEV ) return E_OUTOFMEMORY; HRESULT hr = pEV->QueryInterface( IID_IUnknown, (void**)pVal ); pEV->Release(); return hr; } STDMETHODIMP CPropertyPagePropertyBag::get_Item( VARIANT* pVar, IDispatch** pVal ) { // handle both: // objFoo.Item(1), and // objFoo.Item("string"); // validate parameters if( !pVar || !pVal ) return E_POINTER; *pVal = NULL; if( V_VT(pVar) == VT_BSTR ) { BSTR bstrGuid = V_BSTR(pVar); CLSID clsid; HRESULT hr = S_OK; RPC_STATUS rpcs = UuidFromString( (LPOLESTR)bstrGuid, &clsid ); if( RPC_S_OK != rpcs ) { return HRESULT_FROM_WIN32(rpcs); } std::map::iterator mapiter; mapiter = m_map.find( bstrGuid ); if( mapiter != m_map.end() ) { CBagEntry* pBE = mapiter->second; if( pBE ) { if( !(*pVal = CreateItem( bstrGuid, pBE->GetVariant(), pBE->GetFlags() )) ) { return E_OUTOFMEMORY; } return S_OK; } } } else if ( (V_VT(pVar) == VT_I2) || (V_VT(pVar) == VT_I4) ) { long index; if( V_VT(pVar) == VT_I4 ) { index = V_I4(pVar); } else { index = V_I2(pVar); } BSTR bstr = NULL; VARIANT var; VariantInit (&var); PPPBAG_TYPE dwFlags = PPPBAG_TYPE_UNINITIALIZED; BOOL bIsOwner = FALSE; BOOL bInRange = FALSE; HRESULT hr = Enumerate( index, &bstr, &var, &dwFlags, &bIsOwner, &bInRange ); if( SUCCEEDED(hr) && (bInRange == TRUE) ) { if( !(*pVal = CreateItem( bstr, &var, dwFlags )) ) { hr = E_OUTOFMEMORY; } } VariantClear ( &var ); SysFreeString( bstr ); return hr; } else { return E_UNEXPECTED; // not a valid variant type } return S_FALSE; } STDMETHODIMP CPropertyPagePropertyBag::get_Count( long *pVal ) { if( !pVal ) return E_POINTER; // TODO: figure out how to use map.count method long i = 0; std::map::iterator mapiter = m_map.begin(); while( mapiter != m_map.end() ) { mapiter++; i++; } *pVal = i; return S_OK; } STDMETHODIMP CPropertyPagePropertyBag::Add(BSTR bstrGuid, VARIANT *varValue, long iFlags, IPropertyItem **ppItem) { // validate parameters if( !bstrGuid || !varValue || !ppItem ) return E_POINTER; *ppItem = NULL; HRESULT hr = SetProperty( bstrGuid, varValue, (PPPBAG_TYPE)iFlags ); if( hr == S_OK ) { IDispatch* pDisp = CreateItem( bstrGuid, varValue, (PPPBAG_TYPE)iFlags ); if( !pDisp ) { hr = E_OUTOFMEMORY; } else { hr = pDisp->QueryInterface( IID_IPropertyItem, (void**)ppItem ); pDisp->Release(); } } return hr; } STDMETHODIMP CPropertyPagePropertyBag::Remove( BSTR bstrGuid ) { if( !bstrGuid ) return E_POINTER; CLSID clsid; RPC_STATUS rpcs = UuidFromString( (LPOLESTR)bstrGuid, &clsid ); if( RPC_S_OK != rpcs ) { return HRESULT_FROM_WIN32(rpcs); } return HelperDelete( m_map, bstrGuid, m_dwOwner ); }