// This is a part of the Active Template Library.
// Copyright (C) Microsoft Corporation, 1996 - 1999
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATL_SNAPIN_H__
#define __ATL_SNAPIN_H__

#include <mmc.h>
#include <commctrl.h>
#pragma comment(lib, "mmc.lib")
#pragma comment(lib, "comctl32.lib")

// MAM things which haven't made it into real atlsnap.h yet.
#define TASKPAD_SUPPORT
#define FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES
#define FIX_PROBLEM_WITH_DYNAMIC_CAST_ON_CSNAPINOBJECTROOT_NOT_WORKING
#define FIX_PROBLEM_WITH_GET_RESULT_VIEW_TYPE_NULL_COOKIE

template <class T, bool bAutoDelete = TRUE>
class ATL_NO_VTABLE CSnapInPropertyPageImpl : public CDialogImplBase
{
public:
   PROPSHEETPAGE m_psp;


#ifdef FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES
   LONG_PTR m_hNotificationHandle;
   BOOL        m_bOwnsNotificationHandle;
#endif FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES


   operator PROPSHEETPAGE*() { return &m_psp; }

// Construction
#ifdef FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES
   CSnapInPropertyPageImpl(LONG_PTR hNotificationHandle, LPCTSTR lpszTitle = NULL, BOOL bOwnsNotificationHandle = FALSE)
#else
   CSnapInPropertyPageImpl(LPCTSTR lpszTitle = NULL)
#endif // FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES
   {
      // initialize PROPSHEETPAGE struct
      memset(&m_psp, 0, sizeof(PROPSHEETPAGE));
      m_psp.dwSize = sizeof(PROPSHEETPAGE);
      m_psp.dwFlags = PSP_USECALLBACK;
      m_psp.hInstance = _Module.GetResourceInstance();
      m_psp.pszTemplate = MAKEINTRESOURCE(T::IDD);
      m_psp.pfnDlgProc = (DLGPROC)T::StartDialogProc;
      m_psp.pfnCallback = T::PropPageCallback;
      m_psp.lParam = (LPARAM)this;

      if(lpszTitle != NULL)
      {
         m_psp.pszTitle = lpszTitle;
         m_psp.dwFlags |= PSP_USETITLE;
      }

#ifdef FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES
      m_hNotificationHandle = hNotificationHandle;
      m_bOwnsNotificationHandle = bOwnsNotificationHandle;
#endif //FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES


   }


   void  SetTitles(LPCWSTR szTitle, LPCWSTR szSubtitle)
   {
      m_psp.dwFlags |= PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;

      m_psp.pszHeaderTitle = szTitle;
      m_psp.pszHeaderSubTitle = szSubtitle;
   };


#ifdef FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES
   ~CSnapInPropertyPageImpl()
   {
      if( m_bOwnsNotificationHandle )
      {
         // The MMC docs specify that this handle should only be freed once.
         // For this reason, we have the bOwnsNotificationHandle flag which
         // should only be set for one property page.
         // We only free the handle in the destructor of the page
         // that has been designated as the "owner".
         MMCFreeNotifyHandle( m_hNotificationHandle );
      }

   }



   HRESULT PropertyChangeNotify( LPARAM param )
   {
      return MMCPropertyChangeNotify( m_hNotificationHandle, param );
   }
   

#endif //FIX_PROBLEM_WITH_PROPERTY_CHANGE_NOTIFICATION_HANDLES


   static UINT CALLBACK PropPageCallback(HWND hWnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
   {
      ATLASSERT(hWnd == NULL);
      if(uMsg == PSPCB_CREATE)
      {
         CDialogImplBase* pPage = (CDialogImplBase*)ppsp->lParam;
         _Module.AddCreateWndData(&pPage->m_thunk.cd, pPage);
      }
      if (bAutoDelete && uMsg == PSPCB_RELEASE)
      {
         T* pPage = (T*)ppsp->lParam;
         delete pPage;
      }

      return 1;
   }

   HPROPSHEETPAGE Create()
   {
      return ::CreatePropertySheetPage(&m_psp);
   }

   BOOL EndDialog(int)
   {
      // do nothing here, calling ::EndDialog will close the whole sheet
      ATLASSERT(FALSE);
      return FALSE;
   }

// Operations
   void CancelToClose()
   {
      ATLASSERT(::IsWindow(m_hWnd));
      ATLASSERT(GetParent() != NULL);

      ::SendMessage(GetParent(), PSM_CANCELTOCLOSE, 0, 0L);
   }
   void SetModified(BOOL bChanged = TRUE)
   {
      ATLASSERT(::IsWindow(m_hWnd));
      ATLASSERT(GetParent() != NULL);

      if(bChanged)
         ::SendMessage(GetParent(), PSM_CHANGED, (WPARAM)m_hWnd, 0L);
      else
         ::SendMessage(GetParent(), PSM_UNCHANGED, (WPARAM)m_hWnd, 0L);
   }
   LRESULT QuerySiblings(WPARAM wParam, LPARAM lParam)
   {
      ATLASSERT(::IsWindow(m_hWnd));
      ATLASSERT(GetParent() != NULL);

      return ::SendMessage(GetParent(), PSM_QUERYSIBLINGS, wParam, lParam);
   }

   BEGIN_MSG_MAP(CSnapInPropertyPageImpl< T >)
      MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
   END_MSG_MAP()

// Message handler
   LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
   {
      ATLASSERT(::IsWindow(m_hWnd));
      NMHDR* pNMHDR = (NMHDR*)lParam;

      // don't handle messages not from the page/sheet itself
      if(pNMHDR->hwndFrom != m_hWnd && pNMHDR->hwndFrom != ::GetParent(m_hWnd))
      {
         bHandled = FALSE;
         return 1;
      }

      T* pT = (T*)this;
      LRESULT lResult = 0;
      // handle default
      switch(pNMHDR->code)
      {
      case PSN_SETACTIVE:
         lResult = pT->OnSetActive() ? 0 : -1;
         break;
      case PSN_KILLACTIVE:
         lResult = !pT->OnKillActive();
         break;
      case PSN_APPLY:
         lResult = pT->OnApply() ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE;
         break;
      case PSN_RESET:
         pT->OnReset();
         break;
      case PSN_QUERYCANCEL:
         lResult = !pT->OnQueryCancel();
         break;
      case PSN_WIZNEXT:
         // changed, since the orginal one doesn't work when jump to a different page
         {
            LRESULT  l = pT->OnWizardNext(); 

            switch(l)
            {
            case  TRUE:
               lResult = 0;
               break;
            case  FALSE:
               lResult = -1;
               break;
            default:
               lResult = l;
               break;
            }
         }
         break;
      case PSN_WIZBACK:
         // changed, since the orginal one doesn't work when jump to a different page
         {
            LRESULT  l = pT->OnWizardBack(); 

            switch(l)
            {
            case  TRUE:
               lResult = 0;
               break;
            case  FALSE:
               lResult = -1;
               break;
            default:
               lResult = l;
               break;
            }
         }
         break;
      case PSN_WIZFINISH:
         lResult = !pT->OnWizardFinish();
         break;
      case PSN_HELP:
         lResult = pT->OnHelp();
         break;
      default:
         bHandled = FALSE; // not handled
      }

      return lResult;
   }

// Overridables
   BOOL OnSetActive()
   {
      return TRUE;
   }
   BOOL OnKillActive()
   {
      return TRUE;
   }
   BOOL OnApply()
   {
      return TRUE;
   }
   void OnReset()
   {
   }
   BOOL OnQueryCancel()
   {
      return TRUE;    // ok to cancel
   }
   BOOL OnWizardBack()
   {
      return TRUE;
   }
   BOOL OnWizardNext()
   {
      return TRUE;
   }
   BOOL OnWizardFinish()
   {
      return TRUE;
   }
   BOOL OnHelp()
   {
      return TRUE;
   }
};

#if _ATL_VER < 0x0300
// intended for small number of simple types or pointers
template <class TKey, class TVal>
class CSimpleMap
{
public:
   TKey* m_aKey;
   TVal* m_aVal;
   int m_nSize;

// Construction/destruction
   CSimpleMap() : m_aKey(NULL), m_aVal(NULL), m_nSize(0)
   { }

   ~CSimpleMap()
   {
      RemoveAll();
   }

// Operations
   int GetSize() const
   {
      return m_nSize;
   }
   BOOL Add(TKey key, TVal val)
   {
      TKey* pKey;
      pKey = (TKey*)realloc(m_aKey, (m_nSize + 1) * sizeof(TKey));
      if(pKey == NULL)
         return FALSE;
      m_aKey = pKey;
      TVal* pVal;
      pVal = (TVal*)realloc(m_aVal, (m_nSize + 1) * sizeof(TVal));
      if(pVal == NULL)
         return FALSE;
      m_aVal = pVal;
      m_nSize++;
      SetAtIndex(m_nSize - 1, key, val);
      return TRUE;
   }
   BOOL Remove(TKey key)
   {
      int nIndex = FindKey(key);
      if(nIndex == -1)
         return FALSE;
      if(nIndex != (m_nSize - 1))
      {
         memmove((void*)&m_aKey[nIndex], (void*)&m_aKey[nIndex + 1], (m_nSize - (nIndex + 1)) * sizeof(TKey));
         memmove((void*)&m_aVal[nIndex], (void*)&m_aVal[nIndex + 1], (m_nSize - (nIndex + 1)) * sizeof(TVal));
      }
      TKey* pKey;
      pKey = (TKey*)realloc(m_aKey, (m_nSize - 1) * sizeof(TKey));
      if(pKey != NULL || m_nSize == 1)
         m_aKey = pKey;
      TVal* pVal;
      pVal = (TVal*)realloc(m_aVal, (m_nSize - 1) * sizeof(TVal));
      if(pVal != NULL || m_nSize == 1)
         m_aVal = pVal;
      m_nSize--;
      return TRUE;
   }
   void RemoveAll()
   {
      if(m_nSize > 0)
      {
         free(m_aKey);
         free(m_aVal);
         m_aKey = NULL;
         m_aVal = NULL;
         m_nSize = 0;
      }
   }
   BOOL SetAt(TKey key, TVal val)
   {
      int nIndex = FindKey(key);
      if(nIndex == -1)
         return FALSE;
      SetAtIndex(nIndex, key, val);
      return TRUE;
   }
   TVal Lookup(TKey key) const
   {
      int nIndex = FindKey(key);
      if(nIndex == -1)
         return NULL;   // must be able to convert
      return GetValueAt(nIndex);
   }
   TKey ReverseLookup(TVal val) const
   {
      int nIndex = FindVal(val);
      if(nIndex == -1)
         return NULL;   // must be able to convert
      return GetKeyAt(nIndex);
   }
   TKey& GetKeyAt(int nIndex) const
   {
      ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
      return m_aKey[nIndex];
   }
   TVal& GetValueAt(int nIndex) const
   {
      ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
      return m_aVal[nIndex];
   }

// Implementation
   void SetAtIndex(int nIndex, TKey& key, TVal& val)
   {
      ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
      m_aKey[nIndex] = key;
      m_aVal[nIndex] = val;
   }
   int FindKey(TKey& key) const
   {
      for(int i = 0; i < m_nSize; i++)
      {
         if(m_aKey[i] == key)
            return i;
      }
      return -1;  // not found
   }
   int FindVal(TVal& val) const
   {
      for(int i = 0; i < m_nSize; i++)
      {
         if(m_aVal[i] == val)
            return i;
      }
      return -1;  // not found
   }
};
#endif


class CSnapInItem;
class CSnapInObjectRootBase;

class CObjectData
{
public:
   CSnapInItem* m_pItem;
   DATA_OBJECT_TYPES m_type;
};

class ATL_NO_VTABLE CSnapInItem 
{
public:
   CSnapInItem(unsigned int helpIndex = 0)
      :m_helpIndex(helpIndex)
   {
   }

   virtual ~CSnapInItem()
   {
   }
   STDMETHOD(Notify)(MMC_NOTIFY_TYPE event,
      LPARAM arg,
      LPARAM param,
      IComponentData* pComponentData,
      IComponent* pComponent,
      DATA_OBJECT_TYPES type) = 0;
    
   STDMETHOD(GetScopePaneInfo)(SCOPEDATAITEM  *pScopeDataItem) = 0;

   STDMETHOD(GetResultViewType)(LPOLESTR  *ppViewType,
      long  *pViewOptions) = 0;
    
   STDMETHOD(GetResultPaneInfo)(RESULTDATAITEM  *pResultDataItem) = 0;
    
   STDMETHOD(AddMenuItems)(LPCONTEXTMENUCALLBACK piCallback,
      long  *pInsertionAllowed,
      DATA_OBJECT_TYPES type) = 0;
    
   STDMETHOD(Command)(long lCommandID,    
      CSnapInObjectRootBase* pObj,     
      DATA_OBJECT_TYPES type) = 0;
    
   STDMETHOD(CreatePropertyPages)(LPPROPERTYSHEETCALLBACK lpProvider,
      LONG_PTR handle,
      IUnknown* pUnk,
      DATA_OBJECT_TYPES type) = 0;
    
   STDMETHOD(QueryPagesFor)(DATA_OBJECT_TYPES type) = 0;
    
   STDMETHOD(SetControlbar)(IControlbar  *pControlbar,
      IExtendControlbar  *pExtendControlbar,
      CSimpleMap<UINT, IUnknown*>* pToolbarMap) = 0;
    
   STDMETHOD(ControlbarNotify)(IControlbar  *pControlbar,
      IExtendControlbar  *pExtendControlbar,
      CSimpleMap<UINT, IUnknown*>* pToolbarMap,
      MMC_NOTIFY_TYPE event,
      LPARAM arg,
      LPARAM param,
      CSnapInObjectRootBase* pObj,
      DATA_OBJECT_TYPES type) = 0;
    
   STDMETHOD(GetScopeData)(SCOPEDATAITEM  * *pScopeDataItem) = 0;
    
   STDMETHOD(GetResultData)(RESULTDATAITEM  * *pResultDataItem) = 0;

   STDMETHOD(FillData)(CLIPFORMAT cf, 
      LPSTREAM pStream) = 0;

   virtual void InitDataClass(IDataObject* pDataObject, CSnapInItem* pDefault)
   {
      _ASSERTE(0 && "Override this function in derived class");
   }

   static HRESULT GetDataClass(IDataObject* pDataObj, CSnapInItem** ppItem, DATA_OBJECT_TYPES* pType)
   {
      if (ppItem == NULL)
         return E_POINTER;
      if (pType == NULL)
         return E_POINTER;

      *ppItem = NULL;
      *pType = CCT_UNINITIALIZED;

      STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL };
      FORMATETC formatetc = { m_CCF_SNAPIN_GETOBJECTDATA, 
         NULL, 
         DVASPECT_CONTENT, 
         -1, 
         TYMED_HGLOBAL 
      };

      stgmedium.hGlobal = GlobalAlloc(0, sizeof(CObjectData));
      if (stgmedium.hGlobal == NULL)
         return E_OUTOFMEMORY;

      HRESULT hr = pDataObj->GetDataHere(&formatetc, &stgmedium);
      if (SUCCEEDED(hr))
      {
         CObjectData* pData = (CObjectData*)stgmedium.hGlobal;
          *ppItem = pData->m_pItem;
          *pType = pData->m_type;
      }
      
      GlobalFree(stgmedium.hGlobal);
      
      return hr;
   }

#ifdef TASKPAD_SUPPORT
   STDMETHOD(TaskNotify)(
              IDataObject * pDataObject
            , VARIANT * pvarg
            , VARIANT * pvparam
            ) = 0;

   STDMETHOD(EnumTasks)(
              IDataObject * pDataObject
            , BSTR szTaskGroup
            , IEnumTASK** ppEnumTASK
            ) = 0;
#endif // TASKPAD_SUPPORT

   virtual HRESULT STDMETHODCALLTYPE GetDataObject(IDataObject** pDataObj, DATA_OBJECT_TYPES type) = 0;

   static void Init()
   {
      m_CCF_NODETYPE       = (CLIPFORMAT) RegisterClipboardFormat(_T("CCF_NODETYPE"));
      m_CCF_SZNODETYPE     = (CLIPFORMAT) RegisterClipboardFormat(_T("CCF_SZNODETYPE"));  
      m_CCF_DISPLAY_NAME      = (CLIPFORMAT) RegisterClipboardFormat(_T("CCF_DISPLAY_NAME")); 
      m_CCF_SNAPIN_CLASSID = (CLIPFORMAT) RegisterClipboardFormat(_T("CCF_SNAPIN_CLASSID"));
      m_CCF_SNAPIN_GETOBJECTDATA = (CLIPFORMAT) RegisterClipboardFormat(_T("CCF_GETOBJECTDATA"));
      m_CCF_MMC_MULTISELECT_DATAOBJECT = (CLIPFORMAT) RegisterClipboardFormat(_T("CCF_MMC_MULTISELECT_DATAOBJECT"));
   }

public:
   static CLIPFORMAT m_CCF_NODETYPE;
   static CLIPFORMAT m_CCF_SZNODETYPE;
   static CLIPFORMAT m_CCF_DISPLAY_NAME;
   static CLIPFORMAT m_CCF_SNAPIN_CLASSID;
   static CLIPFORMAT m_CCF_SNAPIN_GETOBJECTDATA;
   static CLIPFORMAT m_CCF_MMC_MULTISELECT_DATAOBJECT;

   // IAS: Added to store some information about which help file to use
   unsigned int m_helpIndex;
};

class CSnapInObjectRootBase
{
public:
   CComPtr <IControlbar> m_spControlbar;
   CSimpleMap <UINT, IUnknown*> m_toolbarMap;
   const int m_ntype;

   CSnapInObjectRootBase(int n = 0) : m_ntype(n)
   {
   }
   HRESULT GetDataClass(IDataObject* pDataObject, CSnapInItem** ppItem, DATA_OBJECT_TYPES* pType)
   {
      return CSnapInItem::GetDataClass(pDataObject, ppItem, pType);
   }

#ifdef FIX_PROBLEM_WITH_DYNAMIC_CAST_ON_CSNAPINOBJECTROOT_NOT_WORKING
   // Need at least one virtual method, or compiler does not generate a vtable
   virtual ~CSnapInObjectRootBase()
   {
   }
#endif FIX_PROBLEM_WITH_DYNAMIC_CAST_ON_CSNAPINOBJECTROOT_NOT_WORKING


};

template <int n, class ComponentData>
class CSnapInObjectRoot : public CSnapInObjectRootBase
{
public :
   CSnapInObjectRoot() : CSnapInObjectRootBase(n)
   {
      m_pComponentData = NULL;
   }
   ComponentData* m_pComponentData;
};

#define EXTENSION_SNAPIN_DATACLASS(dataClass) dataClass m_##dataClass;

#define BEGIN_EXTENSION_SNAPIN_NODEINFO_MAP(classname) \
   HRESULT GetDataClass(IDataObject* pDataObject, CSnapInItem** ppItem, DATA_OBJECT_TYPES* pType) \
   { \
      if (ppItem == NULL) \
         return E_POINTER; \
      if (pType == NULL) \
         return E_POINTER; \
\
      *ppItem = NULL; \
\
      *pType = CCT_UNINITIALIZED; \
\
      STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL }; \
      FORMATETC formatetc = { CSnapInItem::m_CCF_NODETYPE, \
         NULL, \
         DVASPECT_CONTENT, \
         -1, \
         TYMED_HGLOBAL \
      }; \
\
      stgmedium.hGlobal = GlobalAlloc(0, sizeof(GUID)); \
      if (stgmedium.hGlobal == NULL) \
         return E_OUTOFMEMORY; \
\
      HRESULT hr = pDataObject->GetDataHere(&formatetc, &stgmedium); \
      if (FAILED(hr)) \
      { \
         GlobalFree(stgmedium.hGlobal); \
         return hr; \
      } \
\
      GUID guid; \
      memcpy(&guid, stgmedium.hGlobal, sizeof(GUID)); \
\
      GlobalFree(stgmedium.hGlobal); \
      hr = S_OK;

#define EXTENSION_SNAPIN_NODEINFO_ENTRY(dataClass) \
      if (IsEqualGUID(guid, *(GUID*)m_##dataClass.GetNodeType())) \
      { \
         *ppItem = m_##dataClass.GetExtNodeObject(pDataObject, &m_##dataClass); \
         _ASSERTE(*ppItem != NULL); \
         (*ppItem)->InitDataClass(pDataObject, &m_##dataClass); \
         return hr; \
      }

#define END_EXTENSION_SNAPIN_NODEINFO_MAP() \
         return CSnapInItem::GetDataClass(pDataObject, ppItem, pType); \
   };

class ATL_NO_VTABLE CSnapInDataObjectImpl : public IDataObject,
   public CComObjectRoot
{
public:
   BEGIN_COM_MAP(CSnapInDataObjectImpl)
      COM_INTERFACE_ENTRY(IDataObject)
   END_COM_MAP()
   STDMETHOD(GetData)(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
   {
      ATLTRACENOTIMPL(_T("SnapInDataObjectImpl::GetData\n"));
   }

   STDMETHOD(GetDataHere)(FORMATETC* pformatetc, STGMEDIUM* pmedium)
   {
      ATLTRACE(_T("SnapInDataObjectImpl::GetDataHere\n"));
      if (pmedium == NULL)
         return E_POINTER;

      HRESULT hr = DV_E_TYMED;
      // Make sure the type medium is HGLOBAL
      if (pmedium->tymed == TYMED_HGLOBAL)
      {
         // Create the stream on the hGlobal passed in
         CComPtr<IStream> spStream;
         hr = CreateStreamOnHGlobal(pmedium->hGlobal, FALSE, &spStream);
         if (SUCCEEDED(hr))
            if (pformatetc->cfFormat == CSnapInItem::m_CCF_SNAPIN_GETOBJECTDATA)
            {
               hr = DV_E_CLIPFORMAT;
               ULONG uWritten;
               hr = spStream->Write(&m_objectData, sizeof(CObjectData), &uWritten);
            }
            else
               hr = m_objectData.m_pItem->FillData(pformatetc->cfFormat, spStream);
      }
      return hr;
   }

   STDMETHOD(QueryGetData)(FORMATETC* /* pformatetc */)
   {
      ATLTRACENOTIMPL(_T("SnapInDataObjectImpl::QueryGetData\n"));
   }
   STDMETHOD(GetCanonicalFormatEtc)(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */)
   {
      ATLTRACENOTIMPL(_T("SnapInDataObjectImpl::GetCanonicalFormatEtc\n"));
   }
   STDMETHOD(SetData)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */)
   {
      ATLTRACENOTIMPL(_T("SnapInDataObjectImpl::SetData\n"));
   }
   STDMETHOD(EnumFormatEtc)(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */)
   {
      ATLTRACENOTIMPL(_T("SnapInDataObjectImpl::EnumFormatEtc\n"));
   }
   STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink,
      DWORD *pdwConnection)
   {
      ATLTRACENOTIMPL(_T("SnapInDataObjectImpl::SetData\n"));
   }
   STDMETHOD(DUnadvise)(DWORD dwConnection)
   {
      ATLTRACENOTIMPL(_T("SnapInDataObjectImpl::SetDatan\n"));
   }
   STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise)
   {
      ATLTRACENOTIMPL(_T("SnapInDataObjectImpl::SetData\n"));
   }

   CObjectData m_objectData;
};


template <class T, class Component>
class ATL_NO_VTABLE IComponentDataImpl : public IComponentData 
{
public :
   IComponentDataImpl()
   {
      m_pNode = NULL;
   }

    STDMETHOD(Initialize)(LPUNKNOWN pUnknown)
   {
      ATLTRACE(_T("IComponentDataImpl::Initialize\n"));

      HRESULT hr = E_POINTER;

      ATLASSERT(pUnknown != NULL);
      if (pUnknown == NULL)
         ATLTRACE(_T("IComponentData::Initialize called with pUnknown == NULL\n"));
      else
      {
         hr = pUnknown->QueryInterface(IID_IConsole, (void**)&m_spConsole);
         if (FAILED(hr))
            ATLTRACE(_T("QI for IConsole failed\n"));
      }

      return hr;
   }

   STDMETHOD(CreateComponent)(LPCOMPONENT *ppComponent)
   {
      ATLTRACE(_T("IComponentDataImpl::CreateComponent\n"));

      HRESULT hr = E_POINTER;

      ATLASSERT(ppComponent != NULL);
      if (ppComponent == NULL)
         ATLTRACE(_T("IComponentData::CreateComponent called with ppComponent == NULL\n"));
      else
      {
         *ppComponent = NULL;
         
         CComObject< Component >* pComponent;
         hr = CComObject< Component >::CreateInstance(&pComponent);
         ATLASSERT(SUCCEEDED(hr));
         if (FAILED(hr))
            ATLTRACE(_T("IComponentData::CreateComponent : Could not create IComponent object\n"));
         else
         {
            pComponent->m_pComponentData = static_cast<T*>(this);
            hr = pComponent->QueryInterface(IID_IComponent, (void**)ppComponent);
         }
      }
      return hr;
   }

    
    STDMETHOD(Notify)( 
        LPDATAOBJECT lpDataObject,
        MMC_NOTIFY_TYPE event,
        LPARAM arg,
        LPARAM param)
   {
      ATLTRACE(_T("IComponentDataImpl::Notify\n"));

      HRESULT hr = E_POINTER;

      ATLASSERT(lpDataObject != NULL);
      if (lpDataObject == NULL)
         ATLTRACE(_T("IComponentData::Notify called with lpDataObject == NULL\n"));
      else
      {
         T* pT = static_cast<T*>(this);
         CSnapInItem* pItem;
         DATA_OBJECT_TYPES type;
         hr = pT->m_pComponentData->GetDataClass(lpDataObject, &pItem, &type);
         ATLASSERT(SUCCEEDED(hr));
         if (SUCCEEDED(hr))
            hr = pItem->Notify(event, arg, param, pT, NULL, type);
      }
      return hr;
   }

    STDMETHOD(Destroy)(void)
   {
      ATLTRACE(_T("IComponentDataImpl::Destroy\n"));

      T* pT = static_cast<T*>(this);
      if (pT->m_spControlbar != NULL)
      {
         int n = pT->m_toolbarMap.GetSize();
         for (int i = 0; i < n; i++)
         {
            IToolbar* pToolbar = (IToolbar*)pT->m_toolbarMap.GetValueAt(i);
             if (pToolbar != NULL)
             {
               pT->m_spControlbar->Detach(pToolbar);
               pToolbar->Release();
             }
         }
      }
      pT->m_toolbarMap.RemoveAll();

      m_spConsole.Release();
      return S_OK;
   }

    STDMETHOD(QueryDataObject)(MMC_COOKIE cookie,
        DATA_OBJECT_TYPES type,
        LPDATAOBJECT  *ppDataObject)
   {
      ATLTRACE(_T("IComponentDataImpl::QueryDataObject\n"));
      HRESULT hr = E_POINTER;
      
      ATLASSERT(ppDataObject != NULL);
      if (ppDataObject == NULL)
         ATLTRACE(_T("IComponentData::QueryDataObject called with ppDataObject == NULL\n"));
      else
      {
         *ppDataObject = NULL;
         
         CSnapInItem* pItem = (CSnapInItem*) cookie;
         if (cookie == NULL)
            pItem = m_pNode;

         hr = pItem->GetDataObject(ppDataObject, type);
      }
      return hr;
   }
    
    STDMETHOD(GetDisplayInfo)(SCOPEDATAITEM *pScopeDataItem)
   {
      ATLTRACE(_T("IComponentDataImpl::GetDisplayInfo\n"));


      HRESULT hr = E_POINTER;
      
      ATLASSERT(pScopeDataItem != NULL);
      if (pScopeDataItem == NULL)
         ATLTRACE(_T("IComponentData::GetDisplayInfo called with pScopeDataItem == NULL\n"));
      else
      {
         CSnapInItem* pItem= (CSnapInItem*) pScopeDataItem->lParam;
         if (pItem == NULL)
            pItem = m_pNode;

         hr = E_UNEXPECTED;
         if (pItem != NULL)
            hr = pItem->GetScopePaneInfo(pScopeDataItem);
      }
      return hr;
   }
    
    STDMETHOD(CompareObjects)(LPDATAOBJECT lpDataObjectA,
        LPDATAOBJECT lpDataObjectB)
   {
      ATLTRACENOTIMPL(_T("IComponentDataImpl::CompareObjects\n"));
    }

   CComPtr<IConsole> m_spConsole;
   CSnapInItem* m_pNode;
};


template <class T>
class ATL_NO_VTABLE IComponentImpl : public IComponent
{
public:
    STDMETHOD(Initialize)(LPCONSOLE lpConsole)
   {
      ATLTRACE(_T("IComponentImpl::Initialize\n"));

      HRESULT hr = E_POINTER;

      ATLASSERT(lpConsole != NULL);
      if (lpConsole == NULL)
         ATLTRACE(_T("lpConsole is NULL\n"));
      else
      {
         m_spConsole = lpConsole;
   
         CComPtr<IHeaderCtrl> spHeaderCtrl;
         hr = m_spConsole->QueryInterface(IID_IHeaderCtrl, (void**)&spHeaderCtrl);
         if (FAILED(hr))
            ATLTRACE(_T("QI for IHeaderCtrl failed\n"));
         else
         {
            hr = m_spConsole->SetHeader(spHeaderCtrl);
            if (FAILED(hr))
               ATLTRACE(_T("IConsole::SetHeader failed (HRESULT = %x)\n"), hr);
         }
      }
      return hr;
   }
    
   STDMETHOD(Notify)(LPDATAOBJECT lpDataObject,
        MMC_NOTIFY_TYPE event,
        LPARAM arg,
        LPARAM param);
    
    STDMETHOD(Destroy)(MMC_COOKIE cookie)
   {
      ATLTRACE(_T("IComponentImpl::Destroy\n"));

      T* pT = static_cast<T*>(this);

      if (pT->m_spControlbar != NULL)
      {
         int n = pT->m_toolbarMap.GetSize();
         for (int i = 0; i < n; i++)
         {
            IToolbar* pToolbar = (IToolbar*)pT->m_toolbarMap.GetValueAt(i);
             if (pToolbar != NULL)
             {
               pT->m_spControlbar->Detach(pToolbar);
               pToolbar->Release();
             }
         }
      }
      pT->m_toolbarMap.RemoveAll();

      m_spConsole->SetHeader(NULL);
      m_spConsole.Release();
      return S_OK;
   }
    
    STDMETHOD(QueryDataObject)(MMC_COOKIE cookie,
        DATA_OBJECT_TYPES type,
        LPDATAOBJECT  *ppDataObject)
   {
      ATLTRACE(_T("IComponentImpl::QueryDataObject\n"));

      ATLASSERT(ppDataObject != NULL);
      if (ppDataObject == NULL)
      {
         ATLTRACE(_T("IComponent::QueryDataObject called with ppDataObject==NULL \n"));
         return E_POINTER;
      }
      
      if (cookie == NULL)
      {
         ATLTRACE(_T("IComponent::QueryDataObject called with cookie==NULL \n"));
         return E_UNEXPECTED;
      }

      *ppDataObject = NULL;
      if (cookie == MMC_MULTI_SELECT_COOKIE)
      {
         ATLTRACE(_T("Override QueryDataObject to handle multiselect\n"));
         return E_UNEXPECTED;
      }

      CSnapInItem* pItem = (CSnapInItem*) cookie;
      return pItem->GetDataObject(ppDataObject, type);
   }
    
    STDMETHOD(GetResultViewType)(MMC_COOKIE cookie,
        LPOLESTR  *ppViewType,
        long  *pViewOptions)
   {
      ATLTRACE(_T("IComponentImpl::GetResultViewType\n"));

      if (cookie == NULL)
      {

#ifdef FIX_PROBLEM_WITH_GET_RESULT_VIEW_TYPE_NULL_COOKIE
         T* pT = static_cast<T*>(this);

         // We are being asked about our root node.
         _ASSERTE( pT->m_pComponentData!= NULL );
         _ASSERTE( pT->m_pComponentData->m_pNode != NULL );
         return pT->m_pComponentData->m_pNode->GetResultViewType(ppViewType, pViewOptions);
   
#else // FIX_PROBLEM_WITH_GET_RESULT_VIEW_TYPE_NULL_COOKIE
         
         
         *ppViewType = NULL;
         *pViewOptions = MMC_VIEW_OPTIONS_NONE;
         return S_FALSE;
#endif //FIX_PROBLEM_WITH_GET_RESULT_VIEW_TYPE_NULL_COOKIE

      
      }
      

      CSnapInItem* pItem = (CSnapInItem*)cookie;
      return pItem->GetResultViewType(ppViewType, pViewOptions);
   }
    
    STDMETHOD(GetDisplayInfo)(RESULTDATAITEM *pResultDataItem)
   {
      ATLTRACE(_T("IComponentImpl::GetDisplayInfo\n"));

      ATLASSERT(pResultDataItem != NULL);
      if (pResultDataItem == NULL)
      {
         ATLTRACE(_T("IComponent::GetDisplayInfo called with pResultDataItem==NULL\n"));
         return E_POINTER;
      }

      CSnapInItem* pItem = (CSnapInItem*) pResultDataItem->lParam;

      if (pItem == NULL)
      {
         ATLTRACE(_T("Invalid Item\n"));
         return E_UNEXPECTED;
      }
      return pItem->GetResultPaneInfo(pResultDataItem);
   }
    
    STDMETHOD(CompareObjects)( LPDATAOBJECT lpDataObjectA,
        LPDATAOBJECT lpDataObjectB)
   {
      ATLTRACENOTIMPL(_T("IComponentImpl::CompareObjects\n"));
   }

   CComPtr<IConsole> m_spConsole;
};

template <class T>
HRESULT IComponentImpl<T>::Notify(LPDATAOBJECT lpDataObject,
    MMC_NOTIFY_TYPE event,
    LPARAM arg,
    LPARAM param)
{
   ATLTRACE(_T("IComponentImpl::Notify\n"));
   HRESULT hr = E_POINTER;
   
   ATLASSERT(lpDataObject != NULL);
   if (lpDataObject == NULL)
      ATLTRACE(_T("IComponent::Notify called with lpDataObject==NULL \n"));
   else
   {
      T* pT = static_cast<T*>(this);
      CSnapInItem* pItem;
      DATA_OBJECT_TYPES type;
      // Make sure that the object is derived from CSnapInObjectRoot
      hr = pT->m_pComponentData->GetDataClass(lpDataObject, &pItem, &type);
      if (SUCCEEDED(hr))
         hr = pItem->Notify(event, arg, param, NULL, pT, type);
   }
   return hr;
}

template <class T, class D>        
class ATL_NO_VTABLE IResultDataCompareImpl : public IResultDataCompare
{
public:
    STDMETHOD(Compare)(LPARAM lUserParam,
        MMC_COOKIE cookieA,
        MMC_COOKIE cookieB,
        int *pnResult)
   {
      ATLTRACENOTIMPL(_T("IResultDataCompareImpl::Compare"));
   }
};


template <class T>
class ATL_NO_VTABLE IExtendContextMenuImpl : public IExtendContextMenu
{
public:
    STDMETHOD(AddMenuItems)(LPDATAOBJECT pDataObject,
        LPCONTEXTMENUCALLBACK piCallback,
        long *pInsertionAllowed);
   
    STDMETHOD(Command)(long lCommandID,
        LPDATAOBJECT pDataObject);
};

template <class T>
inline HRESULT IExtendContextMenuImpl<T>::AddMenuItems(LPDATAOBJECT pDataObject,
    LPCONTEXTMENUCALLBACK piCallback,
    long *pInsertionAllowed)
{
   ATLTRACE(_T("IExtendContextMenuImpl::AddMenuItems\n"));

   HRESULT hr = E_POINTER;

   ATLASSERT(pDataObject != NULL);
   if (pDataObject == NULL)
      ATLTRACE(_T("IExtendContextMenu::AddMenuItems called with pDataObject==NULL\n"));
   else
   {
      T* pT = static_cast<T*>(this);
      CSnapInItem* pItem;
      DATA_OBJECT_TYPES type;
      hr = pT->m_pComponentData->GetDataClass(pDataObject, &pItem, &type);

      if (SUCCEEDED(hr))
         hr = pItem->AddMenuItems(piCallback, pInsertionAllowed, type);
   }
   return hr;
}

template <class T>
inline HRESULT IExtendContextMenuImpl<T>::Command(long lCommandID,
    LPDATAOBJECT pDataObject)
{
   ATLTRACE(_T("IExtendContextMenuImpl::Command\n"));

   HRESULT hr = E_POINTER;

   ATLASSERT(pDataObject != NULL);
   if (pDataObject == NULL)
      ATLTRACE(_T("IExtendContextMenu::Command called with pDataObject==NULL\n"));
   else
   {
      T* pT = static_cast<T*>(this);
      CSnapInItem* pItem;
      DATA_OBJECT_TYPES type;
      hr = pT->m_pComponentData->GetDataClass(pDataObject, &pItem, &type);
      
      if (SUCCEEDED(hr))
         hr = pItem->Command(lCommandID, (CSnapInObjectRootBase*)pT, type);
   }
   return hr;
}

template<class T>
class ATL_NO_VTABLE IExtendPropertySheetImpl : public IExtendPropertySheet2
{
public:
   STDMETHOD(CreatePropertyPages)(LPPROPERTYSHEETCALLBACK lpProvider,
        LONG_PTR handle,
        LPDATAOBJECT pDataObject);
    
    STDMETHOD(QueryPagesFor)(LPDATAOBJECT pDataObject);
    STDMETHOD(GetWatermarks)( 
            LPDATAOBJECT lpIDataObject,
             HBITMAP *lphWatermark,
             HBITMAP *lphHeader,
             HPALETTE *lphPalette,
             BOOL *bStretch);

};

template<class T>
inline HRESULT IExtendPropertySheetImpl<T>::GetWatermarks(
         LPDATAOBJECT lpIDataObject,
            HBITMAP *lphWatermark,
            HBITMAP *lphHeader,
            HPALETTE *lphPalette,
            BOOL *bStretch)
{
   ATLTRACE(_T("IExtendPropertySheetImpl::GetWatermarks\n"));
   return E_NOTIMPL;
}
    
template<class T>
inline HRESULT IExtendPropertySheetImpl<T>::CreatePropertyPages(LPPROPERTYSHEETCALLBACK lpProvider,
    LONG_PTR handle,
    LPDATAOBJECT pDataObject)
{
   ATLTRACE(_T("IExtendPropertySheetImpl::CreatePropertyPages\n"));

   HRESULT hr = E_POINTER;

   ATLASSERT(pDataObject != NULL);
   if (pDataObject == NULL)
      ATLTRACE(_T("IExtendPropertySheetImpl::CreatePropertyPages called with pDataObject==NULL\n"));
   else
   {
      T* pT = static_cast<T*>(this);
      CSnapInItem* pItem;
      DATA_OBJECT_TYPES type;
      hr = pT->m_pComponentData->GetDataClass(pDataObject, &pItem, &type);

      if (SUCCEEDED(hr))
         hr = pItem->CreatePropertyPages(lpProvider, handle, this, type);
   }

   return hr;
}

template <class T>
inline HRESULT IExtendPropertySheetImpl<T>::QueryPagesFor(LPDATAOBJECT pDataObject)
{
   ATLTRACE(_T("IExtendPropertySheetImpl::QueryPagesFor\n"));
   

   HRESULT hr = E_POINTER;
   
   ATLASSERT(pDataObject != NULL);
   if (pDataObject == NULL)
      ATLTRACE(_T("IExtendPropertySheetImpl::QueryPagesFor called with pDataObject==NULL\n"));
   else
   {
      T* pT = static_cast<T*>(this);
      CSnapInItem* pItem;
      DATA_OBJECT_TYPES type;
      hr = pT->m_pComponentData->GetDataClass(pDataObject, &pItem, &type);

      if (SUCCEEDED(hr))
         hr = pItem->QueryPagesFor(type);
   }
   return hr;
}

template <class T>
class ATL_NO_VTABLE IExtendControlbarImpl : public IExtendControlbar
{
public:
   STDMETHOD(SetControlbar)(LPCONTROLBAR pControlbar)
   {
      ATLTRACE(_T("IExtendControlbarImpl::SetControlbar\n"));
      T* pT = static_cast<T*>(this);

      if (pT->m_spControlbar != NULL)
      {
         int n = pT->m_toolbarMap.GetSize();
         for (int i = 0; i < n; i++)
         {
            IToolbar* pToolbar = (IToolbar*)pT->m_toolbarMap.GetValueAt(i);
             if (pToolbar != NULL)
             {
               pT->m_spControlbar->Detach(pToolbar);
               pToolbar->Release();
             }
         }
      }
      pT->m_toolbarMap.RemoveAll();

      pT->m_spControlbar = pControlbar;
      return S_OK;
   }
    
    STDMETHOD(ControlbarNotify)(MMC_NOTIFY_TYPE event,
        LPARAM arg,
        LPARAM param);
};

template <class T>
inline HRESULT IExtendControlbarImpl<T>::ControlbarNotify(MMC_NOTIFY_TYPE event,
    LPARAM arg,
    LPARAM param)
{
   ATLTRACE(_T("IExtendControlbarImpl::ControlbarNotify\n"));

   CSnapInItem* pItem = NULL;
   DATA_OBJECT_TYPES type;
   HRESULT hr = S_OK;
   T* pT = static_cast<T*>(this);

   if (event == MMCN_BTN_CLICK)
      hr = pT->m_pComponentData->GetDataClass((IDataObject*) arg, &pItem, &type);
   else if (event == MMCN_SELECT)
   {
      hr = pT->m_pComponentData->GetDataClass((IDataObject*) param, &pItem, &type);
      BOOL bSelect = (BOOL) HIWORD (arg);
      BOOL bScope = (BOOL) LOWORD(arg); 
      if (bSelect)
      {
         int n = pT->m_toolbarMap.GetSize();
         for (int i = 0; i < n; i++)
         {
            IToolbar* pToolbar = (IToolbar*)pT->m_toolbarMap.GetValueAt(i);
             if (pToolbar != NULL)
               pT->m_spControlbar->Detach(pToolbar);
         }
      }
   }

   if (SUCCEEDED(hr))
      hr = pItem->ControlbarNotify(pT->m_spControlbar, this, &(pT->m_toolbarMap), event, arg, param, (CSnapInObjectRootBase*) pT, type);

   return hr;
}



#ifdef TASKPAD_SUPPORT
template <class T>
class ATL_NO_VTABLE IExtendTaskPadImpl : public IExtendTaskPad
{
public:

   STDMETHOD(TaskNotify)( 
      /* [in] */ IDataObject __RPC_FAR *lpDataObject,
      /* [in] */ VARIANT __RPC_FAR *pvarg,
      /* [in] */ VARIANT __RPC_FAR *pvparam)
   {
      ATLTRACE(_T("IExtendTaskPadImpl::TaskNotify\n"));

      HRESULT hr = E_POINTER;
      
      ATLASSERT(lpDataObject != NULL);
      if (lpDataObject == NULL)
         ATLTRACE(_T("IExtendTaskPadImpl::TaskNotify called with lpDataObject==NULL \n"));
      else
      {
         T* pT = static_cast<T*>(this);
         CSnapInItem* pItem;
         DATA_OBJECT_TYPES type;
         ATLASSERT(pT->m_pComponentData != NULL);
         hr = pT->m_pComponentData->GetDataClass(lpDataObject, &pItem, &type);
         if (SUCCEEDED(hr))
            hr = pItem->TaskNotify(
                       lpDataObject
                     , pvarg
                     , pvparam
                     );
      }
      return hr;

   }

   STDMETHOD(EnumTasks)( 
      /* [in] */ IDataObject __RPC_FAR *lpDataObject,
      /* [in] */ LPOLESTR szTaskGroup,
      /* [out] */ IEnumTASK __RPC_FAR *__RPC_FAR *ppEnumTASK)
   {
      ATLTRACE(_T("IExtendTaskPadImpl::EnumTasks\n"));

      HRESULT hr = E_POINTER;
      
      ATLASSERT(lpDataObject != NULL);
      if (lpDataObject == NULL)
         ATLTRACE(_T("IExtendTaskPadImpl::EnumTasks called with lpDataObject==NULL \n"));
      else
      {
         T* pT = static_cast<T*>(this);
         CSnapInItem* pItem;
         DATA_OBJECT_TYPES type;
         ATLASSERT(pT->m_pComponentData != NULL);
         hr = pT->m_pComponentData->GetDataClass(lpDataObject, &pItem, &type);
         if (SUCCEEDED(hr))
            hr = pItem->EnumTasks(
                       lpDataObject
                     , szTaskGroup
                     , ppEnumTASK
                     );
      }
      return hr;
   }

   STDMETHOD(GetTitle)( 
      /* [in,string] */ LPOLESTR szGroup,
      /* [out,string] */ LPOLESTR *pszTitle)
   {
      ATLTRACENOTIMPL(_T("IExtendTaskPadImpl::GetTitle\n"));
   }

   STDMETHOD(GetDescriptiveText)( 
      /* [in,string] */ LPOLESTR pszGroup,
      /* [out,string] */ LPOLESTR *pszDescriptiveText)
   {
      ATLTRACENOTIMPL(_T("IExtendTaskPadImpl::GetDescriptiveText\n"));
   }

   STDMETHOD(GetBackground)( 
      /* [in,string] */ LPOLESTR pszGroup,
      /* [out] */ MMC_TASK_DISPLAY_OBJECT *pTDO )
   {
      ATLTRACENOTIMPL(_T("IExtendTaskPadImpl::GetBackground\n"));
   }

   STDMETHOD(GetListPadInfo)( 
            /* [string][in] */ LPOLESTR pszGroup,
            /* [out] */ MMC_LISTPAD_INFO __RPC_FAR *lpListPadInfo)
   {
      ATLTRACENOTIMPL(_T("IExtendTaskPadImpl::GetListPadInfo\n"));
   }


};
#endif // TASKPAD_SUPPORT







#define SNAPINMENUID(id) \
public: \
   static const UINT GetMenuID() \
   { \
      static const UINT IDMENU = id; \
      return id; \
   }

#define EXT_SNAPINMENUID(id) \
public: \
   static const UINT GetMenuID() \
   { \
      static const UINT IDMENU = id; \
      return id; \
   }

#define BEGIN_SNAPINCOMMAND_MAP(theClass, bIsExtension) \
   HRESULT ProcessCommand(UINT nID, \
      bool& bHandled, \
      CSnapInObjectRootBase* pObj, \
      DATA_OBJECT_TYPES type) \
   { \
         bHandled = true; \
         HRESULT hr = S_OK;

#define SNAPINCOMMAND_ENTRY(id, func) \
      if (id == nID) \
      { \
         hr = func(bHandled, pObj); \
         if (bHandled) \
            return hr; \
      }

#define SNAPINCOMMAND_RANGE_ENTRY(id1, id2, func) \
      if (id1 >= nID && nID <= id2) \
      { \
         hr = func(nID, bHandled, pObj); \
         if (bHandled) \
            return hr; \
      }

#define CHAIN_SNAPINCOMMAND_MAP(theChainClass) \
   { \
      hr = theChainClass::ProcessCommand(nID, bHandled, pObj, type); \
      if (bHandled) \
         return hr; \
   }

#define END_SNAPINCOMMAND_MAP() \
         return hr; \
   }

struct CSnapInToolBarData
{
   WORD wVersion;
   WORD wWidth;
   WORD wHeight;
   WORD wItemCount;
   //WORD aItems[wItemCount]

   WORD* items()
      { return (WORD*)(this+1); }
};

#define RT_TOOLBAR  MAKEINTRESOURCE(241)
   
class CSnapInToolbarInfo
{
public:
   ~CSnapInToolbarInfo()
   {
      CleanUp();
   }

   HRESULT CleanUp()
   {
      if (m_pStrToolTip)
      {
         for (UINT i = 0; i < m_nButtonCount; i++)
         {
            delete m_pStrToolTip[i];
            m_pStrToolTip[i] = NULL;
         }
         delete [] m_pStrToolTip;
         m_pStrToolTip = NULL;
      }

      if (m_pStrButtonText)
      {
         for (UINT i = 0; i < m_nButtonCount; i++)
         {
            delete m_pStrButtonText[i];
            m_pStrButtonText[i] = NULL;
         }

         delete [] m_pStrButtonText;
         m_pStrButtonText = NULL;
      }
      if (m_pnButtonID)
      {
         delete m_pnButtonID;
         m_pnButtonID = NULL;
      }

      m_nButtonCount = 0;
      return S_OK;
   }

   OLECHAR** m_pStrToolTip;
   OLECHAR** m_pStrButtonText;
   UINT* m_pnButtonID;
   UINT m_idToolbar;
   UINT m_nButtonCount;
};

#define BEGIN_SNAPINTOOLBARID_MAP(theClass) \
public: \
   static CSnapInToolbarInfo* GetToolbarInfo() \
   { \
      static CSnapInToolbarInfo m_toolbarInfo[] = \
      {

#define SNAPINTOOLBARID_ENTRY(id) \
         { NULL, NULL, NULL, id, 0},

#define END_SNAPINTOOLBARID_MAP() \
         { NULL, NULL, NULL, 0, 0} \
      }; \
      return m_toolbarInfo; \
   }  

template <class T, BOOL bIsExtension = FALSE>
class ATL_NO_VTABLE CSnapInItemImpl : public CSnapInItem
{
public:
   CSnapInItemImpl(unsigned int helpIndex = 0)
      :CSnapInItem(helpIndex)
   {
   }

   virtual ~CSnapInItemImpl()
   {
   }    

public:

    STDMETHOD(Notify)( MMC_NOTIFY_TYPE event,
        LPARAM arg,
        LPARAM param,
      IComponentData* pComponentData,
      IComponent* pComponent,
      DATA_OBJECT_TYPES type)
   {
      ATLASSERT("Override Function in Derived Class");
      ATLTRACENOTIMPL(_T("CSnapInItemImpl::Notify"));
   }
    
    STDMETHOD(GetScopePaneInfo)(SCOPEDATAITEM *pScopeDataItem)
   {
      ATLTRACENOTIMPL(_T("CSnapInItemImpl::GetScopePaneInfo"));
   }
    
    STDMETHOD(GetResultViewType)(LPOLESTR *ppViewType,
        long *pViewOptions)
   {
      ATLTRACE(_T("CSnapInItemImpl::GetResultViewType\n"));
      *ppViewType = NULL;
      *pViewOptions = MMC_VIEW_OPTIONS_NONE;
      return S_FALSE;
   }
    
    STDMETHOD(GetResultPaneInfo)(RESULTDATAITEM *pResultDataItem)
   {
      ATLTRACENOTIMPL(_T("CSnapInItemImpl::GetResultPaneInfo"));
   }
    
    STDMETHOD(AddMenuItems)(LPCONTEXTMENUCALLBACK piCallback,
        long *pInsertionAllowed,
      DATA_OBJECT_TYPES type)
   {
      ATLTRACE(_T("CSnapInItemImpl::AddMenuItems\n"));
      T* pT = static_cast<T*>(this);

      if (!bIsExtension)
         pT->SetMenuInsertionFlags(true, pInsertionAllowed);

      UINT menuID = pT->GetMenuID();
      if (menuID == 0)
         return S_OK;

      HMENU hMenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(menuID));
      long insertionID;
      if (hMenu)
      {
         for (int i = 0; 1; i++)
         {
            HMENU hSubMenu = GetSubMenu(hMenu, i);
            if (hSubMenu == NULL)
               break;
            
            MENUITEMINFO menuItemInfo;
            memset(&menuItemInfo, 0, sizeof(menuItemInfo));
            menuItemInfo.cbSize = sizeof(menuItemInfo);

            switch (i)
            {
            case 0:
               if (! (*pInsertionAllowed & CCM_INSERTIONALLOWED_TOP) )
                  continue;
               insertionID = CCM_INSERTIONPOINTID_PRIMARY_TOP;
               break;

            case 1:
               if (! (*pInsertionAllowed & CCM_INSERTIONALLOWED_NEW) )
                  continue;
               if (bIsExtension)
                  insertionID = CCM_INSERTIONPOINTID_3RDPARTY_NEW;
               else
                  insertionID = CCM_INSERTIONPOINTID_PRIMARY_NEW;
               break;

            case 2:;
               if (! (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK) )
                  continue;
               if (bIsExtension)
                  insertionID = CCM_INSERTIONPOINTID_3RDPARTY_TASK;
               else
                  insertionID = CCM_INSERTIONPOINTID_PRIMARY_TASK;
               break;
            case 3:;
               if (! (*pInsertionAllowed & CCM_INSERTIONALLOWED_VIEW) )
                  continue;
               insertionID = CCM_INSERTIONPOINTID_PRIMARY_VIEW;
               break;
            default:
               {
                  insertionID = 0;
                  continue;
               }
               break;
            }

            menuItemInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
            menuItemInfo.fType = MFT_STRING;
            TCHAR szMenuText[128];

            for (int j = 0; 1; j++)
            {
               menuItemInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
               menuItemInfo.fType = MFT_STRING;
               menuItemInfo.cch = 128;
               menuItemInfo.dwTypeData = szMenuText;
               TCHAR szStatusBar[256];

               if (!GetMenuItemInfo(hSubMenu, j, TRUE, &menuItemInfo))
                  break;
               if (menuItemInfo.fType != MFT_STRING)
                  continue;

               pT->UpdateMenuState(menuItemInfo.wID, szMenuText, &menuItemInfo.fState);
               LoadString(_Module.GetResourceInstance(), menuItemInfo.wID, szStatusBar, 256);

               OLECHAR wszStatusBar[256];
               OLECHAR wszMenuText[128];
               USES_CONVERSION;
               ocscpy(wszMenuText, T2OLE(szMenuText));
               ocscpy(wszStatusBar, T2OLE(szStatusBar));

               CONTEXTMENUITEM contextMenuItem;
               contextMenuItem.strName = wszMenuText;
               contextMenuItem.strStatusBarText = wszStatusBar;
               contextMenuItem.lCommandID = menuItemInfo.wID;
               contextMenuItem.lInsertionPointID = insertionID;
               contextMenuItem.fFlags = menuItemInfo.fState;
               contextMenuItem.fSpecialFlags = 0;
               
               HRESULT hr = piCallback->AddItem(&contextMenuItem);
               ATLASSERT(SUCCEEDED(hr));
            }
         }
         DestroyMenu(hMenu);
      }

      if (!bIsExtension)
         pT->SetMenuInsertionFlags(true, pInsertionAllowed);

      return S_OK;
   }
    
    STDMETHOD(Command)(long lCommandID,      
      CSnapInObjectRootBase* pObj,
      DATA_OBJECT_TYPES type)
   {
      ATLTRACE(_T("CSnapInItemImpl::Command\n"));
      bool bHandled;
      T* pT = static_cast<T*>(this);
      return pT->ProcessCommand(lCommandID, bHandled, pObj, type);
   }
    
    STDMETHOD(CreatePropertyPages)(LPPROPERTYSHEETCALLBACK lpProvider,
        LONG_PTR handle, 
      IUnknown* pUnk,
      DATA_OBJECT_TYPES type)
   {
      ATLASSERT("Override Function in Derived Class");
      ATLTRACENOTIMPL(_T("CSnapInItemImpl::CreatePropertyPages"));
   }
    
    STDMETHOD(QueryPagesFor)(DATA_OBJECT_TYPES type)
   {
      ATLASSERT("Override Function in Derived Class");
      ATLTRACENOTIMPL(_T("CSnapInItemImpl::QueryPagesFor"));
   }

    STDMETHOD(SetControlbar)(IControlbar *pControlbar, 
      IExtendControlbar* pExtendControlBar,
      CSimpleMap<UINT, IUnknown*>* pToolbarMap)
   {
      ATLTRACE(_T("CSnapInItemImpl::SetControlbar\n"));
      T* pT = static_cast<T*>(this);

      CSnapInToolbarInfo* pInfo = pT->GetToolbarInfo();
      if (pInfo == NULL)
         return S_OK;

      for( ; pInfo->m_idToolbar; pInfo++)
      {
         IToolbar* p = (IToolbar*) pToolbarMap->Lookup(pInfo->m_idToolbar);
         if (p != NULL)
            continue;

         HBITMAP hBitmap = LoadBitmap(_Module.GetResourceInstance(), MAKEINTRESOURCE(pInfo->m_idToolbar));
         if (hBitmap == NULL)
            return S_OK;

         HRSRC hRsrc = ::FindResource(_Module.GetResourceInstance(), MAKEINTRESOURCE(pInfo->m_idToolbar), RT_TOOLBAR);
         if (hRsrc == NULL)
            return S_OK;

         HGLOBAL hGlobal = LoadResource(_Module.GetResourceInstance(), hRsrc);
         if (hGlobal == NULL)
            return S_OK;

         CSnapInToolBarData* pData = (CSnapInToolBarData*)LockResource(hGlobal);
         if (pData == NULL)
            return S_OK;
         ATLASSERT(pData->wVersion == 1);
         ATLASSERT(pData->wWidth == 16);
         ATLASSERT(pData->wHeight == 16);

         pInfo->m_nButtonCount = pData->wItemCount;
         if (pInfo->m_pnButtonID == NULL)
            pInfo->m_pnButtonID = new UINT[pInfo->m_nButtonCount];

         if (pInfo->m_pnButtonID == NULL)
            continue;

         MMCBUTTON *pButtons = new MMCBUTTON[pData->wItemCount];
         if (pButtons == NULL)
         {
            delete []pInfo->m_pnButtonID;
            continue;
         }
         
         if (pInfo->m_pStrToolTip == NULL)
         {
            pInfo->m_pStrToolTip = new OLECHAR* [pData->wItemCount];
            if (pInfo->m_pStrToolTip)
               memset(pInfo->m_pStrToolTip, 0, sizeof(OLECHAR*) * pData->wItemCount);
         }

         if (pInfo->m_pStrToolTip == NULL)
         {
            delete []pInfo->m_pnButtonID;
            delete []pButtons;
            continue;
         }


         for (int i = 0, j = 0; i < pData->wItemCount; i++)
         {
            pInfo->m_pnButtonID[i] = pButtons[i].idCommand = pData->items()[i];
            if (pButtons[i].idCommand)
            {
               pButtons[i].nBitmap = j++;
               // get the statusbar string and allow modification of the button state
               TCHAR szStatusBar[512];
               LoadString(_Module.GetResourceInstance(), pButtons[i].idCommand, szStatusBar, 512);

               if (pInfo->m_pStrToolTip[i] == NULL)
                  pInfo->m_pStrToolTip[i] = new OLECHAR[lstrlen(szStatusBar) + 1];
               if (pInfo->m_pStrToolTip[i] == NULL)
                  continue;
               USES_CONVERSION;
               ocscpy(pInfo->m_pStrToolTip[i], T2OLE(szStatusBar));
               pButtons[i].lpTooltipText = pInfo->m_pStrToolTip[i];
               pButtons[i].lpButtonText = OLESTR("");
               pButtons[i].fsState = TBSTATE_ENABLED;
               pButtons[i].fsType = TBSTYLE_BUTTON;
               pT->SetToolbarButtonInfo(pButtons[i].idCommand, &pButtons[i].fsState, &pButtons[i].fsType);
            }
            else
            {
               pButtons[i].lpTooltipText = OLESTR("");
               pButtons[i].lpButtonText = OLESTR("");
               pButtons[i].fsType = TBSTYLE_SEP;
               pButtons[i].fsState = 0;
            }
         }

         IToolbar* pToolbar;
           HRESULT hr = pControlbar->Create(TOOLBAR, pExtendControlBar, reinterpret_cast<LPUNKNOWN*>(&pToolbar));
         if (SUCCEEDED(hr))
         {

            hr = pToolbar->AddBitmap(pData->wItemCount, hBitmap, pData->wWidth, pData->wHeight, RGB(255, 0, 255));
            if (SUCCEEDED(hr))
            {
               hr = pToolbar->AddButtons(pData->wItemCount, pButtons);
               if (SUCCEEDED(hr))
               {
                  pToolbar->AddRef();
                  pToolbarMap->Add(pInfo->m_idToolbar, (IUnknown*)pToolbar);
               }
            }
         }
         pToolbar->Release();
         delete [] pButtons;
      }
      return S_OK;
   }
    
    STDMETHOD(ControlbarNotify)(IControlbar *pControlbar,
        IExtendControlbar *pExtendControlbar,
      CSimpleMap<UINT, IUnknown*>* pToolbarMap,
      MMC_NOTIFY_TYPE event,
        LPARAM arg, 
      LPARAM param,
      CSnapInObjectRootBase* pObj,
      DATA_OBJECT_TYPES type)
   {
      ATLTRACE(_T("CSnapInItemImpl::ControlbarNotify\n"));
      T* pT = static_cast<T*>(this);

      SetControlbar(pControlbar, pExtendControlbar, pToolbarMap);

      if(event == MMCN_SELECT)
      {
         if (pControlbar == NULL)
            return S_OK;

         BOOL bSelect = (BOOL) HIWORD (arg);
         BOOL bScope = (BOOL) LOWORD(arg); 

         CSnapInToolbarInfo* pInfo = pT->GetToolbarInfo();
         if (pInfo == NULL)
            return S_OK;

         for(; pInfo->m_idToolbar; pInfo++)
         {
            IToolbar* pToolbar = (IToolbar*)pToolbarMap->Lookup(pInfo->m_idToolbar);
            if (pToolbar == NULL)
               continue;

            if (!bSelect && bScope)
               pControlbar->Detach(pToolbar);
            else
            {
               pControlbar->Attach(TOOLBAR, pToolbar);
               for (UINT i = 0; i < pInfo->m_nButtonCount; i++)
               {
                  if (pInfo->m_pnButtonID[i])
                  {
                     pToolbar->SetButtonState(pInfo->m_pnButtonID[i], 
                        ENABLED,
                        pT->UpdateToolbarButton(pInfo->m_pnButtonID[i], 
                           ENABLED));
                     pToolbar->SetButtonState(pInfo->m_pnButtonID[i], 
                        CHECKED,
                        pT->UpdateToolbarButton(pInfo->m_pnButtonID[i], 
                           CHECKED));
                     pToolbar->SetButtonState(pInfo->m_pnButtonID[i], 
                        HIDDEN,
                        pT->UpdateToolbarButton(pInfo->m_pnButtonID[i], 
                           HIDDEN));
                     pToolbar->SetButtonState(pInfo->m_pnButtonID[i], 
                        INDETERMINATE,
                        pT->UpdateToolbarButton(pInfo->m_pnButtonID[i], 
                           INDETERMINATE));
                     pToolbar->SetButtonState(pInfo->m_pnButtonID[i], 
                        BUTTONPRESSED,
                        pT->UpdateToolbarButton(pInfo->m_pnButtonID[i], 
                           BUTTONPRESSED));
                  }
               }
            }
         }
         return S_OK;
      }
      else if (event == MMCN_BTN_CLICK)
      {
         bool bHandled;
         return pT->ProcessCommand((UINT) param, bHandled, pObj, type);
      }

      return E_UNEXPECTED;
   }

   STDMETHOD(GetScopeData)(SCOPEDATAITEM **pScopeDataItem)
   {
      if (pScopeDataItem == NULL)
         return E_FAIL;

      *pScopeDataItem = &m_scopeDataItem;
      return S_OK;
   }
        
    STDMETHOD(GetResultData)(RESULTDATAITEM **pResultDataItem)
   {
      if (pResultDataItem == NULL)
         return E_FAIL;

      *pResultDataItem = &m_resultDataItem;
      return S_OK;
   }

   STDMETHOD(GetDataObject)(IDataObject** pDataObj, DATA_OBJECT_TYPES type)
   {
      CComObject<CSnapInDataObjectImpl>* pData;
      HRESULT hr = CComObject<CSnapInDataObjectImpl>::CreateInstance(&pData);
      if (FAILED(hr))
         return hr;

      T* pT = static_cast<T*> (this);
      pData->m_objectData.m_pItem = pT;
      pData->m_objectData.m_type = type;

      hr = pData->QueryInterface(IID_IDataObject, (void**)(pDataObj));
      return hr;
   }

   void UpdateMenuState(UINT id, LPTSTR pBuf, UINT *flags)
   {
      return;
   }

   void SetToolbarButtonInfo(UINT id, BYTE *pfsState, BYTE *pfsType)
   {
      *pfsState = TBSTATE_ENABLED;
      *pfsType = TBSTYLE_BUTTON;
   }

   BOOL UpdateToolbarButton(UINT id, BYTE fsState)
   {
      if (fsState == ENABLED)
         return TRUE;
      return FALSE;
   }

   HRESULT ProcessCommand(UINT nID, 
      bool& bHandled,
      CSnapInObjectRootBase* pObj,
      DATA_OBJECT_TYPES type)
   {
      ATLTRACE(_T("No handler for item with ID %d\n"), nID);
      return S_OK;
   }

   STDMETHOD (FillData)(CLIPFORMAT cf, LPSTREAM pStream)
   {
      HRESULT hr = DV_E_CLIPFORMAT;
      ULONG uWritten;

      T* pT = static_cast<T*> (this);

      if (cf == m_CCF_NODETYPE)
      {
         hr = pStream->Write(pT->GetNodeType(), sizeof(GUID), &uWritten);
         return hr;
      }

      if (cf == m_CCF_SZNODETYPE)
      {
         hr = pStream->Write(pT->GetSZNodeType(), (ocslen((OLECHAR*)pT->GetSZNodeType()) + 1 )* sizeof(OLECHAR), &uWritten);
         return hr;
      }

      if (cf == m_CCF_DISPLAY_NAME)
      {
         hr = pStream->Write(pT->GetDisplayName(), (ocslen((OLECHAR*)pT->GetDisplayName()) + 1) * sizeof(OLECHAR), &uWritten);
         return hr;
      }

      if (cf == m_CCF_SNAPIN_CLASSID)
      {
         hr = pStream->Write(pT->GetSnapInCLSID(), sizeof(GUID), &uWritten);
         return hr;
      }

      return hr;
   }

   static CSnapInToolbarInfo* GetToolbarInfo()
   {
      return NULL;
   }

   static const UINT GetMenuID() 
   {
      return 0;
   }

   void SetMenuInsertionFlags(bool bBeforeInsertion, long* pInsertionAllowed)
   {
   }

   void* GetNodeType()
   {
      return (void*)T::m_NODETYPE;
   }
   void* GetSZNodeType()
   {
      return (void*)T::m_SZNODETYPE;
   }

   void* GetDisplayName()
   {
      return (void*)T::m_SZDISPLAY_NAME;
   }

   void* GetSnapInCLSID()
   {
      return (void*)T::m_SNAPIN_CLASSID;
   }

   CComBSTR m_bstrDisplayName;
   SCOPEDATAITEM m_scopeDataItem;
   RESULTDATAITEM m_resultDataItem;


#ifdef TASKPAD_SUPPORT
   STDMETHOD(TaskNotify)(
              IDataObject * pDataObject
            , VARIANT * pvarg
            , VARIANT * pvparam
            )
   {
      ATLTRACENOTIMPL(_T("CSnapInItemImpl::TaskNotify"));

   }

   STDMETHOD(EnumTasks)(
              IDataObject * pDataObject
            , BSTR szTaskGroup
            , IEnumTASK** ppEnumTASK
            )
   {
      ATLTRACENOTIMPL(_T("CSnapInItemImpl::EnumTasks"));
   }
#endif // TASKPAD_SUPPORT

};


_declspec( selectany ) CLIPFORMAT CSnapInItem::m_CCF_NODETYPE = 0;
_declspec( selectany ) CLIPFORMAT CSnapInItem::m_CCF_SZNODETYPE = 0;
_declspec( selectany ) CLIPFORMAT CSnapInItem::m_CCF_DISPLAY_NAME = 0;
_declspec( selectany ) CLIPFORMAT CSnapInItem::m_CCF_SNAPIN_CLASSID = 0;
_declspec( selectany ) CLIPFORMAT CSnapInItem::m_CCF_SNAPIN_GETOBJECTDATA = 0;
_declspec( selectany ) CLIPFORMAT CSnapInItem::m_CCF_MMC_MULTISELECT_DATAOBJECT = 0;

#endif //__ATL_SNAPIN_H__
