Implementing CEnumDebugBoundBreakpoints

The COM specification suggests that an enumeration support several specific methods: Next, Reset, Clone, and Skip. The Debugging SDK requires enumerations to support not only the suggested methods but an additional method, GetCount. The ATL template class, CComEnum, supports only the basic four methods, so it is necessary to create a new template class with the additional GetCount method. This new template class, CComEnumWithCount, will be used for all enumeration classes we will need in the TextInterpreter project and will live in its own header file.

To implement CEnumDebugBoundBreakpoints

  1. In Solution Explorer, right-click the TextInterpreter project and click Add New item.

  2. Click the Code category on the left and Header File (.h) on the right.

  3. Type the NameTextInterpreterATL.h and click OK.

  4. In the new file, TextInterpreterATL.h, add the following:

    #pragma once
    
    // ----------------------------------------------------------------------------
    // CComEnumWithCountImpl
    
    template <class Base, const IID* piid, class T, class Copy>
    class ATL_NO_VTABLE CComEnumWithCountImpl : public Base
    {
    public:
        CComEnumWithCountImpl()
        {
            m_begin = m_end = m_iter = NULL;
            m_dwFlags = 0;
            m_pUnk = NULL;
        }
    
        ~CComEnumWithCountImpl()
        {
            if (m_dwFlags & BitOwn)
            {
                for (T* p = m_begin; p != m_end; p++)
                    Copy::destroy(p);
                delete [] m_begin;
            }
            if (m_pUnk)
                m_pUnk->Release();
        }
    
        STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched)
        {
            if (rgelt == NULL || (celt != 1 && pceltFetched == NULL))
                return E_POINTER;
            if (m_begin == NULL || m_end == NULL || m_iter == NULL)
                return E_FAIL;
            ULONG nRem = (ULONG)(m_end - m_iter);
            HRESULT hRes = S_OK;
            if (nRem < celt)
                hRes = S_FALSE;
            ULONG nMin = min(celt, nRem);
            if (pceltFetched != NULL)
                *pceltFetched = nMin;
            while(nMin--)
                Copy::copy(rgelt++, m_iter++);
            return hRes;
        }
    
        STDMETHOD(Skip)(ULONG celt)
        {
            m_iter += celt;
            if (m_iter < m_end)
                return S_OK;
            m_iter = m_end;
            return S_FALSE;
        }
    
        STDMETHOD(Reset)(void)
        {
            m_iter = m_begin;
            return S_OK;
        }
    
        STDMETHOD(Clone)(Base** ppEnum)
        {
            typedef CComObject<CComEnumWithCount<Base, piid, T, Copy> > _class;
            HRESULT hRes = E_POINTER;
            if (ppEnum != NULL)
            {
                _class* p = NULL;
                ATLTRY(p = new _class)
                if (p == NULL)
                {
                    *ppEnum = NULL;
                    hRes = E_OUTOFMEMORY;
                }
                else
                {
                    // If the data is a copy then we need to keep "this" object around
                    hRes = p->Init(m_begin, m_end, (m_dwFlags & BitCopy) ? this : m_pUnk);
                    if (FAILED(hRes))
                        delete p;
                    else
                    {
                        p->m_iter = m_iter;
                        hRes = p->_InternalQueryInterface(*piid, (void**)ppEnum);
                        if (FAILED(hRes))
                        delete p;
                    }
                }
            }
            return hRes;
        }
    
        STDMETHOD(GetCount)(ULONG* pcelt)
        {
            *pcelt = (ULONG)(m_end - m_begin);
            return S_OK;
        }
    
        HRESULT Init(T* begin, T* end, IUnknown* pUnk, CComEnumFlags flags = AtlFlagNoCopy)
        {
            if (flags == AtlFlagCopy)
            {
                _ASSERTE(m_begin == NULL); //Init called twice?
                ATLTRY(m_begin = new T[end-begin])
                m_iter = m_begin;
                if (m_begin == NULL)
                    return E_OUTOFMEMORY;
                for (T* i=begin; i != end; i++)
                {
                    Copy::init(m_iter);
                    Copy::copy(m_iter++, i);
                }
                m_end = m_begin + (end-begin);
            }
            else
            {
                m_begin = begin;
                m_end = end;
            }
            m_pUnk = pUnk;
            if (m_pUnk)
                m_pUnk->AddRef();
            m_iter = m_begin;
            m_dwFlags = flags;
            return S_OK;
        }
    
    public:
        IUnknown* m_pUnk;
        T* m_begin;
        T* m_end;
        T* m_iter;
        DWORD m_dwFlags;
    protected:
        enum FlagBits { BitCopy = 1, BitOwn = 2 };
    };
    
    template <class Base, const IID* piid, class T, class Copy, class ThreadModel = CComObjectThreadModel>
    class ATL_NO_VTABLE CComEnumWithCount :
        public CComEnumWithCountImpl<Base, piid, T, Copy>,
        public CComObjectRootEx< ThreadModel >
    {
    public:
        typedef CComEnumWithCount<Base, piid, T, Copy > _CComEnum;
        typedef CComEnumWithCountImpl<Base, piid, T, Copy > _CComEnumBase;
        BEGIN_COM_MAP(_CComEnum)
            COM_INTERFACE_ENTRY_IID(*piid, _CComEnumBase)
        END_COM_MAP()
    };
    
  5. Open the stdafx.h file and add the following bold line to the end of the file. This makes the template class just defined available throughout the project.

    #define WM_CONTINUE  WM_USER + 0x9A
    
    #include "TextInterpreterATL.h" 
    
  6. Open the Breakpoint.h file and add the following bold lines after the last #include. This uses the new template class to declare a specific type of enumeration supporting the IEnumDebugBoundBreakpoints2 interface.

    #include "TextInterpreter.h"
    
    typedef CComEnumWithCount< 
        IEnumDebugBoundBreakpoints2, 
        &IID_IEnumDebugBoundBreakpoints2, 
        IDebugBoundBreakpoint2*, 
        _CopyInterface<IDebugBoundBreakpoint2> 
    > CEnumDebugBoundBreakpoints; 
    
  7. Build the project to make sure there are no errors.

See Also

Concepts

Implementing IDebugPendingBreakpoint2::Bind