// 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