Click here to Skip to main content
15,897,187 members
Articles / Desktop Programming / ATL

En/Decode MIME-Content with MimeSniffer

Rate me:
Please Sign up or sign in to vote.
4.88/5 (26 votes)
2 Dec 20022 min read 377.6K   7K   74  
RFC-compliant Mime-En/Decoder
// ComCollection.h

#ifndef __COMCOLLECTION_H_
#define __COMCOLLECTION_H_

#include "RegistryStorage.h"
#include "FileSystemStorage.h"

#ifdef __COM_COLL_THREAD_SAFE
#include "MyMutex.h"
#endif

// "ComCollection.idl"

// Attention keep this comments. If you want to have this class (and you must have it!)
// then copy the definition of "ComCollection.idl" to your IDL-file!!!!

// MIDL_INTERFACE("7C920D69-2ACC-484A-B2A3-59EF1651EDAD")
// IComCollection : public IDispatch
// {
// public:
// 	virtual HRESULT STDMETHODCALLTYPE get__NewEnum( /*[out, retval]*/ IUnknown** ppUnknown) = 0;
// 	virtual HRESULT STDMETHODCALLTYPE get_Item(/*[in]*/ long Index, /*[out, retval]*/ LPVARIANT pVariant) = 0;
// 	virtual HRESULT STDMETHODCALLTYPE get_Count(/*[out, retval]*/ long* pVal) = 0;
// 	virtual HRESULT STDMETHODCALLTYPE Add(/*[in]*/ VARIANT Item) = 0;
// 	virtual HRESULT STDMETHODCALLTYPE Remove(/*[in]*/ long Index) = 0;
// 	virtual HRESULT STDMETHODCALLTYPE Clear() = 0;
// 	virtual HRESULT STDMETHODCALLTYPE Modify(/*[in]*/ long Index, /*[in]*/ VARIANT Item) = 0;
// 	virtual HRESULT STDMETHODCALLTYPE ItemID(/*[in]*/ long Index, /*[out]*/ VARIANT* pID, /*[out, retval]*/ LPVARIANT pVariant);
// 	virtual HRESULT STDMETHODCALLTYPE ItemByID(/*[in]*/ long ID, /*[out, retval]*/ LPVARIANT pVariant);
// 	virtual HRESULT STDMETHODCALLTYPE RemoveByID(/*[in]*/ long ID);
// 	virtual HRESULT STDMETHODCALLTYPE ModifyByID(/*[in]*/ long ID, /*[in]*/ VARIANT Item);
// };

// use this class instead _Copy<VARIANT>, because we need "VariantCopyInd"!
class _CopyVariant
{
public:
	static HRESULT copy(VARIANT* p1, VARIANT* p2) {return VariantCopyInd(p1, p2);}
	static void init(VARIANT* p) {p->vt = VT_EMPTY;}
	static void destroy(VARIANT* p) {VariantClear(p);}
};

template <class Base, const IID* piid, class T, class Copy, class CollType>
class ATL_NO_VTABLE IEnumWithIDOnSTLImpl : public Base
{
public:
	HRESULT Init(IUnknown *pUnkForRelease, CollType& collection)
	{
		m_spUnk = pUnkForRelease;
		m_pcollection = &collection;
		m_iter = m_pcollection->begin();
		return S_OK;
	}
	STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched);
	STDMETHOD(Skip)(ULONG celt);
	STDMETHOD(Reset)(void)
	{
		if (m_pcollection == NULL)
			return E_FAIL;
		m_iter = m_pcollection->begin();
		return S_OK;
	}
	STDMETHOD(Clone)(Base** ppEnum);
//Data
	CComPtr<IUnknown> m_spUnk;
	CollType* m_pcollection;
	CollType::iterator m_iter;
};

template <class Base, const IID* piid, class T, class Copy, class CollType>
STDMETHODIMP IEnumWithIDOnSTLImpl<Base, piid, T, Copy, CollType>::Next(ULONG celt, T* rgelt,
	ULONG* pceltFetched)
{
	if (rgelt == NULL || (celt != 1 && pceltFetched == NULL))
		return E_POINTER;
	if (m_pcollection == NULL)
		return E_FAIL;

	ULONG nActual = 0;
	HRESULT hr = S_OK;
	T* pelt = rgelt;

	while (SUCCEEDED(hr) && m_iter != m_pcollection->end() && nActual < celt)
	{
		hr = Copy::copy(pelt, &*m_iter);

		if (pelt->vt == VT_DISPATCH)
		{
			CComDispatchDriver driver(pelt->pdispVal);

			driver.PutPropertyByName(L"ItemData", &CComVariant((long)m_iter.id()));
		}

		if (FAILED(hr))
		{
			while (rgelt < pelt)
				Copy::destroy(rgelt++);
			nActual = 0;
		}
		else
		{
			pelt++;
			m_iter++;
			nActual++;
		}
	}
	if (pceltFetched)
		*pceltFetched = nActual;
	if (SUCCEEDED(hr) && (nActual < celt))
		hr = S_FALSE;
	return hr;
}

template <class Base, const IID* piid, class T, class Copy, class CollType>
STDMETHODIMP IEnumWithIDOnSTLImpl<Base, piid, T, Copy, CollType>::Skip(ULONG celt)
{
	HRESULT hr = S_OK;

	while (celt--)
	{
		if (m_iter != m_pcollection->end())
			m_iter++;
		else
		{
			hr = S_FALSE;
			break;
		}
	}
	return hr;
}

template <class Base, const IID* piid, class T, class Copy, class CollType>
STDMETHODIMP IEnumWithIDOnSTLImpl<Base, piid, T, Copy, CollType>::Clone(Base** ppEnum)
{
	typedef CComObject<CComEnumWithIDOnSTL<Base, piid, T, Copy, CollType> > _class;
	HRESULT hRes = E_POINTER;
	if (ppEnum != NULL)
	{
		*ppEnum = NULL;
		_class* p;
		hRes = _class::CreateInstance(&p);
		if (SUCCEEDED(hRes))
		{
			hRes = p->Init(m_spUnk, *m_pcollection);
			if (SUCCEEDED(hRes))
			{
				p->m_iter = m_iter;
				hRes = p->_InternalQueryInterface(*piid, (void**)ppEnum);
			}
			if (FAILED(hRes))
				delete p;
		}
	}
	return hRes;
}

template <class Base, const IID* piid, class T, class Copy, class CollType, class ThreadModel = CComObjectThreadModel>
class ATL_NO_VTABLE CComEnumWithIDOnSTL :
	public IEnumWithIDOnSTLImpl<Base, piid, T, Copy, CollType>,
	public CComObjectRootEx< ThreadModel >
{
public:
	typedef CComEnumWithIDOnSTL<Base, piid, T, Copy, CollType, ThreadModel > _CComEnum;
	typedef IEnumWithIDOnSTLImpl<Base, piid, T, Copy, CollType > _CComEnumBase;
	BEGIN_COM_MAP(_CComEnum)
		COM_INTERFACE_ENTRY_IID(*piid, _CComEnumBase)
	END_COM_MAP()
};

/////////////////////////////////////////////////////////////////////

typedef CComEnumOnSTL<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT,
                  _CopyVariant, vector<CComVariant> > VarVarEnum;


/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// Attention: Difference to "ICollectionOnSTLImpl": this class is zero-based
//
template <class T, class CollType, class ItemType, class CopyItem, class EnumType>
class IMyCollectionOnSTLImpl : public T
{
protected:
#ifdef __COM_COLL_THREAD_SAFE
	CMutex m_mtx;
#endif

public:
	STDMETHOD(get_Count)(long* pcount)
	{
		if (pcount == NULL)
			return E_POINTER;
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		*pcount = m_coll.size();

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return S_OK;
	}
	STDMETHOD(get_Item)(long Index, ItemType* pvar)
	{
		//Index is 0-based
		if (pvar == NULL)
			return E_POINTER;
		HRESULT hr = E_FAIL;

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		CollType::iterator iter = m_coll.begin();
		while (iter != m_coll.end() && Index > 0)
		{
			iter++;
			Index--;
		}
		if (iter != m_coll.end())
			hr = CopyItem::copy(pvar, &*iter);

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return hr;
	}
	STDMETHOD(get__NewEnum)(IUnknown** ppUnk)
	{
		if (ppUnk == NULL)
			return E_POINTER;
		*ppUnk = NULL;
		HRESULT hRes = S_OK;
		CComObject<EnumType>* p;
		hRes = CComObject<EnumType>::CreateInstance(&p);
		if (SUCCEEDED(hRes))
		{
			LPDISPATCH pThis = this;

#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Lock();
#endif
			hRes = p->Init(pThis, m_coll);

#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Unlock();
#endif
			if (hRes == S_OK)
				hRes = p->QueryInterface(IID_IUnknown, (void**)ppUnk);
		}
		if (hRes != S_OK)
			delete p;
		return hRes;
	}
	CollType m_coll;
};

template <class T, class CollType>
class IMyVariantCollectionOnSTLImpl : public IMyCollectionOnSTLImpl<T, CollType
																	 , VARIANT
																	 , _CopyVariant
																	 , VarVarEnum>
									, public IPersistStreamInit
									, public IPersistStream
{
public:
	STDMETHOD(GetClassID)(CLSID* pClassID)
	{
		// override, if you get an assertation here
		ATLASSERT(FALSE);
		return E_NOTIMPL;
	}
	STDMETHOD(IsDirty)(void)
	{
		return S_OK;
	}       
	STDMETHOD(Load)(LPSTREAM pStm)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.clear();

		long	nCount;
		ULONG	nRead = 0;
		
		if (SUCCEEDED(pStm->Read(&nCount, sizeof(nCount), &nRead)) && nRead == sizeof(nCount))
		{
			for (long lauf = 0; lauf < nCount; lauf++)
			{
				VARTYPE vtRead;

				if (SUCCEEDED(pStm->Read(&vtRead, sizeof(VARTYPE), NULL)))
				{
					if (vtRead != VT_DISPATCH)
					{
						CComVariant var;

						if (SUCCEEDED(var.ReadFromStream(pStm)))
						{
							m_coll.push_back(var);
						}
					}
					else
					{
						CLSID clsid = CLSID_NULL;

						if (SUCCEEDED(ReadClassStm(pStm, &clsid)))
						{
							if (!IsEqualCLSID(clsid, CLSID_NULL))
							{
								IDispatchPtr pDisp;

								if (SUCCEEDED(pDisp.CreateInstance(clsid)))
								{
									CComVariant var;

									IPersistStreamPtr		pPersist;
									IPersistStreamInitPtr	pPersistInit;

									if (SUCCEEDED(pDisp->QueryInterface(&pPersist)))
									{
										pPersist->Load(pStm);
									}
									else if (SUCCEEDED(pDisp->QueryInterface(&pPersistInit)))
									{
										pPersistInit->Load(pStm);
									}
									var = (LPDISPATCH)pDisp;
									m_coll.push_back(var);
								}
							}
						}
					}
				}
			}
		}
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return S_OK;
	}
	STDMETHOD(Save)(LPSTREAM pStm, BOOL fClearDirty)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		ULONG	nWritten	= 0;
		long	nCount		= m_coll.size();

		if (SUCCEEDED(pStm->Write(&nCount, sizeof(nCount), &nWritten)) && nWritten == sizeof(nCount))
		{
			CollType::iterator iter = m_coll.begin();

			while (iter != m_coll.end())
			{
				CComVariant var(*iter);
				
				pStm->Write(&var.vt, sizeof(VARTYPE), NULL);

				if (var.vt == VT_DISPATCH)
				{
					if (var.pdispVal != NULL)
					{
						IPersistStreamPtr		pPersist;
						IPersistStreamInitPtr	pPersistInit;

						if (SUCCEEDED(var.pdispVal->QueryInterface(&pPersist)))
						{
							CLSID clsid = CLSID_NULL;
							pPersist->GetClassID(&clsid);

							WriteClassStm(pStm, clsid);
							pPersist->Save(pStm, FALSE);
						}
						else if (SUCCEEDED(var.pdispVal->QueryInterface(&pPersistInit)))
						{
							CLSID clsid = CLSID_NULL;
							pPersistInit->GetClassID(&clsid);

							WriteClassStm(pStm, clsid);
							pPersistInit->Save(pStm, FALSE);
						}
						else
							WriteClassStm(pStm, CLSID_NULL);
					}
					else
						WriteClassStm(pStm, CLSID_NULL);
				}
				else
				{
					var.WriteToStream(pStm);
				}
				iter++;
			}
		}
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return S_OK;
	}
	STDMETHOD(GetSizeMax)(ULARGE_INTEGER __RPC_FAR *pCbSize)
	{
		return E_NOTIMPL;
	}       
	STDMETHOD(InitNew)(void)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.clear();

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return S_OK;
	}
};
        

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

class CStatsTGHelper
{
public:
	CStatsTGHelper(bool bWantName = TRUE)
	{
		memset(&m_statstg, 0, sizeof(m_statstg));
		m_bWantName = bWantName;
	}
	~CStatsTGHelper()
	{
		Clear();
	}
	void Clear()
	{
		if (m_statstg.pwcsName != NULL)
			CoTaskMemFree(m_statstg.pwcsName);
		memset(&m_statstg, 0, sizeof(m_statstg));
	}
	operator STATSTG* ()
	{
		return &m_statstg;
	}
	operator const WCHAR* ()
	{
		return m_statstg.pwcsName;
	}
	operator WCHAR* ()
	{
		return m_statstg.pwcsName;
	}
public:
	STATSTG	m_statstg;
	bool	m_bWantName;
};

template <class T>
class CStorageIterator
{
public:

	CStorageIterator(IStorage* pStorage = NULL, DWORD dwIdx = MAXDWORD)
	{
		m_pStorage			= pStorage;
		m_dwCurrent			= dwIdx;
		m_dwCurrentStatsTG	= MAXDWORD;

		if (m_pStorage != NULL)
			m_pStorage->AddRef();

		CheckPos();
	}
    CStorageIterator(const CStorageIterator& x)
	{
		m_pStorage			= NULL;
		m_dwCurrent			= MAXDWORD;
		m_dwCurrentStatsTG	= MAXDWORD;

		operator=(x);
	}
	virtual ~CStorageIterator()
	{
		if (m_pStorage != NULL)
			m_pStorage->Release();
	}
    VARIANT operator*()              // dereferencing
    {
		CComVariant			vt;
		CComPtr<IDispatch>	obj;

		try
		{
			if (obj.CoCreateInstance(T::GetItemClsid()) == S_OK)
			{
				if (LoadObject(obj))
				{
					((IDispatch*)obj)->AddRef();
					vt = (IDispatch*)obj;
				}
			}
		}
		catch(_com_error e)
		{
			ATLTRACE(_T("CStorageIterator: Create of Object failed because: %s\n"), e.ErrorMessage());
		}
        return vt;
    }

/*         const VARIANT operator*() const  // dereferencing
    {
		CComVariant			vt;
        return vt;
    }
*/
    CStorageIterator& operator++()       // prefix
    {
		m_dwCurrent++;

        CheckPos();

		return *this;
    }

    CStorageIterator operator++(int)   // postfix
    {
        CStorageIterator temp = *this;
        ++*this;
        return temp;
    }

	CStorageIterator& operator=(const CStorageIterator& x)
	{
		if (m_pStorage != NULL)
			m_pStorage->Release();

		m_pStorage			= x.m_pStorage;
		m_dwCurrent			= x.m_dwCurrent;
		m_dwCurrentStatsTG	= x.m_dwCurrentStatsTG;

		if (m_pStorage != NULL)
			m_pStorage->AddRef();

		return *this;
	}
    bool operator==(const CStorageIterator& x) const
    {
        return m_dwCurrent == x.m_dwCurrent;
    }
    bool operator!=(const CStorageIterator& x) const
    {
        return m_dwCurrent != x.m_dwCurrent;
    }

public:        
	bool IsValid()
	{
		return m_pStorage != NULL && m_dwCurrent != MAXDWORD;
	}
	DWORD id()
	{
		WCHAR* sid = m_statstg;

		if (sid != NULL)
			return _wtol(sid);
		if (GetStatsTGFromCurrent())
		{
			sid = m_statstg;

			if (sid != NULL)
				return _wtol(sid);
		}
		return MAXDWORD;
	}
	bool SetToID(DWORD dwID)
	{
		IEnumSTATSTG*	pEnum		= NULL;
		DWORD			fetched		= 0;

		if (m_pStorage->EnumElements(0, NULL, 0, &pEnum) != S_OK)
			return false;

		m_dwCurrent = 0;

		CStatsTGHelper st;

		while(pEnum->Next(1, st, &fetched) == S_OK)
		{
			if (fetched == 1)
			{
				if (dwID == _wtol(st))
				{
					pEnum->Release();
					CheckPos();
					return true;
				}
			}
			st.Clear();
			m_dwCurrent++;
		}
		pEnum->Release();
		m_dwCurrent = MAXDWORD;

		return false;
	}
	bool GetStatsTGFromCurrent(bool bCreateIfNotThrere = false)
	{
		if (m_dwCurrentStatsTG != MAXDWORD && m_dwCurrentStatsTG == m_dwCurrent && (const WCHAR*)m_statstg != NULL)
			return true;

		if (!IsValid() && !bCreateIfNotThrere)
			return false;

		IEnumSTATSTG*	pEnum		= NULL;
		DWORD			fetched		= 0;

		if (m_pStorage->EnumElements(0, NULL, 0, &pEnum) != S_OK)
			return false;

		m_statstg.Clear();

		if (!IsValid() && bCreateIfNotThrere)
		{
			m_dwCurrent = 0;

			while(pEnum->Skip(1) == S_OK)
				m_dwCurrent++;
			
			// eine neue ID bestimmen
			DWORD dwID = m_dwCurrent;

			if (dwID > 0)
			{
				// die letzte Item-ID holen
				pEnum->Reset();

				pEnum->Skip(dwID-1);

				CStatsTGHelper st;

				if (pEnum->Next(1, st, &fetched) == S_OK)
				{
					if (fetched == 1)
					{
						dwID = _wtol(st) + 1;
					}
				}
			}
			pEnum->Release();

			WCHAR buf[32];

			swprintf(buf, L"%010lu", dwID);
			
			if ((m_statstg.m_statstg.pwcsName = (WCHAR*)CoTaskMemAlloc((wcslen(buf)+1)*sizeof(WCHAR))) != NULL)
				wcscpy(m_statstg.m_statstg.pwcsName, buf);
			else
				return false;

			m_dwCurrentStatsTG = m_dwCurrent;

			return true;
		}
		else
		{
			if (m_dwCurrent > 0)
				pEnum->Skip(m_dwCurrent);

			if (pEnum->Next(1, m_statstg, &fetched) == S_OK)
			{
				if (fetched == 1)
				{
					pEnum->Release();

					m_dwCurrentStatsTG = m_dwCurrent;

					return true;
				}
			}
		}
		pEnum->Release();

		return false;
	}
	void* GetStorageFromCurrent(bool bLoad = true)
	{
		if (!IsValid())
			return NULL;

		switch(m_statstg.m_statstg.type)
		{
			case STGTY_STORAGE:
			{
				IStorage* pStorage = NULL;

				if (bLoad)
				{
					if (m_pStorage->OpenStorage(m_statstg, NULL
							  , STGM_SHARE_EXCLUSIVE|STGM_READ
							  , NULL
							  , 0
							  , &pStorage) != S_OK)
					{
						pStorage = NULL;
						ATLTRACE(_T("CStorageIterator: OpenStorage failed\n"));
					}
				}
				else
				{
					if (m_pStorage->CreateStorage(m_statstg
							  , STGM_SHARE_EXCLUSIVE|STGM_READWRITE|STGM_CREATE 
							  , NULL
							  , 0
							  , &pStorage) != S_OK)
					{
						pStorage = NULL;
						ATLTRACE(_T("CStorageIterator: CreateStorage failed\n"));
					}
				}
				return pStorage;
			}
			break;

			case STGTY_STREAM:
			{
				IStream* pStream = NULL;

				if (bLoad)
				{
					if (m_pStorage->OpenStream(m_statstg, NULL
							  , STGM_SHARE_EXCLUSIVE|STGM_READ
							  , 0
							  , &pStream) != S_OK)
					{
						pStream = NULL;
						ATLTRACE(_T("CStorageIterator: OpenStream failed\n"));
					}
				}
				else
				{
					if (m_pStorage->CreateStream(m_statstg
							  , STGM_SHARE_EXCLUSIVE|STGM_READWRITE|STGM_CREATE
							  , 0
							  , 0
							  , &pStream) != S_OK)
					{
						pStream = NULL;
						ATLTRACE(_T("CStorageIterator: CreateStream failed\n"));
					}
				}
				return pStream;
			}
			break;
		}
		return NULL;
	}
	void CheckPos()
	{
		if (m_dwCurrent != MAXDWORD)
		{
		 // Position g�ltig ?
			if (!GetStatsTGFromCurrent())
			{
			   m_dwCurrent = MAXDWORD;
			}
		}
	 }
	 bool DeleteObject()
	 {
		bool bResult = false;

		if (m_dwCurrent != MAXDWORD)
		{
			if (GetStatsTGFromCurrent())
			{
				bResult = m_pStorage->DestroyElement(m_statstg) == S_OK;

				if (bResult)
				{
					if (m_pStorage->Commit(STGC_CONSOLIDATE) != S_OK)
						m_pStorage->Commit(STGC_DEFAULT);
				}
			}
		}
		return bResult;
	 }
	 bool QueryStorageFromObject(LPDISPATCH pDispatch, IPersistStorage** ppPersistStorage, IPersistStream** ppPersistStream, IPersistStreamInit** ppPersistStreamInit)
	 {
		bool bResult = false;

		if (pDispatch == NULL || ppPersistStorage == NULL || ppPersistStream == NULL || ppPersistStreamInit == NULL)
			return false;

		*ppPersistStorage		= NULL;
		*ppPersistStream		= NULL;
		*ppPersistStreamInit	= NULL;

		try
		{
			// try storage-based first
			if (pDispatch->QueryInterface(ppPersistStorage) == S_OK)
			{
				bResult = true;
			}
			else
			{
				*ppPersistStorage = NULL;

				ATLTRACE(_T("CStorageIterator: PersistStorage no supported of Object\n"));

				if (pDispatch->QueryInterface(ppPersistStream) != S_OK)
				{
					*ppPersistStream = NULL;

					if (pDispatch->QueryInterface(ppPersistStreamInit) != S_OK)
					{
						*ppPersistStreamInit = NULL;
						ATLTRACE(_T("CStorageIterator: PersistStream no supported of Object\n"));
					}
					else
					{
						bResult = true;
					}
				}
				else
				{
					bResult = true;
				}
			}
		}
		catch(_com_error e)
		{
			ATLTRACE(_T("CStorageIterator: QueryInterface on Object failed because: %s\n"), e.ErrorMessage());
		}
		return bResult;
	 }
	 bool PersistObject(LPDISPATCH pDispatch, bool bLoad)
	 {
		bool bResult = false;

		if (pDispatch == NULL)
			return bResult;

		try
		{
			// try storage-based first
			IPersistStorage*	pPersistStorage		= NULL;
			IPersistStream*		pPersistStream		= NULL;
			IPersistStreamInit* pPersistStreamInit	= NULL;

			if (QueryStorageFromObject(pDispatch, &pPersistStorage, &pPersistStream, &pPersistStreamInit))
			{
				if (GetStatsTGFromCurrent(!bLoad))
				{
					if (pPersistStorage != NULL)
					{
						if (!bLoad)
							m_statstg.m_statstg.type = STGTY_STORAGE;

						if (m_statstg.m_statstg.type == STGTY_STORAGE)
						{
							IStorage* pStorage = (IStorage*)GetStorageFromCurrent(bLoad);

							ATLASSERT(pStorage != NULL);

							if (bLoad)
								bResult = pPersistStorage->Load(pStorage) == S_OK;
							else
							{
								bResult = pPersistStorage->Save(pStorage, FALSE) == S_OK;

								pStorage->Commit(STGC_DEFAULT);
								m_pStorage->Commit(STGC_DEFAULT);
							}
							pStorage->Release();

						}
					}
					else
					{
						if (!bLoad)
							m_statstg.m_statstg.type = STGTY_STREAM;

						if (m_statstg.m_statstg.type == STGTY_STREAM)
						{
							// generate Stream Object
							IStream* pStream = (IStream*)GetStorageFromCurrent(bLoad);

							ATLASSERT(pStream != NULL);

							if (pPersistStream != NULL)
							{
								if (bLoad)
									bResult = pPersistStream->Load(pStream) == S_OK;
								else
								{
									bResult = pPersistStream->Save(pStream, TRUE) == S_OK;
									m_pStorage->Commit(STGC_DEFAULT);
								}
							}
							else if (pPersistStreamInit != NULL)
							{
								if (bLoad)
									bResult = pPersistStreamInit->Load(pStream) == S_OK;
								else
								{
									bResult = pPersistStreamInit->Save(pStream, TRUE) == S_OK;
									m_pStorage->Commit(STGC_DEFAULT);
								}
							}
							pStream->Release();
						}
					}
				}
				if (pPersistStorage != NULL)
					pPersistStorage->Release();
				if (pPersistStream != NULL)
					pPersistStream->Release();
				if (pPersistStreamInit != NULL)
					pPersistStreamInit->Release();
			}
			else
			{
				ATLTRACE(_T("CStorageIterator: QueryStorageFromObject failed\n"));
			}
		}
		catch(_com_error e)
		{
			ATLTRACE(_T("CStorageIterator: Load of Object failed because: %s\n"), e.ErrorMessage());
		}
		return bResult;
	 }
	 bool LoadObject(LPDISPATCH pDispatch)
	 {
		if (!IsValid())
			return false;

		return PersistObject(pDispatch, TRUE);
	 }
	 bool SaveObject(LPDISPATCH pDispatch)
	 {
		return PersistObject(pDispatch, FALSE);
	 }

public:        
	IStorage*		m_pStorage;
	DWORD			m_dwCurrent; // pointer to current element 
	CStatsTGHelper	m_statstg;
	DWORD			m_dwCurrentStatsTG;  // pointer "m_statstg" is pointing to
};

/////////////////////////////////////////////////////////////////////

template <class T>
class CStorageCollectionContainer
{
public:
	typedef CStorageIterator<T> iterator;

public:
	CStorageCollectionContainer()
	{
		m_pStorage = NULL;
	}
	virtual ~CStorageCollectionContainer()
	{
	}

	virtual iterator end()
	{
		return iterator(m_pStorage);
	}
	virtual iterator begin()
	{
		return iterator(m_pStorage, 0);
	}
	virtual iterator push_back(const VARIANT& _X)
	{
		LPDISPATCH pDisp = GetDispFromVariant(_X);

		if (m_pStorage != NULL && pDisp != NULL)
		{
			iterator i(m_pStorage);
			
			i.SaveObject(pDisp);
			
			return i;
		}
		return end();
	}
	virtual iterator modify(DWORD idx, const VARIANT& _X) 
	{
		LPDISPATCH pDisp = GetDispFromVariant(_X);

		if (m_pStorage != NULL && pDisp != NULL)
		{
			iterator i(m_pStorage, idx);

			if (i.IsValid())
			{
				i.SaveObject(pDisp);
				return i;
			}
		}
		return end();
	}
	virtual iterator modify_byid(DWORD id, const VARIANT& _X) 
	{
		LPDISPATCH pDisp = GetDispFromVariant(_X);

		if (m_pStorage != NULL && pDisp != NULL)
		{
			iterator i(m_pStorage);

			i.SetToID(id);

			if (i.IsValid())
			{
				i.SaveObject(pDisp);
				return i;
			}
		}
		return end();
	}
	virtual void erase(DWORD idx, VARIANT* pvar = NULL)
	{ 
		iterator i(m_pStorage, idx);

		if (pvar != NULL)
		{
			VariantInit(pvar);
			VariantCopyInd(pvar, &*i);
		}
		
		if (i.IsValid())
		{
			i.DeleteObject();
		}
	}
	virtual void erase_byid(DWORD id, VARIANT* pvar = NULL)
	{ 
		iterator i(m_pStorage);

		i.SetToID(id);

		if (i.IsValid())
		{
			if (pvar != NULL)
			{
				VariantInit(pvar);
				VariantCopyInd(pvar, &*i);
			}
			i.DeleteObject();
		}
	}
	virtual void clear()
	{
		while (size() > 0)
		{
			begin().DeleteObject();
		}
	}
	virtual DWORD	size()
	{
		DWORD dwSize = 0;

		iterator i = begin();
		iterator iend = end();

		while (i != iend)
		{
			++i;
			++dwSize;
		}
		return dwSize;
	}
private:
	static LPDISPATCH GetDispFromVariant(const VARIANT& _X)
	{
		if (_X.vt == VT_DISPATCH)
		{
			return _X.pdispVal;
		}
		VARIANT vt;

		vt = _X;

		if (VariantChangeType(&vt, &vt, 0, VT_DISPATCH) == S_OK)
			return vt.pdispVal;

		return NULL;
	}
public:
	IStorage*	m_pStorage;
};

/////////////////////////////////////////////////////////////////////

template <class T>
class CFileCollectionContainer : public CStorageCollectionContainer<T>
{
public:
	CFileCollectionContainer()
	{
		HRESULT hr;

		m_strDocFileName = T::GetDocFileName();

		if ((hr = StgOpenStorage(m_strDocFileName, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &m_pStorage)) != S_OK)
		{
			ATLTRACE(_T("CFileCollectionContainer::CFileCollectionContainer(): StgOpenStorage failed because %lx\n"), hr);

			// mh, perhaps existing, but haven't got the required rights
			if ((hr = StgOpenStorage(m_strDocFileName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &m_pStorage)) != S_OK)
			{
				ATLTRACE(_T("CFileCollectionContainer::CFileCollectionContainer(): StgOpenStorage (read-only) failed because %lx\n"), hr);

				// doesn't exist -> generate
				if ((hr = StgCreateDocfile(m_strDocFileName, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &m_pStorage)) != S_OK)
				{
					ATLTRACE(_T("CFileCollectionContainer::CFileCollectionContainer(): StgCreateDocfile failed because %lx\n"), hr);
					m_pStorage = NULL;
					m_strDocFileName = "";
				}
			}
		}
	}
	virtual ~CFileCollectionContainer()
	{
		if (m_pStorage != NULL)
		{
			m_pStorage->Commit(STGC_DEFAULT);
			m_pStorage->Release();
		}
	}
public:
	_bstr_t			m_strDocFileName;
};

/////////////////////////////////////////////////////////////////////

template <class T>
class CRegistryCollectionContainer : public CStorageCollectionContainer<T>
{
public:
	CRegistryCollectionContainer()
	{
		HRESULT hr;
		m_bFullAccess = FALSE;

		m_hKeyParent = T::GetKeyParent();
		m_strRegKey	 = T::GetRegKey();

		if ((hr = CRegistryStorage::StgCreateReg(m_hKeyParent, m_strRegKey, STGM_READWRITE, 0, &m_pStorage)) != S_OK)
		{
			ATLTRACE(_T("CRegistryCollectionContainer::CRegistryCollectionContainer(): StgCreateReg failed because %lx\n"), hr);

			// mh, perhaps existing, but haven't got the required rights
			if ((hr = CRegistryStorage::StgCreateReg(m_hKeyParent, m_strRegKey, STGM_READ, 0, &m_pStorage)) != S_OK)
			{
				ATLTRACE(_T("CRegistryCollectionContainer::CRegistryCollectionContainer(): StgCreateReg (read-only) failed because %lx\n"), hr);

				// doesn't exist -> generate
				if ((hr = CRegistryStorage::StgCreateReg(m_hKeyParent, m_strRegKey, STGM_READWRITE|STGM_CREATE, 0, &m_pStorage)) != S_OK)
				{
					ATLTRACE(_T("CRegistryCollectionContainer::CRegistryCollectionContainer(): StgCreateReg failed because %lx\n"), hr);
					m_pStorage = NULL;
				}
				else
					m_bFullAccess = TRUE;
			}
		}
		else
		   m_bFullAccess = TRUE;
	}
	virtual ~CRegistryCollectionContainer()
	{
		if (m_pStorage != NULL)
		{
			m_pStorage->Commit(STGC_DEFAULT);
			m_pStorage->Release();
		}
	}
public:
	BOOL		m_bFullAccess;
	HKEY		m_hKeyParent;
	_bstr_t		m_strRegKey;
};

/////////////////////////////////////////////////////////////////////

template <class T>
class CFileSystemCollectionContainer : public CStorageCollectionContainer<T>
{
public:
	CFileSystemCollectionContainer()
	{
		HRESULT hr;

		m_strFolder = T::GetFolderName();

		if ((hr = CFileSystemStorage::StgCreateFolder(m_strFolder, STGM_READWRITE, 0, &m_pStorage)) != S_OK)
		{
			ATLTRACE(_T("CFileSystemCollectionContainer::CFileSystemCollectionContainer(): StgCreateFolder failed because %lx\n"), hr);

			// mh, perhaps not existing
			if ((hr = CFileSystemStorage::StgCreateFolder(m_strFolder, STGM_READWRITE|STGM_CREATE, 0, &m_pStorage)) != S_OK)
			{
				ATLTRACE(_T("CFileSystemCollectionContainer::CFileSystemCollectionContainer(): StgCreateFolder failed because %lx\n"), hr);

				m_pStorage = NULL;
			}
		}
	}
	virtual ~CFileSystemCollectionContainer()
	{
		if (m_pStorage != NULL)
		{
			m_pStorage->Commit(STGC_DEFAULT);
			m_pStorage->Release();
		}
	}
public:
	_bstr_t			m_strFolder;
};

/////////////////////////////////////////////////////////////////////

class _MyVariantCopy
{
public:
	static HRESULT copy(VARIANT* p1, VARIANT* p2) 
	{
		HRESULT hr = VariantCopy(p1, p2);

		destroy(p2);
		return hr;
	}
	static void init(VARIANT* p)
	{
		p->vt = VT_EMPTY;
	}
	static void destroy(VARIANT* p)
	{
		VariantClear(p);
	}
};

/////////////////////////////////////////////////////////////////////

template<class Coll, class T>
class IStorageCollectionImpl
	: public IMyCollectionOnSTLImpl<T
									, Coll 
									, VARIANT
									, _MyVariantCopy
									, CComEnumWithIDOnSTL<IEnumVARIANT
													, &IID_IEnumVARIANT
													, VARIANT
													, _MyVariantCopy
													, Coll
												   >
									 >
{
protected:
	virtual void OnCollectionInsert(const VARIANT& vtItem) {}
	virtual void OnCollectionUpdate(const VARIANT& vtItem) {}
	virtual void OnCollectionDelete(const VARIANT& vtItem) {}

public:
	STDMETHOD(Add)(/*[in]*/ VARIANT Item)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		Coll::iterator iter = m_coll.push_back(Item);

		if (iter != m_coll.end())
		{
#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Unlock();
#endif
			CComVariant vtDisp;

			::VariantCopyInd(&vtDisp, &Item);

			if (vtDisp.vt == VT_DISPATCH)
			{
				CComDispatchDriver driver(vtDisp.pdispVal);

				driver.PutPropertyByName(L"ItemData", &CComVariant((long)iter.id()));
			}
			OnCollectionInsert(vtDisp);

			return S_OK;
		}
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return E_FAIL;
	}
	STDMETHOD(Remove)(/*[in]*/ long Index)
	{
		VARIANT var;

		VariantInit(&var);

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.erase(Index, &var);

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		OnCollectionDelete(var);

		return S_OK;
	}
	STDMETHOD(Clear)()
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.clear();

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		VARIANT var;

		VariantInit(&var);
		
		OnCollectionDelete(var);

		return S_OK;
	}
	STDMETHOD(Modify)(/*[in]*/ long Index, /*[in]*/ VARIANT Item)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		Coll::iterator iter = m_coll.modify(Index, Item);

		if (iter != m_coll.end())
		{
#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Unlock();
#endif
			CComVariant vtDisp;

			::VariantCopyInd(&vtDisp, &Item);

			if (vtDisp.vt == VT_DISPATCH)
			{
				CComDispatchDriver driver(vtDisp.pdispVal);

				driver.PutPropertyByName(L"ItemData", &CComVariant((long)iter.id()));
			}
			OnCollectionUpdate(vtDisp);

			return S_OK;
		}
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return E_FAIL;
	}
	STDMETHOD(get_Item)(long Index, LPVARIANT pvar)
	{
		//Index is 0-based
		if (pvar == NULL)
			return E_POINTER;
		HRESULT hr = E_FAIL;

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		Coll::iterator iter = m_coll.begin();

		while (iter != m_coll.end() && Index > 0)
		{
			iter++;
			Index--;
		}
		if (iter != m_coll.end())
		{
			hr = _MyVariantCopy::copy(pvar, &*iter);

#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Unlock();
#endif
			if (pvar->vt == VT_DISPATCH)
			{
				CComDispatchDriver driver(pvar->pdispVal);

				driver.PutPropertyByName(L"ItemData", &CComVariant((long)iter.id()));
			}
		}
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return hr;
	}
	STDMETHOD(get_ItemID)(/*[in]*/ long Index, /*[out]*/ VARIANT* pID, /*[out, retval]*/ LPVARIANT pvar)
	{
		//Index is 1-based
		if (pvar == NULL || pID == NULL)
			return E_POINTER;

		HRESULT hr = E_FAIL;
		
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		Coll::iterator iter = m_coll.begin();
		
		while (iter != m_coll.end() && Index > 0)
		{
			iter++;
			Index--;
		}
		if (iter != m_coll.end())
		{
			hr = _MyVariantCopy::copy(pvar, &*iter);

#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Unlock();
#endif
			pID->lVal	= iter.id();
			pID->vt	= VT_I4;

			if (pvar->vt == VT_DISPATCH)
			{
				CComDispatchDriver driver(pvar->pdispVal);

				driver.PutPropertyByName(L"ItemData", pID);
			}
		}
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return hr;
	}
	STDMETHOD(get_ItemByID)(/*[in]*/ long ID, /*[out, retval]*/ LPVARIANT pvar)
	{
		if (pvar == NULL)
			return E_POINTER;

		HRESULT hr = E_FAIL;
		
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		Coll::iterator iter = m_coll.begin();
		
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		iter.SetToID(ID);

		if (iter.IsValid())
		{
			hr = _MyVariantCopy::copy(pvar, &*iter);

			if (pvar->vt == VT_DISPATCH)
			{
				CComDispatchDriver driver(pvar->pdispVal);

				driver.PutPropertyByName(L"ItemData", &CComVariant((long)iter.id()));
			}
		}
		return hr;
	}
	STDMETHOD(RemoveByID)(/*[in]*/ long ID)
	{
		VARIANT var;

		VariantInit(&var);

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.erase_byid(ID, &var);

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		CComVariant vtDisp;

		::VariantCopyInd(&vtDisp, &var);

		if (vtDisp.vt == VT_DISPATCH)
		{
			CComDispatchDriver driver(vtDisp.pdispVal);

			driver.PutPropertyByName(L"ItemData", &CComVariant(ID));
		}
		OnCollectionDelete(vtDisp);

		return S_OK;
	}
	STDMETHOD(ModifyByID)(/*[in]*/ long ID, /*[in]*/ VARIANT Item)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		Coll::iterator iter = m_coll.modify_byid(ID, Item);

		if (iter != m_coll.end())
		{
#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Unlock();
#endif
			CComVariant vtDisp;

			::VariantCopyInd(&vtDisp, &Item);

			if (vtDisp.vt == VT_DISPATCH)
			{
				CComDispatchDriver driver(vtDisp.pdispVal);

				driver.PutPropertyByName(L"ItemData", &CComVariant((long)iter.id()));
			}
			OnCollectionUpdate(vtDisp);

			return S_OK;
		}
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		return E_FAIL;
	}
};


/////////////////////////////////////////////////////////////////////

template <class T>
class IVariantCollectionImpl : public IMyVariantCollectionOnSTLImpl<T, vector<CComVariant> >
{
protected:
	virtual void OnCollectionInsert(const VARIANT& vtItem) {}
	virtual void OnCollectionUpdate(const VARIANT& vtItem) {}
	virtual void OnCollectionDelete(const VARIANT& vtItem) {}

public:
	STDMETHOD(Add)(/*[in]*/ VARIANT Item)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.push_back(Item);

#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Unlock();
#endif

		OnCollectionInsert(Item);

		return S_OK;
	}
	STDMETHOD(Remove)(/*[in]*/ long Index)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		// for std::vector
		vector<CComVariant>::iterator i = m_coll.begin();

		while (Index--)
			++i;

		CComVariant vtItem = *i;

		m_coll.erase(i);

#ifdef __COM_COLL_THREAD_SAFE
			m_mtx.Unlock();
#endif
		OnCollectionDelete(vtItem);

		return S_OK;
	}
	STDMETHOD(Clear)()
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.clear();

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		CComVariant vtEmpty;

		OnCollectionDelete(vtEmpty);

		return S_OK;
	}
	STDMETHOD(Modify)(/*[in]*/ long Index, /*[in]*/ VARIANT Item)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll[Index] = Item;

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		OnCollectionUpdate(Item);

		return S_OK;
	}
	STDMETHOD(get_ItemID)(/*[in]*/ long Index, /*[out]*/ VARIANT* pID, /*[out, retval]*/ LPVARIANT pVariant)
	{
		return E_NOTIMPL;
	}
	STDMETHOD(get_ItemByID)(/*[in]*/ long ID, /*[out, retval]*/ LPVARIANT pVariant)
	{
		return E_NOTIMPL;
	}
	STDMETHOD(RemoveByID)(/*[in]*/ long ID)
	{
		return E_NOTIMPL;
	}
	STDMETHOD(ModifyByID)(/*[in]*/ long ID, /*[in]*/ VARIANT Item)
	{
		return E_NOTIMPL;
	}
};

/////////////////////////////////////////////////////////////////////

template <class T>
class IVariantWithStandardIDCollectionImpl : public IMyVariantCollectionOnSTLImpl<T, vector<CComVariant> >
{
protected:
	virtual void OnCollectionInsert(const VARIANT& vtItem) {}
	virtual void OnCollectionUpdate(const VARIANT& vtItem) {}
	virtual void OnCollectionDelete(const VARIANT& vtItem) {}

public:
	STDMETHOD(get_Item)(VARIANT Index, LPVARIANT pVariant)
	{
		if (Index.vt == VT_I4)
		{
			return IMyCollectionOnSTLImpl<T, vector<CComVariant>, VARIANT, _CopyVariant, VarVarEnum>::get_Item(Index.lVal, pVariant);
		}
		return get_ItemByID(Index, pVariant);				
	}
	STDMETHOD(Add)(/*[in]*/ VARIANT Item)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.push_back(Item);

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		OnCollectionInsert(Item);

		return S_OK;
	}
	STDMETHOD(Remove)(/*[in]*/ long Index)
	{
		// for std::vector
		vector<CComVariant>::iterator i = m_coll.begin();

		while (Index--)
			++i;

		CComVariant vtItem = *i;

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.erase(i);

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		OnCollectionDelete(vtItem);

		return S_OK;
	}
	STDMETHOD(Remove)(/*[in]*/ VARIANT Index)
	{
		if (Index.vt == VT_I4)
		{
			return Remove(Index.lVal);
		}
		return RemoveByID(Index);
	}
	STDMETHOD(Clear)()
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll.clear();

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		CComVariant vtEmpty;

		OnCollectionDelete(vtEmpty);

		return S_OK;
	}
	STDMETHOD(Modify)(/*[in]*/ long Index, /*[in]*/ VARIANT Item)
	{
#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Lock();
#endif
		m_coll[Index] = Item;

#ifdef __COM_COLL_THREAD_SAFE
		m_mtx.Unlock();
#endif
		OnCollectionUpdate(Item);

		return S_OK;
	}
	STDMETHODIMP Modify(/*[in]*/ VARIANT Index, /*[in]*/ VARIANT Item)
	{
		if (Index.vt == VT_I4)
		{
			return Modify(Index.lVal, Item);
		}
		return ModifyByID(Index, Item);
	}
	STDMETHOD(get_ItemID)(/*[in]*/ long Index, /*[out]*/ VARIANT* pID, /*[out, retval]*/ LPVARIANT pVariant)
	{
		if (IMyCollectionOnSTLImpl<T, vector<CComVariant>, VARIANT, _CopyVariant, VarVarEnum>::get_Item(Index, pVariant) == S_OK)
		{
			if (pVariant->vt == VT_DISPATCH)
			{
				CComDispatchDriver pDispDriver(pVariant->pdispVal);

				return pDispDriver.GetPropertyByName(L"ID", pID);
			}
		}
		return E_FAIL;
	}
	STDMETHOD(get_ItemByID)(/*[in]*/ VARIANT ID, /*[out, retval]*/ LPVARIANT pVariant)
	{
		long nIdx = GetIndexByID(ID);

		if (nIdx >= 0)
			return IMyCollectionOnSTLImpl<T, vector<CComVariant>, VARIANT, _CopyVariant, VarVarEnum>::get_Item(nIdx, pVariant);

		return E_FAIL;
	}
	STDMETHOD(RemoveByID)(/*[in]*/ VARIANT ID)
	{
		long nIdx = GetIndexByID(ID);

		if (nIdx >= 0)
			return Remove(nIdx);

		return E_FAIL;
	}
	STDMETHOD(ModifyByID)(/*[in]*/ VARIANT ID, /*[in]*/ VARIANT Item)
	{
		long nIdx = GetIndexByID(ID);

		if (nIdx >= 0)
			return Modify(nIdx, Item);

		return E_FAIL;
	}
protected:
	long GetIndexByID(const VARIANT& ID)
	{
		long nCount;

		get_Count(&nCount);

		for (long lauf = 0; lauf < nCount; lauf++)
		{
			CComVariant vtValue;

			if (IMyCollectionOnSTLImpl<T, vector<CComVariant>, VARIANT, _CopyVariant, VarVarEnum>::get_Item(lauf, &vtValue) == S_OK)
			{
				ATLASSERT(vtValue.vt == VT_DISPATCH);

				CComDispatchDriver pDispValue(vtValue.pdispVal);

				CComVariant vtID;

				if (pDispValue.GetPropertyByName(L"ID", &vtID) == S_OK)
				{
					if (vtID == ID)
					{
						return lauf;
					}
				}
			}
		}
		return -1;
	}

};

#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions