OK, I'm finally getting our organization to upgrade from v6, and the first
project I try to recompile I run into hairy problems. We've used this ATL COM
collection class for years. It looks like maybe I could write a simpler one
with newer features in ATL, but this one works fine if only it would compile.
Any suggestions?
COMVectorCollection.h:
#include <vector>
////////////////////////////////////////////////////////////////////////
/////
// CComCollect
//
// icontainer: The interface for the container
// container_iid: The IID for the container interface
// libid: The GUID for the library
// container: The implementation class for the container
// icontained: The interface for the contained interface
// contained_iid: The IID for the contained interface
//
// Usage:
// Derive your collection class from this and CComObjectRootEx. Since this
template class
// inherits from CComCoClass, IDispatchImpl, and ISupportErrorInfo, you
should not inherit
// from those in your concrete class. Here's an example declaration:
//
// class ATL_NO_VTABLE CSomeCollection :
// public CComObjectRootEx<CComSingleThreadModel>,
// public CComCollect<ISomeCollection, &IID_ISomeCollection,&LIBID_SomeLib,
CSomeItem,
// &CLSID_SomeCollection, ISomeInterface,
&IID_ISomeInterface>
//
// If all you need are the standard Add, Remove, Item, get_Count, and
get__NewEnum methods,
// then you do not need to provide any other implementation. Just make sure
you declare
// the interface for your collection class in an IDL file. Here's the
example for the
// above declaration:
//
// interface ISomeCollection : IDispatch
// {
// [propget, id(1)] HRESULT Count([out, retval] long *pVal);
// [id(2)] HRESULT Add([in] ISPCChart* inItem);
// [id(3)] HRESULT Remove([in] long inIndex);
// [propget, id(DISPID_VALUE)] HRESULT Item([in] long inIndex,
// [out, retval] ISPCChart**
outChart);
// [propget, id(DISPID_NEWENUM)] HRESULT _NewEnum([out,
retval]LPUNKNOWN *pVal);
// };
//
// You are free to add additional accessor/builder methods (note that the
collection
// implementation is exposed. Just declare it in your header and the IDL
and provide an
// implementation.
//
template <class icontainer, const IID* container_iid, const GUID* libid,
class container,
const CLSID* container_clsid, class icontained, const IID*
contained_iid>
class ATL_NO_VTABLE CComCollect :
public CComCoClass<container, container_clsid>,
public IDispatchImpl<icontainer, container_iid, libid>,
public ISupportErrorInfo
{
public:
CComCollect() {}
// ISPCChartSpecs
public:
STDMETHOD(RemoveAll)();
STDMETHOD(Add)(/*[in]*/ icontained* inItem);
STDMETHOD(get__NewEnum)(/*[out, retval]*/ LPUNKNOWN *pVal);
STDMETHOD(get_Item)(/*[in]*/ long inIndex, /*[out, retval]*/
icontained** outChart);
STDMETHOD(get_Count)(/*[out, retval]*/ long *pVal);
STDMETHOD(Remove)(/*[in]*/ long inIndex);
// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
// Helper functions
typedef std::vector<CComVariant> collection;
typedef collection::iterator iterator;
iterator begin();
iterator end();
protected:
collection mCollect;
bool mDirty;
};
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained, const IID*
contained_iid>
STDMETHODIMP CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained, contained_iid>
::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] = { container_iid, };
for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained,
const IID* contained_iid>
typename CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained, contained_iid>::iterator
typename CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained, contained_iid>::begin()
{
return mCollect.begin();
}
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained, const IID*
contained_iid>
typename CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained, contained_iid>::iterator
typename CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained, contained_iid>::end()
{
return mCollect.end();
}
// Returns the number of elements in the collection
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained, const IID*
contained_iid>
STDMETHODIMP CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained,
contained_iid>::get_Count(long * pVal)
{
// Make sure we didn't get a null pointer passed in
if (!pVal)
return E_POINTER;
// Get the size from the collection
*pVal = mCollect.size();
return S_OK;
}
// Returns a single item corresponding to the index
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained, const IID*
contained_iid>
STDMETHODIMP CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained,
contained_iid>::get_Item(long inIndex, icontained** outContained)
{
// Make sure we didn't get a null pointer passed in
if (!outContained)
return E_POINTER;
// Make sure the index is within range (index is 1-based)
if (inIndex < 1 || inIndex > mCollect.size())
return E_INVALIDARG;
// Get the variant out of the collection
CComVariant& var = mCollect[inIndex-1];
// Make sure we've got an element
HRESULT hr = var.pdispVal->QueryInterface(*contained_iid,
(void**)outContained);
if (FAILED(hr))
return E_UNEXPECTED;
return S_OK;
}
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained, const IID*
contained_iid>
STDMETHODIMP CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained,
contained_iid>::get__NewEnum(LPUNKNOWN * pVal)
{
// Make sure we didn't get a null pointer passed in
if (!pVal)
return E_POINTER;
*pVal = 0;
// Create the enumeration object
typedef CComObject<CComEnum<IEnumVARIANT, &IID_IEnumVARIANT,
VARIANT, _Copy<VARIANT> > > enumVar;
enumVar* pEnum = new enumVar;
if (!pEnum)
return E_OUTOFMEMORY;
// Initialize with the chart collection iterators
HRESULT hr = pEnum->Init(mCollect.begin(), mCollect.end(),
NULL,AtlFlagCopy);
if (SUCCEEDED(hr))
hr = pEnum->QueryInterface(IID_IEnumVARIANT, (void**)pVal);
if (FAILED(hr))
delete pEnum;
return hr;
}
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained, const IID*
contained_iid>
STDMETHODIMP CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained,
contained_iid>::Add(icontained* inItem)
{
HRESULT hr = S_OK;
icontained* pContained;
// Make sure we've got an element
hr = inItem->QueryInterface(*contained_iid, (void **)&pContained);
if (FAILED(hr))
return E_UNEXPECTED;
// Add the variant (dispatch) to the collection
mDirty = TRUE;
mCollect.push_back(CComVariant(inItem));
pContained->Release();
return hr;
}
// Removes an item at an integer index
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained, const IID*
contained_iid>
STDMETHODIMP CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained,
contained_iid>::Remove(long inIndex)
{
HRESULT hr = S_OK;
// Make sure the index is within range (index is 1-based)
if (inIndex < 1 || inIndex > mCollect.size())
return E_INVALIDARG;
mDirty = TRUE;
mCollect.erase(mCollect.begin() + inIndex - 1);
return hr;
}
/////////////////////////////////////////////////////////////
//
// Remove all elements from the collection
//
template <class icontainer, const IID* container_iid, const GUID* libid,
class container, const CLSID* container_clsid, class icontained, const IID*
contained_iid>
STDMETHODIMP CComCollect<icontainer, container_iid, libid, container,
container_clsid, icontained,
contained_iid>::RemoveAll()
{
mCollect.erase(mCollect.begin(), mCollect.end());
return S_OK;
}