#pragma once
// These collection helpers provide for a managed collection of objects, each holding a reference
// back to the parent collection, bypassing the problems encountered by circular referencing.
// This is achieved by having the collection contain the data required to create the member objects,
// rather than holding references to the member objects themselves. These MemberData Objects
// DO NOT hold onto a reference to the parent - only the MemberObjects that are generated
// have this reference.
// An example of how this works would be the classic Employee/Department example.
// Each department is a collection of emplyees, but each employee needs a refrence back to
// their department. Using this method, there would be an EmployeeData object which is collected.
// A possible scenario for a department of 10 employees would be:
// 1. Client creates a department object. EmployeeData Reference count will be for each
// of the 10 objects, and the department reference count is 1 for the client's reference.
// 2. Client creates a new employee object. This, in turn, creates an employee data object.
// Employee reference count = 1 for the client reference, and EmployeeData reference count
// = 1 for reference in employee.
// 3. New Employee is added to department. The Employee's parent property is set to the
// department object, increasing its reference count to 2. The EmployeeData is added to
// the collection, increaing its reference count to 2. The Employee object's reference
// count is still only 1, for the client's reference to it.
// 4. The client retrieves an existing employee from the collection. This creates a new
// Employee object that is refernced only by the client. This new employee object has
// a reference to its EmployeeData object, increasing its reference count to 2. The
// Employee object also has a reference to the department, increasing its reference to 3.
// 5. The client destroys its own reference to the department. This decreases its reference
// count to 2, for the references that are still held by the Employee objects.
// As the department is not yet destroyed, there is no change to any of the EmployeeData
// reference counts.
// 6. The client releases it references of the employees. As their reference count was only 1
// each, these objects get destroyed. This decreases the individual EmployeeData reference
// counts to 1 (for the reference still held by the department), and then decreases the
// department's reference count to zero (as there are no more references to it), causing the
// department to be destroyed. Destroying the department decreases the reference count
// of the EmployeeData members of the collection to zero, causeing these objects to also
// be destroyed, and, hey-presto, all references to everything is gone.
/////////////////////////////////////////////////////
// Implementation
// --------------
// There are a few thing required to implement this system of object collections. There are
// three implementation classes involved in this header, one for the Collection itself, one
// for the Object exposed by the collection, and one for the data required to generate the
// exposed object. It is this last object that is, in fact, stored in the collection. The
// exposed object references this data object for setting/retrieving its properties.
// CContainer Class
// ----------------
// The first of these classes is the collection object itself. This is implemented by the
// CContainer class, which is templated to take the typename of the exposed interface,
// and an STL collection type which containes IMemberData interface pointers. Implemetation
// code is included for list<IMemberData*>, vector<IMemberData*>, set<IMemberData*> and
// map<CComBSTR, IMemberData*> collections. The usage and size of the collection will
// determine your prefernce for STL container.
//
// When you inherit from CContainer, the usual collection interfaces have already been implemented.
// They have, not, however, been exposed by the IContainer interface, allowing the developer
// to choose which of these interfaces should be exposed to the user. These methods include:
// Add(IMember* pObject) Used for adding an object to a list, vector, or set
// type collection. (Can also be used for a map, but is
// inefficient as the map key needs to be extracted from
// the object). The add method extracts the IMemberData
// object from the IMember interface, and adds that to the
// collection, incrementing the Data's reference count. This
// refernce is destroyed when the collection is destroyed. It
// also sets the Parent property of the Member object to the
// collection, incrementing the collection's reference count.
// This reference is destroyed when the member object is destroyed.
// Add(VARIANT Index, IMember *Object)
// This method is similar to the previous Add Method, but
// associates an index with the member. This is most efficient
// for a map based collection where the index is a key. The
// index parameter is ignored for a set based collection, and
// this method really shouldn't be used in that case. For list
// and vector based collections, the method assumes that the
// index is the location that the new object should be inserted
// before. If the index is not found, then the object is inserted
// at the end.
// Remove(VARIANT Index) Removes the object identified by Index. If the index is found,
// the MemberData object is removed from the collection, and its
// reference count is decremented. Providing there are no
// other references held to this data (by the client holding a
// reference to the Member object), then this data object will
// be destroyed. The only possibility for a logic error here
// occurs if a member is removed from the collection, and
// is still referenced by the client, the client's reference
// will still point to the collection in it's parent property.
// get_Item(VARIANT Index, IMember **ppObject)
// This method actually creates a new Member object from the
// data stored in the collection. The member includes a reference
// to the parent, and to the data object, but only the client
// holds a reference to this newly created object.
// Count(long *pCount) returns the number of objects in the collection
// get__NewEnum(IUnknown **ppUnk)
// Returns an enumerator object (implementing IEnumVARIANT).
// The enumerator contains a reference to the collection, so
// the collection will not get destroyed while there are
// enumerators active.
// CMember class
// -------------
// Inherit your exposed collection members from the CMember class. This class is templated
// such that you pass your Member Data class, inherited from IMemberData, and the interface
// implemented by your MemberData class. This can be IMemberData, or any other interface
// implemented by the class. It is good idea is to implement the same interface as the CMember
// exposes, to allow simple transfreeing of data from the data object to the exposed
// object.
//
// Not that the CMember constructor creates a new CMemebrData object, and holds on to its COM
// reference in the m_pData member. This allows a createable object that can be added to
// a collection. If the object is being created from the Data Object (ie: it is being retrieved
// from the collection), then this data object is replaced by the one in storeed in the collection.
// CMember has the following members.
// CComPtr<IMemberDataInterface> m_pData
// This member holds the reference to the data object.
// CComPtr<IContainer> m_pParent
// A reference to the parent container object
// get_Data(IMemberData **ppData)
// Returns the reference to the data object through COM
// put_Data(IMemberData *pData)
// Sets the COM reference to the data object
// get_Parent(IContainer *pParent)
// Returns the COM reference to the parent collection.
// CMemberData Class
// -----------------
// The CMemberData is the class that holds the identifying data to create the exposed
// objects of your collection on demand. References to these objects are what is actually
// stored in the collection, and are used to create the objects your collection actually
// wants to expose.
// The simplest idea is to have your exppoed object's full implementation taken care of
// in this Data Object, with the exception of methods that deal directly with the parent.
// YOU CANNOT HOLD ANY REFERENCES TO THE PARENT COLLECTION, OR TO THE EXPOSED OBJECTS IN
// THIS OBJECT, OR THE WHOLE SYSTEM FALLS DOWN. Your exposed object simply passes its
// exposed interface methods through to this object to produce the desired results.
// This class takes a single template parameter, which is your exposed Coclass name, to
// enable this object to create instances of the exposed object. The CreateObject method
// is implemented to perform this function.
// CMemberData also declares a number of other methods, which are designed to help with
// the indexing and referenceing of the objects within the STL collection. The CanCompareXXX
// and CompareXXX methods no not need to be implemented unless you want to index
// your collections on some property of the data. See the description of the these methods
// below for further details.
//
// CreateObject(IMember **ppObject);
// This method uses your CoClass to create an instance
// if your exposed class, and then sets the reference to
// its data to "this".
// CanCompareKeys();
// If the CompareKey() and the get_Key methods have been
// implemented for this object, then this method should
// return S_OK. Otherwise, this method should return S_FALSE.
// CanCompareObjects();
// This method returns S_OK if the CompareObject method has
// been implemented, or S_FALSE if it hasn't.
// get_Key(BSTR *pKey);
// By default, this method returns E_NOTIMPL. However, if
// your objects contain some sort of Key field, you can
// implement this method to allow the creation of map
// type collections, where the index of the map is included
// in the object.
// CompareKey(BSTR sKey, short *pCompare);
// This method compares a given string key with the object
// itself, and returns a -ve, 0, or +ve result in pCompare
// if the objects key is less than, equal to, or greater than
// the supplied key respectively.
// CompareObject(IMemberData *pObject, short *pCompare);
// By default, this method returns E_NOTIMPL. It is included
// so that the developer may implement a comparison function
// on the member objects themselves. If this function is
// implemented, CanCompareObjects should be overwritten
// to return S_OK. The function should return a -ve, 0, or
// +ve value in pCompare if the object being compared is
// less than, equal to, or greater than the current object
// respectively.
// STL Collection helpers
// ----------------------
// A less than and an equal to operator for IMemberData interface pointers
// These work whether or not the CompareXXX interfaces have been implemented
#include <list>
#include <vector>
#include <set>
#include <map>
#include <functional>
template <>
struct std::less<IMemberData*> {
bool operator()(const IMemberData* p1, const IMemberData* p2) const {
IMemberData *m1 = (IMemberData*) p1;
IMemberData *m2 = (IMemberData*) p2;
short nCompare = 0;
if (m1->CanCompareObjects() == S_OK)
m1->CompareObject(m2, &nCompare);
else if (m1->CanCompareKeys() == S_OK) {
CComBSTR sKey;
m2->get_Key(&sKey);
m1->CompareKey(sKey, &nCompare);
}
else if (p1 < p2)
nCompare = -1;
return nCompare < 0;
}
};
template <>
struct std::equal_to<IMemberData*> {
bool operator()(const IMemberData* p1, const IMemberData* p2) const {
IMemberData *m1 = (IMemberData*) p1;
IMemberData *m2 = (IMemberData*) p2;
short nCompare = -1;
if (m1->CanCompareObjects() == S_OK)
m1->CompareObject(m2, &nCompare);
else if (m1->CanCompareKeys() == S_OK) {
CComBSTR sKey;
m2->get_Key(&sKey);
m1->CompareKey(sKey, &nCompare);
}
else if (p1 == p2)
nCompare = 0;
return nCompare == 0;
}
};
// The following class implements a find() method for the STL collection types
// std::vector<IMemberData*>
// std::list<IMemberData*>
// std::set<IMemberData*>
// std::map<CComBSTR, IMemberData*>
template <typename STLContainer>
class CSTLContainer : public STLContainer {
public:
typedef STLContainer CollType;
typedef typename STLContainer::iterator iterator;
public:
CSTLContainer() { }
virtual ~CSTLContainer() {
for (iterator it = begin(); it != end(); ++it)
Reference(it)->Release();
}
IMemberData* Reference(iterator it) { return *it; }
iterator find(VARIANT Index);
bool insert(VARIANT Index, IMemberData *pData);
bool append(IMemberData *pData);
bool erase(VARIANT Index) {
// This one will work the same for all collection types
iterator it = find(Index);
if (it != end()) {
Reference(it)->Release();
CollType::erase(it);
return true;
}
return false;
}
};
// implementations for vector<IMemberObject*> collections
template < >
inline std::vector<IMemberData*>::iterator CSTLContainer<std::vector<IMemberData*> >::find(VARIANT Index) {
iterator it;
CComVariant vIndex;
if (size() == 0)
return end();
if (Index.vt == VT_DISPATCH || Index.vt == VT_UNKNOWN) {
CComQIPtr<IMemberData> pMember;
if (Index.vt == VT_DISPATCH)
pMember = Index.pdispVal;
else
pMember = Index.punkVal;
if (pMember->CanCompareKeys() == S_FALSE) {
std::equal_to<IMemberData*> MemberEquals;
for (it = begin(); it != end(); ++it) {
if (MemberEquals(pMember, *it))
break;
}
}
else {
// Do this through a key comparison - it is a bit more efficient as we only need
// to get pMember's key once.
CComBSTR sKey;
pMember->get_Key(&sKey);
for (it = begin(); it != end(); ++it) {
short nCompare;
(*it)->CompareKey(sKey, &nCompare);
if (nCompare == 0)
break;
}
}
}
else if (Index.vt == VT_BSTR || Index.vt == (VT_BSTR | VT_BYREF) && (*begin())->CanCompareKeys() == S_OK) {
vIndex.ChangeType(VT_BSTR, &Index);
for (it = begin(); it != end(); ++it) {
short nCompare;
(*it)->CompareKey(vIndex.bstrVal, &nCompare);
if (nCompare == 0)
break;
}
}
else if (SUCCEEDED(vIndex.ChangeType(VT_I4, &Index))) {
if (vIndex.lVal > 0 && vIndex.lVal <= (long)size()) {
it = begin() + (vIndex.lVal - 1);
}
else // Invalid index
it = end();
}
else // Invalid index
it = end();
return it;
}
template < >
inline bool CSTLContainer<std::vector<IMemberData*> >::insert(VARIANT IndexBefore, IMemberData* pMember) {
bool bSuccess = false;
try {
iterator it = CollType::insert(find(IndexBefore), pMember);
if (it != end()) {
pMember->AddRef();
bSuccess = true;
}
else
bSuccess = append(pMember);
}
catch(...) { }
return bSuccess;
}
template <>
inline bool CSTLContainer<std::vector<IMemberData*> >::append(IMemberData *pMember) {
bool bSuccess = false;
try {
CollType::push_back(pMember);
pMember->AddRef();
bSuccess = true;
}
catch(...) { }
return bSuccess;
}
// list<IMemberData*> implementations
template < >
inline std::list<IMemberData*>::iterator CSTLContainer<std::list<IMemberData*> >::find(VARIANT Index) {
iterator it;
CComVariant vIndex;
if (size() == 0)
return end();
if (Index.vt == VT_DISPATCH || Index.vt == VT_UNKNOWN) {
CComQIPtr<IMemberData> pMember;
if (Index.vt == VT_DISPATCH)
pMember = Index.pdispVal;
else
pMember = Index.punkVal;
if (pMember->CanCompareKeys() == S_FALSE) {
std::equal_to<IMemberData*> MemberEquals;
for (it = begin(); it != end(); ++it) {
if (MemberEquals(pMember, *it))
break;
}
}
else {
// Do this through a key comparison - it is a bit more efficient as we only need
// to get pMember's key once.
CComBSTR sKey;
pMember->get_Key(&sKey);
for (it = begin(); it != end(); ++it) {
short nCompare;
(*it)->CompareKey(sKey, &nCompare);
if (nCompare == 0)
break;
}
}
}
else if (Index.vt == VT_BSTR || Index.vt == (VT_BSTR | VT_BYREF) && (*begin())->CanCompareKeys() == S_OK) {
vIndex.ChangeType(VT_BSTR, &Index);
for (it = begin(); it != end(); ++it) {
short nCompare;
(*it)->CompareKey(vIndex.bstrVal, &nCompare);
if (nCompare == 0)
break;
}
}
else if (SUCCEEDED(vIndex.ChangeType(VT_I4, &Index))) {
it = begin();
while (it != end() && --vIndex.lVal > 0)
++it;
}
else // Invalid index
it = end();
return it;
}
template < >
inline bool CSTLContainer<std::list<IMemberData*> >::insert(VARIANT IndexBefore, IMemberData* pMember) {
bool bSuccess = false;
try {
iterator it = CollType::insert(find(IndexBefore), pMember);
if (it != end()) {
pMember->AddRef();
bSuccess = true;
}
else
bSuccess = append(pMember);
}
catch(...) { }
return bSuccess;
}
template < >
inline bool CSTLContainer<std::list<IMemberData*> >::append(IMemberData *pMember) {
bool bSuccess = false;
try {
CollType::push_back(pMember);
pMember->AddRef();
bSuccess = true;
}
catch(...) { }
return bSuccess;
}
// set<IMemberData*> implementations.
// NOTE: Any collection saving its data in a set should implement the IMemberData::CompareObjects
// interface on the member objects. Otherwise, the <set> just compares interface pointer values.
template < >
inline std::set<IMemberData*>::iterator CSTLContainer<std::set<IMemberData*> >::find(VARIANT Index) {
iterator it;
CComVariant vIndex;
if (size() == 0)
return end();
if (Index.vt == VT_DISPATCH || Index.vt == VT_UNKNOWN) {
CComQIPtr<IMemberData> pMember;
if (Index.vt == VT_DISPATCH)
pMember = Index.pdispVal;
else
pMember = Index.punkVal;
return CollType::find(pMember);
}
else if (Index.vt == VT_BSTR || Index.vt == (VT_BSTR | VT_BYREF) && (*begin())->CanCompareKeys() == S_OK) {
vIndex.ChangeType(VT_BSTR, &Index);
for (it = begin(); it != end(); ++it) {
short nCompare;
(*it)->CompareKey(vIndex.bstrVal, &nCompare);
if (nCompare == 0)
break;
}
}
else if (SUCCEEDED(vIndex.ChangeType(VT_I4, &Index))) {
it = begin();
while (it != end() && --vIndex.lVal > 0)
++it;
}
else // Invalid index
it = end();
return it;
}
// The insert function ignores the key value, as sets are stored in key order regardless
template < >
inline bool CSTLContainer<std::set<IMemberData*> >::insert(VARIANT, IMemberData* pMember) {
return append(pMember);
}
template < >
inline bool CSTLContainer<std::set<IMemberData*> >::append(IMemberData *pMember) {
bool bSuccess = false;
try {
if (CollType::insert(pMember).second) {
pMember->AddRef();
bSuccess = true;
}
}
catch (...) { }
return bSuccess;
}
// map<CComBSTR, IMemberData*> Implementation
template < >
inline IMemberData* CSTLContainer<std::map<CComBSTR, IMemberData*> >::Reference(std::map<CComBSTR, IMemberData*>::iterator it) {
return it->second;
}
template < >
inline std::map<CComBSTR, IMemberData*>::iterator CSTLContainer<std::map<CComBSTR, IMemberData*> >::find(VARIANT Index) {
iterator it;
CComVariant vIndex;
if (size() == 0)
return end();
if (Index.vt == VT_DISPATCH || Index.vt == VT_UNKNOWN) {
CComQIPtr<IMemberData> pMember;
if (Index.vt == VT_DISPATCH)
pMember = Index.pdispVal;
else
pMember = Index.punkVal;
if (pMember->CanCompareKeys() == S_OK) {
CComBSTR sKey;
pMember->get_Key(&sKey);
return CollType::find(sKey);
}
}
if (Index.vt != VT_BSTR && Index.vt != (VT_BSTR | VT_BYREF) && SUCCEEDED(vIndex.ChangeType(VT_I4, &Index))) {
it = begin();
while (it != end() && --vIndex.lVal > 0)
++it;
}
else if (SUCCEEDED(vIndex.ChangeType(VT_BSTR, &Index))) {
CComBSTR sKey = vIndex.bstrVal;
return CollType::find(sKey);
}
else // Invalid index
it = end();
return it;
}
// The insert function assumes that the IndexBefore parameter is the new key value.
template < >
inline bool CSTLContainer<std::map<CComBSTR, IMemberData*> >::insert(VARIANT IndexBefore, IMemberData *pMember) {
bool bSuccess = false;
CComVariant vIndex;
try {
if (SUCCEEDED(vIndex.ChangeType(VT_BSTR, &IndexBefore)))
if (CollType::insert(CollType::value_type(vIndex.bstrVal, pMember)).second) {
pMember->AddRef();
bSuccess = true;
}
}
catch(...) { }
return bSuccess;
}
// The append method only works if the get_Key method if the IMemberData object is implemented
template < >
inline bool CSTLContainer<std::map<CComBSTR, IMemberData*> >::append(IMemberData *pMember) {
CComBSTR sKey;
bool bSuccess = false;
try {
if (SUCCEEDED(pMember->get_Key(&sKey)))
if (CollType::insert(CollType::value_type(sKey, pMember)).second) {
pMember->AddRef();
bSuccess = true;
}
}
catch (...) { }
return bSuccess;
}
// This is the actual collection object implementation.
// It will work for collections of any objects derrived from IMemberData.
// The collection must object itself should inherit from this class, which implements the
// IContainer interface. Note that there are also implementations for the standard collection interfaces,
// but they have not been included in the IContainer interface, so that the
// user can expose only those methods they wish to.
template <typename IExposed, typename STLContainerType = std::vector<IMemberData*> >
// IExposed is the interface of the exposed objects contained by the collection
// Your Exposed class should be derrived from CMember, or at least implement the IMember interface
// The STLContainer type defines the container you use store the data objects.
// The default is a vector, which allows for fast random access based on an offset
// Implementations have been allowed for list, set, and map (indexed on BSTR) also.
// The container contents should all implement the IMemberData interface, which knows
// how to create an object that implements IExposed.
class CContainer : public IContainer {
public:
// useful typedefs
typedef IExposed Exposed;
typedef CSTLContainer<STLContainerType> CollType;
typedef typename STLContainerType::iterator iterator;
typedef CContainer<IExposed, STLContainerType> Container;
public:
// Construction and Destruction
CContainer() { }
public:
// The collection itself
CollType m_coll;
public:
// An Enumerator class for the collection
class CEnumerator : public IEnumVARIANT {
private:
ULONG m_uRef;
public:
CEnumerator(CContainer *pContainer) : m_pParent(pContainer), m_uRef(0) { m_pParent->AddRef(); m_it = m_pParent->m_coll.begin(); }
~CEnumerator() { m_pParent->Release(); }
CContainer* m_pParent;
iterator m_it;
public:
// IUnknown Implementation
virtual ULONG STDMETHODCALLTYPE AddRef() { return ++m_uRef; }
virtual ULONG STDMETHODCALLTYPE Release() {
if (--m_uRef)
return m_uRef;
delete this;
return 0;
}
STDMETHOD (QueryInterface)(REFIID iid, void **ppVal) {
if (IsEqualIID(iid, IID_IUnknown))
*ppVal = (IUnknown*)this;
else if (IsEqualIID(iid, IID_IEnumVARIANT))
*ppVal = (IEnumVARIANT*)this;
else {
*ppVal = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
// IEnumVARIANT Implementation
STDMETHOD (Clone)(IEnumVARIANT FAR* FAR* ppEnum) {
CEnumerator *pEnum = new CEnumerator(m_pParent);
if (pEnum) {
pEnum->AddRef();
pEnum->m_it = m_it;
*ppEnum = (IEnumVARIANT*)pEnum;
return S_OK;
}
return E_OUTOFMEMORY;
}
STDMETHOD (Next)(ULONG celt, VARIANT FAR* rgVar, ULONG FAR* pFetched) {
ULONG nFetch = 0;
while(nFetch < celt && m_it != m_pParent->m_coll.end()) {
CComPtr<IMemberData> pData = m_pParent->m_coll.Reference(m_it);
CComPtr<IMember> pMember;
HRESULT hr = pData->CreateObject(&pMember);
if (FAILED(hr))
return hr;
CComPtr<IContainer> pContainer;
m_pParent->QueryInterface(&pContainer);
pMember->put_Parent(pContainer);
CComQIPtr<IDispatch> pDisp = pMember;
rgVar->vt = VT_DISPATCH;
rgVar->pdispVal = pDisp.Detach();
rgVar++;
m_it++;
nFetch++;
}
if (pFetched)
*pFetched = nFetch;
return nFetch == celt ? S_OK : S_FALSE;
}
STDMETHOD (Reset)() { m_it = m_pParent->m_coll.begin(); return S_OK; }
STDMETHOD (Skip)(ULONG celt) {
while (celt && m_it != m_pParent->m_coll.end()) {
celt--;
m_it++;
}
return celt ? S_FALSE : S_OK;
}
};
public:
// helpers
iterator find(VARIANT Index) { return m_coll.find(Index); }
long size() { return (long)m_coll.size(); }
iterator begin() { return m_coll.begin(); }
iterator end() { return m_coll.end(); }
public:
STDMETHOD (Add)(IExposed* pObject) {
CComQIPtr<IMember> pMember = pObject; // Your member objects sjhould also implement IMember interface
if (!pMember)
return E_INVALIDARG;
CComPtr<IMemberData> pData;
HRESULT hr = pMember->get_Data(&pData);
if (FAILED(hr))
return hr;
if (!pData) // Data object not initialised in the member object
return E_FAIL;
if (m_coll.append(pData)) {
CComPtr<IContainer> pContainer;
QueryInterface(&pContainer);
pMember->put_Parent(pContainer);
return S_OK;
}
return E_FAIL;
}
STDMETHOD (Add)(VARIANT Index, IExposed *pObject) {
CComQIPtr<IMember> pMember = pObject; // Your member objects should also implement IMember interface
if (!pMember)
return E_INVALIDARG;
CComPtr<IMemberData> pData;
HRESULT hr = pMember->get_Data(&pData);
if (FAILED(hr))
return hr;
if (!pData) // Data object not initialised in the member object
return E_FAIL;
if (m_coll.insert(Index, pData)) {
CComPtr<IContainer> pContainer;
QueryInterface(&pContainer);
pMember->put_Parent(pContainer);
return S_OK;
}
return E_FAIL;
}
STDMETHOD (Remove)(VARIANT Index) {
return m_coll.erase(Index) ? S_OK : E_INVALIDARG;
}
STDMETHOD (get_Item)(VARIANT Index, IMember **pMember) {
iterator it = m_coll.find(Index);
if (it == m_coll.end())
return E_INVALIDARG;
CComQIPtr<IMemberData> pData = m_coll.Reference(it);
if (!pData) // Collection should only contain pointers IMemberData objects.
return E_UNEXPECTED;
HRESULT hr = pData->CreateObject(pMember);
if (SUCCEEDED(hr) && pMember) {
CComPtr<IContainer> pContainer;
QueryInterface(&pContainer);
hr = (*pMember)->put_Parent(pContainer);
}
return hr;
}
STDMETHOD (get_Count)(long *pCount) {
*pCount = (long)m_coll.size();
return S_OK;
}
STDMETHOD (get__NewEnum)(IUnknown **ppUnk) {
CEnumerator *pEnum = new CEnumerator(this);
if (pEnum) {
pEnum->AddRef();
*ppUnk = (IUnknown*)pEnum;
return S_OK;
}
return E_OUTOFMEMORY;
}
};
// This class implements the required methods for your MemberData objects
// Implementation of the CompareObject, CompareKey, and get_Key methods is optional
// but should be included in the following cases:
// 1. If your collection is based on a set, the the ComapreObject method should be
// implemented, and the CanCompareObject method should be overwritten to return true
// 2. If your collection is based on a map with a BSTR index, then you can implement the
// get_Key and CompareKey methods to allow automatic retrieval of the index value.
// 3. You need to implement the get_Key and the CompareKey methods if you want the user
// access your collection by a string, unless your collection is based on map<CComBSTR, IMemberData*>
//
// The CanCompareObjects and CanCompareKeys methods should be overwritten to return S_OK
// if the CompareObjects and CompareKeys methods are implemented. By default, these methods return S_FALSE
//
// This class implements a default method for CreateObject, which returns an interface pointer
// to an IMember object. Your derrived class may wish to implement an alternative CreateObject,
// which returns an interface to your class derived from IMember. The easy way to do this
// would be something like :
//
// STDMETHODIMP CMyMemberData::CreateObject(IMyMemberInterface **ppMyMember) {
// CComPtr<IMember> pMember;
// HRESULT hr = CMemberData::CreateObject(&pMember)
// if (SUCCEEDED(hr))
// hr = pMember->QueryInterface(ppMyMember);
// return hr;
// }
template <typename CMemberClass>
// The template parameter here is your Member class, which implements IMember, and is inherited from CComObjectRoot.
// This implementation uses CComObject<CMemberClass>::CreateInstance in order to create the instance
// of the Member object. Note at this time, it has not yet been added to a collection,
// so the parent member of your member will still be NULL
class CMemberData: public IMemberData {
public:
typedef CMemberClass MemberCoclass;
STDMETHOD (CreateObject)(IMember **ppObject) {
CComObject<CMemberClass> *pNewObject = NULL;
HRESULT hr = CComObject<CMemberClass>::CreateInstance(&pNewObject);
if (SUCCEEDED(hr)) {
if (FAILED(hr = pNewObject->QueryInterface(IID_IMember, (void**)ppObject)))
delete pNewObject;
}
pNewObject->put_Data((IMemberData*)this);
return hr;
}
// These following methods are implemented in this class, simply returning a not implemented
// HRESULT. Implementing these is not to difficult, however. If you return S_OK from
STDMETHOD (CanCompareObjects)() { return S_FALSE; }
STDMETHOD (CanCompareKeys)() { return S_FALSE; }
STDMETHOD (get_Key)(BSTR *pKey) { return E_NOTIMPL; }
STDMETHOD (CompareKey)(BSTR sKey, short *pCompare) {return E_NOTIMPL; }
STDMETHOD (CompareObject)(IMemberData *pData, short *pCompare) { return E_NOTIMPL; }
};
// This last class is what you inherit them member objects from. It implements the IMember interface, to
// control bot the data and parent (collection) reference counting.
template <class CMemberDataClass, typename IMemberDataInterface>
// The 1st template object is the your class inherited from CMemberData. This allows the inital construction
// to create an empty object on creation (so that objects can be created outside of the collection,
// and later added to the collection. The second template member is the interface implemented
// by the data object, which often would be the same as the interface exposed by the Member
// object. This allows simple transfer of data from the exposed object to the data object.
// eg: MyMember::get_Property(*pValue) { return m_pData->get_Property(pValue); }
class CMember : public IMember {
public:
// The References to the data object and the Collection (parent) object
CComPtr<IMemberDataInterface> m_pData;
CComPtr<IContainer> m_pParent;
public:
// The constructor creates a new, empty data object for use when this member is
// object is being created from new. If the object is being created FROM a data object,
// then this new data object will be replaced.
CMember() {
CComObject<CMemberDataClass> *pData = NULL;
if (SUCCEEDED(CComObject<CMemberDataClass>::CreateInstance(&pData))) {
pData->QueryInterface(&m_pData);
}
}
// Attaches the data to object to the member
STDMETHOD (put_Data)(IMemberData *pData) {
if (m_pData)
m_pData.Release();
if (pData)
return pData->QueryInterface(&m_pData);
// Set to NULL
return S_OK;
}
STDMETHOD (get_Data)(IMemberData **ppData) {
if (m_pData)
return m_pData.QueryInterface(ppData);
return E_NOINTERFACE;
}
STDMETHOD (put_Parent)(IContainer *pContainer) {
// There can be only one
if (m_pParent)
m_pParent.Release();
m_pParent = pContainer;
return S_OK;
}
STDMETHOD (get_Parent)(IContainer **ppContainer) {
if ((*ppContainer = m_pParent.p) != NULL)
(*ppContainer)->AddRef();
return S_OK;
}
};