// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// 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 __ATLCTL_H__
#define __ATLCTL_H__

#ifndef __cplusplus
	#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#include <atlwin.h>
#include <objsafe.h>
#include <urlmon.h>

#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "urlmon.lib")

ATLAPI_(void) AtlHiMetricToPixel(const SIZEL * lpSizeInHiMetric, LPSIZEL lpSizeInPix);
ATLAPI_(void) AtlPixelToHiMetric(const SIZEL * lpSizeInPix, LPSIZEL lpSizeInHiMetric);
ATLAPI_(HDC) AtlCreateTargetDC(HDC hdc, DVTARGETDEVICE* ptd);

// Include GUIDs for the new stock property dialogs contained in the dll MSStkProp.DLL
#include "msstkppg.h"
#define CLSID_MSStockFont CLSID_StockFontPage
#define CLSID_MSStockColor CLSID_StockColorPage
#define CLSID_MSStockPicture CLSID_StockPicturePage

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

#pragma pack(push, _ATL_PACKING)

// Forward declarations
//
class ATL_NO_VTABLE CComControlBase;
template <class T> class CComControl;
class CComDispatchDriver;

struct ATL_PROPMAP_ENTRY
{
	LPCOLESTR szDesc;
	DISPID dispid;
	const CLSID* pclsidPropPage;
	const IID* piidDispatch;

};

struct ATL_DRAWINFO
{
	UINT cbSize;
	DWORD dwDrawAspect;
	LONG lindex;
	DVTARGETDEVICE* ptd;
	HDC hicTargetDev;
	HDC hdcDraw;
	LPCRECTL prcBounds; //Rectangle in which to draw
	LPCRECTL prcWBounds; //WindowOrg and Ext if metafile
	BOOL bOptimize;
	BOOL bZoomed;
	BOOL bRectInHimetric;
	SIZEL ZoomNum;      //ZoomX = ZoomNum.cx/ZoomNum.cy
	SIZEL ZoomDen;
};

//////////////////////////////////////////////////////////////////////////////
// CComDispatchDriver / Specialization of CComQIPtr<IDispatch, IID_IDispatch>
class CComDispatchDriver
{
public:
	CComDispatchDriver()
	{
		p = NULL;
	}
	CComDispatchDriver(IDispatch* lp)
	{
		if ((p = lp) != NULL)
			p->AddRef();
	}
	CComDispatchDriver(IUnknown* lp)
	{
		p=NULL;
		if (lp != NULL)
			lp->QueryInterface(IID_IDispatch, (void **)&p);
	}
	~CComDispatchDriver() { if (p) p->Release(); }
	void Release() {if (p) p->Release(); p=NULL;}
	operator IDispatch*() {return p;}
	IDispatch& operator*() {_ASSERTE(p!=NULL); return *p; }
	IDispatch** operator&() {_ASSERTE(p==NULL); return &p; }
	IDispatch* operator->() {_ASSERTE(p!=NULL); return p; }
	IDispatch* operator=(IDispatch* lp){return (IDispatch*)AtlComPtrAssign((IUnknown**)&p, lp);}
	IDispatch* operator=(IUnknown* lp)
	{
		return (IDispatch*)AtlComQIPtrAssign((IUnknown**)&p, lp, IID_IDispatch);
	}
	BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}

	HRESULT GetProperty(DISPID dwDispID, VARIANT* pVar)
	{
		_ASSERTE(p);
		return GetProperty(p, dwDispID, pVar);
	}
	HRESULT PutProperty(DISPID dwDispID, VARIANT* pVar)
	{
		_ASSERTE(p);
		return PutProperty(p, dwDispID, pVar);
	}

	static HRESULT GetProperty(IDispatch* pDisp, DISPID dwDispID, VARIANT* pVar);
	static HRESULT PutProperty(IDispatch* pDisp, DISPID dwDispID, VARIANT* pVar);
	IDispatch* p;
};

//////////////////////////////////////////////////////////////////////////////
// CFirePropNotifyEvent
class CFirePropNotifyEvent
{
public:
	static HRESULT FireOnRequestEdit(IUnknown* pUnk, DISPID dispID);
	static HRESULT FireOnChanged(IUnknown* pUnk, DISPID dispID);
};


//////////////////////////////////////////////////////////////////////////////
// CFakeFirePropNotifyEvent
class CFakeFirePropNotifyEvent
{
public:
	static HRESULT FireOnRequestEdit(IUnknown* /*pUnk*/, DISPID /*dispID*/)
	{
		return S_OK;
	}
	static HRESULT FireOnChanged(IUnknown* /*pUnk*/, DISPID /*dispID*/)
	{
		return S_OK;
	}
};


typedef CFakeFirePropNotifyEvent _ATL_PROP_NOTIFY_EVENT_CLASS;

//////////////////////////////////////////////////////////////////////////////
// CComControl
class ATL_NO_VTABLE CComControlBase
{
public:
	CComControlBase(HWND& h) : m_hWndCD(h)
	{
		memset(this, 0, sizeof(CComControlBase));
		m_phWndCD = &h;
		m_sizeExtent.cx = 2*2540;
		m_sizeExtent.cy = 2*2540;
		m_sizeNatural = m_sizeExtent;
	}
	~CComControlBase()
	{
		if (m_hWndCD != NULL)
			::DestroyWindow(m_hWndCD);
		ATLTRACE(_T("Control Destroyed\n"));
	}

// methods
public:
	// Control helper functions can go here
	// non-virtuals only please
	void SetDirty(BOOL bDirty)
	{
		m_bRequiresSave = bDirty;
	}
	BOOL GetDirty()
	{
		return m_bRequiresSave ? TRUE : FALSE;
	}
	void GetZoomInfo(ATL_DRAWINFO& di);
	HRESULT SendOnRename(IMoniker *pmk)
	{
		HRESULT hRes = S_OK;
		if (m_spOleAdviseHolder)
			hRes = m_spOleAdviseHolder->SendOnRename(pmk);
		return hRes;
	}
	HRESULT SendOnSave()
	{
		HRESULT hRes = S_OK;
		if (m_spOleAdviseHolder)
			hRes = m_spOleAdviseHolder->SendOnSave();
		return hRes;
	}
	HRESULT SendOnClose()
	{
		HRESULT hRes = S_OK;
		if (m_spOleAdviseHolder)
			hRes = m_spOleAdviseHolder->SendOnClose();
		return hRes;
	}
	HRESULT SendOnDataChange(DWORD advf = 0);
	HRESULT SendOnViewChange(DWORD dwAspect, LONG lindex = -1)
	{
		if (m_spAdviseSink)
			m_spAdviseSink->OnViewChange(dwAspect, lindex);
		return S_OK;
	}
	LRESULT OnSetFocus(UINT /* nMsg */, WPARAM /* wParam */, LPARAM /* lParam */, BOOL& /* bHandled */)
	{
		CComQIPtr <IOleControlSite, &IID_IOleControlSite> spSite(m_spClientSite);
		if (m_bInPlaceActive && spSite)
			spSite->OnFocus(TRUE);
		return 0;
	}

	LRESULT OnKillFocus(UINT /* nMsg */, WPARAM /* wParam */, LPARAM /* lParam */, BOOL& /* bHandled */)
	{
		CComQIPtr <IOleControlSite, &IID_IOleControlSite> spSite(m_spClientSite);
		if (m_bInPlaceActive && spSite)
			spSite->OnFocus(FALSE);
		return 0;
	}
	LRESULT OnGetDlgCode(UINT /* nMsg */, WPARAM /* wParam */, LPARAM /* lParam */, BOOL& /* bHandled */)
	{
		return 0;
	}

	HRESULT GetAmbientProperty(DISPID dispid, VARIANT& var)
	{
		HRESULT hRes = E_FAIL;
		if (m_spAmbientDispatch.p != NULL)
			hRes = m_spAmbientDispatch.GetProperty(dispid, &var);
		return hRes;
	}
	HRESULT GetAmbientAppearance(short& nAppearance)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_APPEARANCE, var);
		_ASSERTE(var.vt == VT_I2 || FAILED(hRes));
		nAppearance = var.iVal;
		return hRes;
	}
	HRESULT GetAmbientBackColor(OLE_COLOR& BackColor)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_BACKCOLOR, var);
		_ASSERTE(var.vt == VT_I4 || FAILED(hRes));
		BackColor = var.lVal;
		return hRes;
	}
	HRESULT GetAmbientDisplayName(BSTR& bstrDiaplayName)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_DISPLAYNAME, var);
		_ASSERTE(var.vt == VT_BSTR || FAILED(hRes));
		bstrDiaplayName = var.bstrVal;
		return hRes;
	}
	HRESULT GetAmbientFont(IFont** ppFont)
	{
		// caller MUST Release the font!
		if (ppFont == NULL)
			return E_POINTER;
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_FONT, var);
		_ASSERTE((var.vt == VT_UNKNOWN || var.vt == VT_DISPATCH) || FAILED(hRes));
		if (SUCCEEDED(hRes) && var.pdispVal)
		{
			if (var.vt == VT_UNKNOWN || var.vt == VT_DISPATCH)
				hRes = var.pdispVal->QueryInterface(IID_IFont, (void**)ppFont);
			else
				hRes = DISP_E_BADVARTYPE;
		}
		return hRes;
	}
	HRESULT GetAmbientForeColor(OLE_COLOR& ForeColor)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_FORECOLOR, var);
		_ASSERTE(var.vt == VT_I4 || FAILED(hRes));
		ForeColor = var.lVal;
		return hRes;
	}
	HRESULT GetAmbientLocaleID(LCID& lcid)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_LOCALEID, var);
		_ASSERTE(var.vt == VT_I4 || FAILED(hRes));
		lcid = var.lVal;
		return hRes;
	}
	HRESULT GetAmbientScaleUnits(BSTR& bstrScaleUnits)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_SCALEUNITS, var);
		_ASSERTE(var.vt == VT_BSTR || FAILED(hRes));
		bstrScaleUnits = var.bstrVal;
		return hRes;
	}
	HRESULT GetAmbientTextAlign(short& nTextAlign)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_TEXTALIGN, var);
		_ASSERTE(var.vt == VT_I2 || FAILED(hRes));
		nTextAlign = var.iVal;
		return hRes;
	}
	HRESULT GetAmbientUserMode(BOOL& bUserMode)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_USERMODE, var);
		_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
		bUserMode = var.boolVal;
		return hRes;
	}
	HRESULT GetAmbientUIDead(BOOL& bUIDead)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_UIDEAD, var);
		_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
		bUIDead = var.boolVal;
		return hRes;
	}
	HRESULT GetAmbientShowGrabHandles(BOOL& bShowGrabHandles)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_SHOWGRABHANDLES, var);
		_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
		bShowGrabHandles = var.boolVal;
		return hRes;
	}
	HRESULT GetAmbientShowHatching(BOOL& bShowHatching)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_SHOWHATCHING, var);
		_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
		bShowHatching = var.boolVal;
		return hRes;
	}
	HRESULT GetAmbientMessageReflect(BOOL& bMessageReflect)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_MESSAGEREFLECT, var);
		_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
		bMessageReflect = var.boolVal;
		return hRes;
	}
	HRESULT GetAmbientAutoClip(BOOL& bAutoClip)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_AUTOCLIP, var);
		_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
		bAutoClip = var.boolVal;
		return hRes;
	}
	HRESULT GetAmbientDisplayAsDefault(BOOL& bDisplaysDefault)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_DISPLAYASDEFAULT, var);
		_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
		bDisplaysDefault = var.boolVal;
		return hRes;
	}
	HRESULT GetAmbientSupportsMnemonics(BOOL& bSupportMnemonics)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_SUPPORTSMNEMONICS, var);
		_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
		bSupportMnemonics = var.boolVal;
		return hRes;
	}
	HRESULT GetAmbientPalette(HPALETTE& hPalette)
	{
		CComVariant var;
		HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_PALETTE, var);
		_ASSERTE(var.vt == VT_I4 || FAILED(hRes));
		hPalette = reinterpret_cast<HPALETTE>(var.lVal);
		return hRes;
	}

	BOOL DoesVerbUIActivate(LONG iVerb)
	{
		BOOL b = FALSE;
		switch (iVerb)
		{
			case OLEIVERB_UIACTIVATE:
			case OLEIVERB_PRIMARY:
				b = TRUE;
				break;
		}
		// if no ambient dispatch then in old style OLE container
		if (DoesVerbActivate(iVerb) && m_spAmbientDispatch.p == NULL)
			b = TRUE;
		return b;
	}

	BOOL DoesVerbActivate(LONG iVerb)
	{
		BOOL b = FALSE;
		switch (iVerb)
		{
			case OLEIVERB_UIACTIVATE:
			case OLEIVERB_PRIMARY:
			case OLEIVERB_SHOW:
			case OLEIVERB_INPLACEACTIVATE:
				b = TRUE;
				break;
		}
		return b;
	}

	BOOL SetControlFocus(BOOL bGrab);
	HRESULT IQuickActivate_QuickActivate(QACONTAINER *pQACont,
		QACONTROL *pQACtrl);
	HRESULT IPersistPropertyBag_Load(LPPROPERTYBAG pPropBag,
		LPERRORLOG pErrorLog, ATL_PROPMAP_ENTRY* pMap);
	HRESULT IPersistPropertyBag_Save(LPPROPERTYBAG pPropBag,
		BOOL fClearDirty, BOOL fSaveAllProperties, ATL_PROPMAP_ENTRY* pMap);
	HRESULT ISpecifyPropertyPages_GetPages(CAUUID* pPages,
		ATL_PROPMAP_ENTRY* pMap);
	HRESULT DoVerbProperties(LPCRECT /* prcPosRect */, HWND hwndParent);
	HRESULT InPlaceActivate(LONG iVerb, const RECT* prcPosRect = NULL);
	HRESULT IPersistStreamInit_Load(LPSTREAM pStm, ATL_PROPMAP_ENTRY* pMap);
	HRESULT IPersistStreamInit_Save(LPSTREAM pStm, BOOL /* fClearDirty */,
		ATL_PROPMAP_ENTRY* pMap);

	HRESULT IOleObject_SetClientSite(IOleClientSite *pClientSite);
	HRESULT IOleObject_GetClientSite(IOleClientSite **ppClientSite);
	HRESULT IOleObject_Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection);
	HRESULT IOleObject_Close(DWORD dwSaveOption);
	HRESULT IOleObject_SetExtent(DWORD dwDrawAspect, SIZEL *psizel);
	HRESULT IOleInPlaceObject_InPlaceDeactivate(void);
	HRESULT IOleInPlaceObject_UIDeactivate(void);
	HRESULT IOleInPlaceObject_SetObjectRects(LPCRECT prcPos,LPCRECT prcClip);
	HRESULT IViewObject_Draw(DWORD dwDrawAspect, LONG lindex, void *pvAspect,
		DVTARGETDEVICE *ptd, HDC hicTargetDev, HDC hdcDraw,
		LPCRECTL prcBounds, LPCRECTL prcWBounds);
	HRESULT IDataObject_GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);

	HRESULT FireViewChange();
	LRESULT OnPaint(UINT /* nMsg */, WPARAM /* wParam */, LPARAM /* lParam */,
		BOOL& /* lResult */);

	virtual HWND CreateControlWindow(HWND hWndParent, RECT& rcPos) = 0;
	virtual HRESULT ControlQueryInterface(const IID& iid, void** ppv) = 0;
	virtual HRESULT OnDrawAdvanced(ATL_DRAWINFO& di);
	virtual HRESULT OnDraw(ATL_DRAWINFO& di)
	{
		return S_OK;
	}


// Attributes
public:
	CComPtr<IOleInPlaceSiteWindowless> m_spInPlaceSite;
	CComPtr<IDataAdviseHolder> m_spDataAdviseHolder;
	CComPtr<IOleAdviseHolder> m_spOleAdviseHolder;
	CComPtr<IOleClientSite> m_spClientSite;
	CComPtr<IAdviseSink> m_spAdviseSink;
	CComDispatchDriver m_spAmbientDispatch;

	SIZE m_sizeNatural; //unscaled size in himetric
	SIZE m_sizeExtent;  //current extents in himetric
	RECT m_rcPos; // position in pixels
	union
	{
		HWND& m_hWndCD;
		HWND* m_phWndCD;
	};
	union
	{
		// m_nFreezeEvents is the only one actually used
		int m_nFreezeEvents; // count of freezes versus thaws

		// These are here to make stock properties work
		IPictureDisp* m_pMouseIcon;
		IPictureDisp* m_pPicture;
		IFontDisp* m_pFont;
		OLE_COLOR m_clrBackColor;
		OLE_COLOR m_clrBorderColor;
		OLE_COLOR m_clrFillColor;
		OLE_COLOR m_clrForeColor;
		BSTR m_bstrText;
		BSTR m_bstrCaption;
		BOOL m_bValid;
		BOOL m_bTabStop;
		BOOL m_bBorderVisible;
		long m_nBackStyle;
		long m_nBorderStyle;
		long m_nBorderWidth;
		long m_nDrawMode;
		long m_nDrawStyle;
		long m_nDrawWidth;
		long m_nFillStyle;
		long m_nAppearance;
		long m_nMousePointer;
		long m_nReadyState;
	};

	unsigned m_bNegotiatedWnd:1;
	unsigned m_bWndLess:1;
	unsigned m_bInPlaceActive:1;
	unsigned m_bUIActive:1;
	unsigned m_bEnabled:1;
	unsigned m_bUsingWindowRgn:1;
	unsigned m_bInPlaceSiteEx:1;
	unsigned m_bWindowOnly:1;
	unsigned m_bRequiresSave:1;
	unsigned m_bWasOnceWindowless:1;
	unsigned m_bAutoSize:1; //SetExtent fails if size doesn't match existing
	unsigned m_bRecomposeOnResize:1; //implies OLEMISC_RECOMPOSEONRESIZE
	unsigned m_bResizeNatural:1;  //resize natural extent on SetExtent
	unsigned m_bDrawFromNatural:1; //instead of m_sizeExtent
	unsigned m_bDrawGetDataInHimetric:1; //instead of pixels
};

template <class T>
class ATL_NO_VTABLE CComControl :  public CComControlBase, public CWindowImpl<T>
{
public:
	CComControl() : CComControlBase(m_hWnd) {}
	HRESULT FireOnRequestEdit(DISPID dispID)
	{
		T* pT = static_cast<T*>(this);
		return T::__ATL_PROP_NOTIFY_EVENT_CLASS::FireOnRequestEdit(pT->GetUnknown(), dispID);
	}
	HRESULT FireOnChanged(DISPID dispID)
	{
		T* pT = static_cast<T*>(this);
		return T::__ATL_PROP_NOTIFY_EVENT_CLASS::FireOnChanged(pT->GetUnknown(), dispID);
	}
	virtual HRESULT ControlQueryInterface(const IID& iid, void** ppv)
	{
		T* pT = static_cast<T*>(this);
		return pT->_InternalQueryInterface(iid, ppv);
	}
	virtual HWND CreateControlWindow(HWND hWndParent, RECT& rcPos)
	{
		T* pT = static_cast<T*>(this);
		return pT->Create(hWndParent, rcPos);
	}
};

// Forward declarations
//
template <class T> class IPersistImpl;
template <class T> class IPersistStreamInitImpl;
template <class T> class IPersistStorageImpl;
template <class T> class IPersistPropertyBagImpl;

template <class T> class IOleControlImpl;
template <class T> class IRunnableObjectImpl;
template <class T> class IQuickActivateImpl;
template <class T> class IOleObjectImpl;
template <class T> class IPropertyPageImpl;
template <class T> class IPropertyPage2Impl;
template <class T> class IPerPropertyBrowsingImpl;
template <class T> class IViewObjectExImpl;
template <class T> class IOleWindowImpl;
template <class T> class ISpecifyPropertyPagesImpl;
template <class T> class IPointerInactiveImpl;
template <class T, class CDV> class IPropertyNotifySinkCP;
template <class T> class IBindStatusCallbackImpl;
template <class T> class CBindStatusCallback;

//////////////////////////////////////////////////////////////////////////////
// IPersistImpl
template <class T>
class ATL_NO_VTABLE IPersistImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistImpl)

	// IPersist
	STDMETHOD(GetClassID)(CLSID *pClassID)
	{
		ATLTRACE(_T("IPersistImpl::GetClassID\n"));
		T* pT = static_cast<T*>(this);
		*pClassID = pT->GetObjectCLSID();
		return S_OK;
	}
};

#define BEGIN_PROPERTY_MAP(theClass) \
	typedef _ATL_PROP_NOTIFY_EVENT_CLASS __ATL_PROP_NOTIFY_EVENT_CLASS; \
	static ATL_PROPMAP_ENTRY* GetPropertyMap()\
	{\
		static ATL_PROPMAP_ENTRY pPropMap[] = \
		{

#define PROP_ENTRY(szDesc, dispid, clsid) \
		{OLESTR(szDesc), dispid, &clsid, &IID_IDispatch},

#define PROP_ENTRY_EX(szDesc, dispid, clsid, iidDispatch) \
		{OLESTR(szDesc), dispid, &clsid, &iidDispatch},

#define PROP_PAGE(clsid) \
		{NULL, NULL, &clsid, &IID_NULL},

#define END_PROPERTY_MAP() \
			{NULL, 0, NULL, &IID_NULL} \
		}; \
		return pPropMap; \
	}


//////////////////////////////////////////////////////////////////////////////
// IPersistStreamInitImpl
template <class T>
class ATL_NO_VTABLE IPersistStreamInitImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistStreamInitImpl)

	// IPersist
	STDMETHOD(GetClassID)(CLSID *pClassID)
	{
		ATLTRACE(_T("IPersistStreamInitImpl::GetClassID\n"));
		T* pT = static_cast<T*>(this);
		*pClassID = pT->GetObjectCLSID();
		return S_OK;
	}

	// IPersistStream
	STDMETHOD(IsDirty)()
	{
		ATLTRACE(_T("IPersistStreamInitImpl::IsDirty\n"));
		T* pT = static_cast<T*>(this);
		return (pT->m_bRequiresSave) ? S_OK : S_FALSE;
	}
	STDMETHOD(Load)(LPSTREAM pStm)
	{
		ATLTRACE(_T("IPersistStreamInitImpl::Load\n"));
		T* pT = static_cast<T*>(this);
		return pT->IPersistStreamInit_Load(pStm, T::GetPropertyMap());
	}
	STDMETHOD(Save)(LPSTREAM pStm, BOOL fClearDirty)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPersistStreamInitImpl::Save\n"));
		return pT->IPersistStreamInit_Save(pStm, fClearDirty, T::GetPropertyMap());
	}
	STDMETHOD(GetSizeMax)(ULARGE_INTEGER FAR* /* pcbSize */)
	{
		ATLTRACENOTIMPL(_T("IPersistStreamInitImpl::GetSizeMax"));
	}

	// IPersistStreamInit
	STDMETHOD(InitNew)()
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPersistStreamInitImpl::InitNew\n"));
		pT->SendOnDataChange();
		return S_OK;
	}

};


//////////////////////////////////////////////////////////////////////////////
// IPersistStorageImpl
template <class T>
class ATL_NO_VTABLE IPersistStorageImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistStorageImpl)

	// IPersist
	STDMETHOD(GetClassID)(CLSID *pClassID)
	{
		ATLTRACE(_T("IPersistStorageImpl::GetClassID\n"));
		T* pT = static_cast<T*>(this);
		*pClassID = pT->GetObjectCLSID();
		return S_OK;
	}

	// IPersistStorage
	STDMETHOD(IsDirty)(void)
	{
		ATLTRACE(_T("IPersistStorageImpl::IsDirty\n"));
		T* pT = static_cast<T*>(this);
		CComPtr<IPersistStreamInit> p;
		p.p = IPSI_GetIPersistStreamInit();
		return (p != NULL) ? p->IsDirty() : E_FAIL;
	}
	STDMETHOD(InitNew)(IStorage*)
	{
		ATLTRACE(_T("IPersistStorageImpl::InitNew\n"));
		T* pT = static_cast<T*>(this);
		CComPtr<IPersistStreamInit> p;
		p.p = IPSI_GetIPersistStreamInit();
		return (p != NULL) ? p->InitNew() : E_FAIL;
	}
	STDMETHOD(Load)(IStorage* pStorage)
	{
		ATLTRACE(_T("IPersistStorageImpl::Load\n"));
		T* pT = static_cast<T*>(this);
		CComPtr<IPersistStreamInit> p;
		p.p = IPSI_GetIPersistStreamInit();
		HRESULT hr = E_FAIL;
		if (p != NULL)
		{
			CComPtr<IStream> spStream;
			hr = pStorage->OpenStream(OLESTR("Contents"), NULL,
				STGM_DIRECT | STGM_SHARE_EXCLUSIVE, 0, &spStream);
			if (SUCCEEDED(hr))
				hr = p->Load(spStream);
		}
		return hr;
	}
	STDMETHOD(Save)(IStorage* pStorage, BOOL fSameAsLoad)
	{
		ATLTRACE(_T("IPersistStorageImpl::Save\n"));
		T* pT = static_cast<T*>(this);
		CComPtr<IPersistStreamInit> p;
		p.p = IPSI_GetIPersistStreamInit();
		HRESULT hr = E_FAIL;
		if (p != NULL)
		{
			CComPtr<IStream> spStream;
			static LPCOLESTR vszContents = OLESTR("Contents");
			hr = pStorage->CreateStream(vszContents,
				STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
				0, 0, &spStream);
			if (SUCCEEDED(hr))
				hr = p->Save(spStream, fSameAsLoad);
		}
		return hr;
	}
	STDMETHOD(SaveCompleted)(IStorage* /* pStorage */)
	{
		ATLTRACE(_T("IPersistStorageImpl::SaveCompleted\n"));
		return S_OK;
	}
	STDMETHOD(HandsOffStorage)(void)
	{
		ATLTRACE(_T("IPersistStorageImpl::HandsOffStorage\n"));
		return S_OK;
	}
private:
	IPersistStreamInit* IPSI_GetIPersistStreamInit();
};

template <class T>
IPersistStreamInit* IPersistStorageImpl<T>::IPSI_GetIPersistStreamInit()
{
	T* pT = static_cast<T*>(this);
	IPersistStreamInit* p;
	if (FAILED(pT->GetUnknown()->QueryInterface(IID_IPersistStreamInit, (void**)&p)))
		pT->_InternalQueryInterface(IID_IPersistStreamInit, (void**)&p);
	return p;
}

//////////////////////////////////////////////////////////////////////////////
// IPersistPropertyBagImpl
template <class T>
class ATL_NO_VTABLE IPersistPropertyBagImpl
{
public:

	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistPropertyBagImpl)

	// IPersist
	STDMETHOD(GetClassID)(CLSID *pClassID)
	{
		ATLTRACE(_T("IPersistPropertyBagImpl::GetClassID\n"));
		T* pT = static_cast<T*>(this);
		*pClassID = pT->GetObjectCLSID();
		return S_OK;
	}

	// IPersistPropertyBag
	//
	STDMETHOD(InitNew)()
	{
		ATLTRACE(_T("IPersistPropertyBagImpl::InitNew\n"));
		return S_OK;
	}
	STDMETHOD(Load)(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
	{
		ATLTRACE(_T("IPersistPropertyBagImpl::Load\n"));
		T* pT = static_cast<T*>(this);
		ATL_PROPMAP_ENTRY* pMap = T::GetPropertyMap();
		_ASSERTE(pMap != NULL);
		return pT->IPersistPropertyBag_Load(pPropBag, pErrorLog, pMap);
	}
	STDMETHOD(Save)(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
	{
		ATLTRACE(_T("IPersistPropertyBagImpl::Save\n"));
		T* pT = static_cast<T*>(this);
		ATL_PROPMAP_ENTRY* pMap = T::GetPropertyMap();
		_ASSERTE(pMap != NULL);
		return pT->IPersistPropertyBag_Save(pPropBag, fClearDirty, fSaveAllProperties, pMap);
	}
};


//////////////////////////////////////////////////////////////////////////////
// IOleControlImpl
template <class T>
class ATL_NO_VTABLE IOleControlImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleControlImpl)

	// IOleControl methods
	//
	STDMETHOD(GetControlInfo)(LPCONTROLINFO /* pCI */)
	{
		ATLTRACENOTIMPL(_T("IOleControlImpl::GetControlInfo"));
	}
	STDMETHOD(OnMnemonic)(LPMSG /* pMsg */)
	{
		ATLTRACENOTIMPL(_T("IOleControlImpl::OnMnemonic"));
	}
	STDMETHOD(OnAmbientPropertyChange)(DISPID dispid)
	{
		dispid;
		ATLTRACE(_T("IOleControlImpl::OnAmbientPropertyChange\n"));
		ATLTRACE(_T(" -- DISPID = %d (%d)\n"), dispid);
		return S_OK;
	}
	STDMETHOD(FreezeEvents)(BOOL bFreeze)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleControlImpl::FreezeEvents\n"));
		if (bFreeze)
			pT->m_nFreezeEvents++;
		else
			pT->m_nFreezeEvents--;
		return S_OK;
	}
};


//////////////////////////////////////////////////////////////////////////////
// IQuickActivateImpl
template <class T>
class ATL_NO_VTABLE IQuickActivateImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IQuickActivateImpl)

	// IQuickActivate
	//
	STDMETHOD(QuickActivate)(QACONTAINER *pQACont, QACONTROL *pQACtrl)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IQuickActivateImpl::QuickActivate\n"));
		return pT->IQuickActivate_QuickActivate(pQACont, pQACtrl);
	}
	STDMETHOD(SetContentExtent)(LPSIZEL pSize)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IQuickActivateImpl::SetContentExtent\n"));
		return pT->IOleObjectImpl<T>::SetExtent(DVASPECT_CONTENT, pSize);
	}
	STDMETHOD(GetContentExtent)(LPSIZEL pSize)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IQuickActivateImpl::GetContentExtent\n"));
		return pT->IOleObjectImpl<T>::GetExtent(DVASPECT_CONTENT, pSize);
	}
};


//////////////////////////////////////////////////////////////////////////////
// IOleObjectImpl
template <class T>
class ATL_NO_VTABLE IOleObjectImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleObjectImpl)

	// IOleObject
	//
	STDMETHOD(SetClientSite)(IOleClientSite *pClientSite)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::SetClientSite\n"));
		return pT->IOleObject_SetClientSite(pClientSite);
	}
	STDMETHOD(GetClientSite)(IOleClientSite **ppClientSite)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::GetClientSite\n"));
		return pT->IOleObject_GetClientSite(ppClientSite);
	}
	STDMETHOD(SetHostNames)(LPCOLESTR /* szContainerApp */, LPCOLESTR /* szContainerObj */)
	{
		ATLTRACE(_T("IOleObjectImpl::SetHostNames\n"));
		return S_OK;
	}
	STDMETHOD(Close)(DWORD dwSaveOption)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::Close\n"));
		return pT->IOleObject_Close(dwSaveOption);
	}
	STDMETHOD(SetMoniker)(DWORD /* dwWhichMoniker */, IMoniker* /* pmk */)
	{
		ATLTRACENOTIMPL(_T("IOleObjectImpl::SetMoniker"));
	}
	STDMETHOD(GetMoniker)(DWORD /* dwAssign */, DWORD /* dwWhichMoniker */, IMoniker** /* ppmk */)
	{
		ATLTRACENOTIMPL(_T("IOleObjectImpl::GetMoniker"));
	}
	STDMETHOD(InitFromData)(IDataObject* /* pDataObject */, BOOL /* fCreation */, DWORD /* dwReserved */)
	{
		ATLTRACENOTIMPL(_T("IOleObjectImpl::InitFromData"));
	}
	STDMETHOD(GetClipboardData)(DWORD /* dwReserved */, IDataObject** /* ppDataObject */)
	{
		ATLTRACENOTIMPL(_T("IOleObjectImpl::GetClipboardData"));
	}

	// Helpers for DoVerb - Over-rideable in user class
	HRESULT DoVerbPrimary(LPCRECT prcPosRect, HWND hwndParent)
	{
		T* pT = static_cast<T*>(this);
		BOOL bDesignMode = FALSE;
		CComVariant var;
		// if container doesn't support this property
		// don't allow design mode
		HRESULT hRes = pT->GetAmbientProperty(DISPID_AMBIENT_USERMODE, var);
		if (SUCCEEDED(hRes) && var.vt == VT_BOOL && !var.boolVal)
			bDesignMode = TRUE;
		if (bDesignMode)
			return pT->DoVerbProperties(prcPosRect, hwndParent);
		else
			return pT->DoVerbInPlaceActivate(prcPosRect, hwndParent);
	}
	HRESULT DoVerbShow(LPCRECT prcPosRect, HWND /* hwndParent */)
	{
		T* pT = static_cast<T*>(this);
		return pT->InPlaceActivate(OLEIVERB_SHOW, prcPosRect);
	}
	HRESULT DoVerbInPlaceActivate(LPCRECT prcPosRect, HWND /* hwndParent */)
	{
		T* pT = static_cast<T*>(this);
		return pT->InPlaceActivate(OLEIVERB_INPLACEACTIVATE, prcPosRect);
	}
	HRESULT DoVerbUIActivate(LPCRECT prcPosRect, HWND /* hwndParent */)
	{
		T* pT = static_cast<T*>(this);
		return pT->InPlaceActivate(OLEIVERB_UIACTIVATE, prcPosRect);
	}
	HRESULT DoVerbHide(LPCRECT /* prcPosRect */, HWND /* hwndParent */)
	{
		T* pT = static_cast<T*>(this);
		pT->UIDeactivate();
		if (pT->m_hWnd)
			pT->ShowWindow(SW_HIDE);
		return S_OK;
	}
	HRESULT DoVerbOpen(LPCRECT /* prcPosRect */, HWND /* hwndParent */)
	{
		return S_OK;
	}
	HRESULT DoVerbDiscardUndo(LPCRECT /* prcPosRect */, HWND /* hwndParent */)
	{
		return S_OK;
	}
	STDMETHOD(DoVerb)(LONG iVerb, LPMSG /* lpmsg */, IOleClientSite* /* pActiveSite */, LONG /* lindex */,
									 HWND hwndParent, LPCRECT lprcPosRect)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::DoVerb\n"));
		_ASSERTE(pT->m_spClientSite);

		HRESULT hr = E_NOTIMPL;
		switch (iVerb)
		{
		case OLEIVERB_PRIMARY:
			hr = pT->DoVerbPrimary(lprcPosRect, hwndParent);
			break;
		case OLEIVERB_SHOW:
			hr = pT->DoVerbShow(lprcPosRect, hwndParent);
			break;
		case OLEIVERB_INPLACEACTIVATE:
			hr = pT->DoVerbInPlaceActivate(lprcPosRect, hwndParent);
			break;
		case OLEIVERB_UIACTIVATE:
			hr = pT->DoVerbUIActivate(lprcPosRect, hwndParent);
			break;
		case OLEIVERB_HIDE:
			hr = pT->DoVerbHide(lprcPosRect, hwndParent);
			break;
		case OLEIVERB_OPEN:
			hr = pT->DoVerbOpen(lprcPosRect, hwndParent);
			break;
		case OLEIVERB_DISCARDUNDOSTATE:
			hr = pT->DoVerbDiscardUndo(lprcPosRect, hwndParent);
			break;
		case OLEIVERB_PROPERTIES:
			hr = pT->DoVerbProperties(lprcPosRect, hwndParent);
		}
		return hr;
	}
	STDMETHOD(EnumVerbs)(IEnumOLEVERB **ppEnumOleVerb)
	{
		ATLTRACE(_T("IOleObjectImpl::EnumVerbs\n"));
		_ASSERTE(ppEnumOleVerb);
		if (!ppEnumOleVerb)
			return E_POINTER;
		return OleRegEnumVerbs(T::GetObjectCLSID(), ppEnumOleVerb);
	}
	STDMETHOD(Update)(void)
	{
		ATLTRACE(_T("IOleObjectImpl::Update\n"));
		return S_OK;
	}
	STDMETHOD(IsUpToDate)(void)
	{
		ATLTRACE(_T("IOleObjectImpl::IsUpToDate\n"));
		return S_OK;
	}
	STDMETHOD(GetUserClassID)(CLSID *pClsid)
	{
		ATLTRACE(_T("IOleObjectImpl::GetUserClassID\n"));
		_ASSERTE(pClsid);
		if (!pClsid)
			return E_POINTER;
		*pClsid = T::GetObjectCLSID();
		return S_OK;
	}
	STDMETHOD(GetUserType)(DWORD dwFormOfType, LPOLESTR *pszUserType)
	{
		ATLTRACE(_T("IOleObjectImpl::GetUserType\n"));
		return OleRegGetUserType(T::GetObjectCLSID(), dwFormOfType, pszUserType);
	}
	STDMETHOD(SetExtent)(DWORD dwDrawAspect, SIZEL *psizel)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::SetExtent\n"));
		return pT->IOleObject_SetExtent(dwDrawAspect, psizel);
	}
	STDMETHOD(GetExtent)(DWORD dwDrawAspect, SIZEL *psizel)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::GetExtent\n"));
		if (dwDrawAspect != DVASPECT_CONTENT)
			return E_FAIL;
		if (psizel == NULL)
			return E_POINTER;
		*psizel = pT->m_sizeExtent;
		return S_OK;
	}
	STDMETHOD(Advise)(IAdviseSink *pAdvSink, DWORD *pdwConnection)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::Advise\n"));
		return pT->IOleObject_Advise(pAdvSink, pdwConnection);
	}
	STDMETHOD(Unadvise)(DWORD dwConnection)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::Unadvise\n"));
		HRESULT hRes = E_FAIL;
		if (pT->m_spOleAdviseHolder != NULL)
			hRes = pT->m_spOleAdviseHolder->Unadvise(dwConnection);
		return hRes;
	}
	STDMETHOD(EnumAdvise)(IEnumSTATDATA **ppenumAdvise)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleObjectImpl::EnumAdvise\n"));
		HRESULT hRes = E_FAIL;
		if (pT->m_spOleAdviseHolder != NULL)
			hRes = pT->m_spOleAdviseHolder->EnumAdvise(ppenumAdvise);
		return hRes;
	}
	STDMETHOD(GetMiscStatus)(DWORD dwAspect, DWORD *pdwStatus)
	{
		ATLTRACE(_T("IOleObjectImpl::GetMiscStatus\n"));
		return OleRegGetMiscStatus(T::GetObjectCLSID(), dwAspect, pdwStatus);
	}
	STDMETHOD(SetColorScheme)(LOGPALETTE* /* pLogpal */)
	{
		ATLTRACENOTIMPL(_T("IOleObjectImpl::SetColorScheme"));
	}
};

//local struct used for implementation
#pragma pack(push, 1)
struct _ATL_DLGTEMPLATEEX
{
	WORD dlgVer;
	WORD signature;
	DWORD helpID;
	DWORD exStyle;
	DWORD style;
	WORD cDlgItems;
	short x;
	short y;
	short cx;
	short cy;
};
#pragma pack(pop)

//////////////////////////////////////////////////////////////////////////////
// IPropertyPageImpl
template <class T>
class ATL_NO_VTABLE IPropertyPageImpl
{

public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleControlImpl)

	void SetDirty(BOOL bDirty)
	{
		T* pT = static_cast<T*>(this);
		if (!pT->m_bDirty && bDirty)
			pT->m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY | PROPPAGESTATUS_VALIDATE);
		pT->m_bDirty = bDirty;
	}

	IPropertyPageImpl()
	{
		T* pT = static_cast<T*>(this);
		pT->m_pPageSite = NULL;
		pT->m_size.cx = 0;
		pT->m_size.cy = 0;
		pT->m_dwTitleID = 0;
		pT->m_dwHelpFileID = 0;
		pT->m_dwDocStringID = 0;
		pT->m_dwHelpContext = 0;
		pT->m_ppUnk = NULL;
		pT->m_nObjects = 0;
		pT->m_bDirty = FALSE;
		pT->m_hWnd = NULL;
	}

	~IPropertyPageImpl()
	{
		T* pT = static_cast<T*>(this);
		if (pT->m_pPageSite != NULL)
			pT->m_pPageSite->Release();

		for (UINT i = 0; i < m_nObjects; i++)
			pT->m_ppUnk[i]->Release();

		delete[] pT->m_ppUnk;
	}

	// IPropertyPage
	//
	STDMETHOD(SetPageSite)(IPropertyPageSite *pPageSite)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::SetPageSite\n"));

		if (!pPageSite && pT->m_pPageSite)
		{
			pT->m_pPageSite->Release();
			return S_OK;
		}

		if (!pPageSite && !pT->m_pPageSite)
			return S_OK;

		if (pPageSite && pT->m_pPageSite)
		{
			ATLTRACE(_T("Error : setting page site again with non NULL value\n"));
			return E_UNEXPECTED;
		}

		pT->m_pPageSite = pPageSite;
		pT->m_pPageSite->AddRef();
		return S_OK;
	}
	STDMETHOD(Activate)(HWND hWndParent, LPCRECT pRect, BOOL /* bModal */)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::Activate\n"));

		if (pRect == NULL)
		{
			ATLTRACE(_T("Error : Passed a NULL rect\n"));
			return E_POINTER;
		}

		pT->m_hWnd = pT->Create(hWndParent);
		Move(pRect);

		m_size.cx = pRect->right - pRect->left;
		m_size.cy = pRect->bottom - pRect->top;

		return S_OK;

	}
	STDMETHOD(Deactivate)( void)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::Deactivate\n"));

		if (pT->m_hWnd)
		{
			ATLTRACE(_T("Destroying Dialog\n"));
			if (::IsWindow(pT->m_hWnd))
				pT->DestroyWindow();
			pT->m_hWnd = NULL;
		}

		return S_OK;

	}
	STDMETHOD(GetPageInfo)(PROPPAGEINFO *pPageInfo)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::GetPageInfo\n"));

		if (pPageInfo == NULL)
		{
			ATLTRACE(_T("Error : PROPPAGEINFO passed == NULL\n"));
			return E_POINTER;
		}

		HRSRC hRsrc = FindResource(_Module.GetResourceInstance(),
								   MAKEINTRESOURCE(T::IDD), RT_DIALOG);
		if (hRsrc == NULL)
		{
			ATLTRACE(_T("Could not find resource template\n"));
			return E_UNEXPECTED;
		}

		HGLOBAL hGlob = LoadResource(_Module.GetResourceInstance(), hRsrc);
		DLGTEMPLATE* pTemp = (DLGTEMPLATE*)LockResource(hGlob);
		if (pTemp == NULL)
		{
			ATLTRACE(_T("Could not load resource template\n"));
			return E_UNEXPECTED;
		}
		pT->GetDialogSize(pTemp, &m_size);

		pPageInfo->cb = sizeof(PROPPAGEINFO);
		pPageInfo->pszTitle = LoadStringHelper(pT->m_dwTitleID);
		pPageInfo->size = m_size;
		pPageInfo->pszHelpFile = LoadStringHelper(pT->m_dwHelpFileID);
		pPageInfo->pszDocString = LoadStringHelper(pT->m_dwDocStringID);
		pPageInfo->dwHelpContext = pT->m_dwHelpContext;

		return S_OK;
	}

	STDMETHOD(SetObjects)(ULONG nObjects, IUnknown **ppUnk)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::SetObjects\n"));

		if (ppUnk == NULL)
			return E_POINTER;

		if (pT->m_ppUnk != NULL && pT->m_nObjects > 0)
		{
			for (UINT iObj = 0; iObj < pT->m_nObjects; iObj++)
				pT->m_ppUnk[iObj]->Release();

			delete [] pT->m_ppUnk;
		}

		pT->m_ppUnk = NULL;
		ATLTRY(pT->m_ppUnk = new IUnknown*[nObjects]);

		if (pT->m_ppUnk == NULL)
			return E_OUTOFMEMORY;

		for (UINT i = 0; i < nObjects; i++)
		{
			ppUnk[i]->AddRef();
			pT->m_ppUnk[i] = ppUnk[i];
		}

		pT->m_nObjects = nObjects;

		return S_OK;
	}
	STDMETHOD(Show)(UINT nCmdShow)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::Show\n"));

		if (pT->m_hWnd == NULL)
			return E_UNEXPECTED;

		ShowWindow(pT->m_hWnd, nCmdShow);
		return S_OK;
	}
	STDMETHOD(Move)(LPCRECT pRect)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::Move\n"));

		if (pT->m_hWnd == NULL)
			return E_UNEXPECTED;

		if (pRect == NULL)
			return E_POINTER;

		MoveWindow(pT->m_hWnd, pRect->left, pRect->top, pRect->right - pRect->left,
				 pRect->bottom - pRect->top, TRUE);

		return S_OK;

	}
	STDMETHOD(IsPageDirty)(void)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::IsPageDirty\n"));
		return pT->m_bDirty ? S_OK : S_FALSE;
	}
	STDMETHOD(Apply)(void)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::Apply\n"));
		return S_OK;
	}
	STDMETHOD(Help)(LPCOLESTR pszHelpDir)
	{
		T* pT = static_cast<T*>(this);
		USES_CONVERSION;

		ATLTRACE(_T("IPropertyPageImpl::Help\n"));
		WinHelp(pT->m_hWnd, OLE2CT(pszHelpDir), HELP_CONTEXTPOPUP, NULL);
		return S_OK;
	}
	STDMETHOD(TranslateAccelerator)(MSG *pMsg)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IPropertyPageImpl::TranslateAccelerator\n"));
		if (pMsg == NULL)
			return E_POINTER;
		LRESULT lRes;
		return pT->ProcessWindowMessage(pT->m_hWnd, pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ? S_OK : S_FALSE;
	}

	IPropertyPageSite* m_pPageSite;
	IUnknown** m_ppUnk;
	ULONG m_nObjects;
	SIZE m_size;
	UINT m_dwTitleID;
	UINT m_dwHelpFileID;
	UINT m_dwDocStringID;
	DWORD m_dwHelpContext;
	BOOL m_bDirty;

//methods
public:

	BEGIN_MSG_MAP(IPropertyPageImpl<T>)
		MESSAGE_HANDLER(WM_STYLECHANGING, OnStyleChange)
	END_MSG_MAP()

	LRESULT OnStyleChange(UINT, WPARAM wParam, LPARAM lParam, BOOL&)
	{
		if (wParam == GWL_EXSTYLE)
		{
			LPSTYLESTRUCT lpss = (LPSTYLESTRUCT) lParam;
			lpss->styleNew |= WS_EX_CONTROLPARENT;
			return 0;
		}
		return 1;
	}

	LPOLESTR LoadStringHelper(UINT idRes)
	{
		USES_CONVERSION;

		TCHAR szTemp[_MAX_PATH];
		LPOLESTR sz = (LPOLESTR)CoTaskMemAlloc(_MAX_PATH*sizeof(OLECHAR));
		if (sz == NULL)
			return NULL;
		sz[0] = NULL;

		if (LoadString(_Module.GetResourceInstance(), idRes, szTemp, _MAX_PATH))
			ocscpy(sz, T2OLE(szTemp));
		else
		{
			ATLTRACE(_T("Error : Failed to load string from res\n"));
		}

		return sz;
	}

	void GetDialogSize(const DLGTEMPLATE* pTemplate, SIZE* pSize)
	{
		// If the dialog has a font we use it otherwise we default
		// to the system font.
		if (HasFont(pTemplate))
		{
			TCHAR szFace[LF_FACESIZE];
			WORD  wFontSize = 0;
			GetFont(pTemplate, szFace, &wFontSize);
			GetSizeInDialogUnits(pTemplate, pSize);
			ConvertDialogUnitsToPixels(szFace, wFontSize, pSize);
		}
		else
		{
			GetSizeInDialogUnits(pTemplate, pSize);
			LONG nDlgBaseUnits = GetDialogBaseUnits();
			pSize->cx = MulDiv(pSize->cx, LOWORD(nDlgBaseUnits), 4);
			pSize->cy = MulDiv(pSize->cy, HIWORD(nDlgBaseUnits), 8);
		}
	}

	static void ConvertDialogUnitsToPixels(LPCTSTR pszFontFace, WORD wFontSize, SIZE* pSizePixel)
	{
		// Attempt to create the font to be used in the dialog box
		UINT cxSysChar, cySysChar;
		LOGFONT lf;
		HDC hDC = ::GetDC(NULL);
		int cxDlg = pSizePixel->cx;
		int cyDlg = pSizePixel->cy;

		ZeroMemory(&lf, sizeof(LOGFONT));
		lf.lfHeight = -MulDiv(wFontSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
		lf.lfWeight = FW_NORMAL;
		lf.lfCharSet = DEFAULT_CHARSET;
		lstrcpy(lf.lfFaceName, pszFontFace);

		HFONT hNewFont = CreateFontIndirect(&lf);
		if (hNewFont != NULL)
		{
			TEXTMETRIC  tm;
			SIZE        size;
			HFONT       hFontOld = (HFONT)SelectObject(hDC, hNewFont);
			GetTextMetrics(hDC, &tm);
			cySysChar = tm.tmHeight + tm.tmExternalLeading;
			::GetTextExtentPoint(hDC,
				_T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), 52,
				&size);
			cxSysChar = (size.cx + 26) / 52;
			SelectObject(hDC, hFontOld);
			DeleteObject(hNewFont);
		}
		else
		{
			// Could not create the font so just use the system's values
			cxSysChar = LOWORD(GetDialogBaseUnits());
			cySysChar = HIWORD(GetDialogBaseUnits());
		}
		::ReleaseDC(NULL, hDC);

		// Translate dialog units to pixels
		pSizePixel->cx = MulDiv(cxDlg, cxSysChar, 4);
		pSizePixel->cy = MulDiv(cyDlg, cySysChar, 8);
	}

	static BOOL IsDialogEx(const DLGTEMPLATE* pTemplate)
	{
		return ((_ATL_DLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF;
	}

	static BOOL HasFont(const DLGTEMPLATE* pTemplate)
	{
		return (DS_SETFONT &
			(IsDialogEx(pTemplate) ?
				((_ATL_DLGTEMPLATEEX*)pTemplate)->style : pTemplate->style));
	}

	static BYTE* GetFontSizeField(const DLGTEMPLATE* pTemplate)
	{
		BOOL bDialogEx = IsDialogEx(pTemplate);
		WORD* pw;

		if (bDialogEx)
			pw = (WORD*)((_ATL_DLGTEMPLATEEX*)pTemplate + 1);
		else
			pw = (WORD*)(pTemplate + 1);

		if (*pw == (WORD)-1)        // Skip menu name string or ordinal
			pw += 2; // WORDs
		else
			while(*pw++);

		if (*pw == (WORD)-1)        // Skip class name string or ordinal
			pw += 2; // WORDs
		else
			while(*pw++);

		while (*pw++);          // Skip caption string

		return (BYTE*)pw;
	}

	static BOOL GetFont(const DLGTEMPLATE* pTemplate, TCHAR* pszFace, WORD* pFontSize)
	{
		USES_CONVERSION;
		if (!HasFont(pTemplate))
			return FALSE;

		BYTE* pb = GetFontSizeField(pTemplate);
		*pFontSize = *(WORD*)pb;
		// Skip over font attributes to get to the font name
		pb += sizeof(WORD) * (IsDialogEx(pTemplate) ? 3 : 1);

		_tcscpy(pszFace, W2T((WCHAR*)pb));
		return TRUE;
	}

	static void GetSizeInDialogUnits(const DLGTEMPLATE* pTemplate, SIZE* pSize)
	{
		if (IsDialogEx(pTemplate))
		{
			pSize->cx = ((_ATL_DLGTEMPLATEEX*)pTemplate)->cx;
			pSize->cy = ((_ATL_DLGTEMPLATEEX*)pTemplate)->cy;
		}
		else
		{
			pSize->cx = pTemplate->cx;
			pSize->cy = pTemplate->cy;
		}
	}
};


//////////////////////////////////////////////////////////////////////////////
// IPropertyPage2Impl
template <class T>
class ATL_NO_VTABLE IPropertyPage2Impl : public IPropertyPageImpl<T>
{
public:

	STDMETHOD(EditProperty)(DISPID dispID)
	{
		ATLTRACENOTIMPL(_T("IPropertyPage2Impl::EditProperty\n"));
	}
};



//////////////////////////////////////////////////////////////////////////////
// IPerPropertyBrowsingImpl
template <class T>
class ATL_NO_VTABLE IPerPropertyBrowsingImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPerPropertyBrowsingImpl)

	STDMETHOD(GetDisplayString)(DISPID dispID,BSTR *pBstr)
	{
		ATLTRACE(_T("IPerPropertyBrowsingImpl::GetDisplayString\n"));
		T* pT = static_cast<T*>(this);
		CComVariant var;
		if (FAILED(CComDispatchDriver::GetProperty(pT, dispID, &var)))
		{
			*pBstr = NULL;
			return S_FALSE;
		}

		BSTR bstrTemp = var.bstrVal;
		if (var.vt != VT_BSTR)
		{
			CComVariant varDest;
			if (FAILED(::VariantChangeType(&varDest, &var, VARIANT_NOVALUEPROP, VT_BSTR)))
			{
				*pBstr = NULL;
				return S_FALSE;
			}
			bstrTemp = varDest.bstrVal;
		}
		*pBstr = SysAllocString(bstrTemp);
		return S_OK;
	}

	STDMETHOD(MapPropertyToPage)(DISPID dispID, CLSID *pClsid)
	{
		ATLTRACE(_T("IPerPropertyBrowsingImpl::MapPropertyToPage\n"));
		T* pT = static_cast<T*>(this);
		ATL_PROPMAP_ENTRY* pMap = T::GetPropertyMap();
		_ASSERTE(pMap != NULL);
		for(int i = 0; pMap[i].pclsidPropPage != NULL; i++)
		{
			if (pMap[i].szDesc == NULL)
				continue;
			if (pMap[i].dispid == dispID)
			{
				_ASSERTE(pMap[i].pclsidPropPage != NULL);
				*pClsid = *(pMap[i].pclsidPropPage);
				return S_OK;
			}
		}
		*pClsid = CLSID_NULL;
		return E_INVALIDARG;
	}
	STDMETHOD(GetPredefinedStrings)(DISPID dispID, CALPOLESTR *pCaStringsOut,CADWORD *pCaCookiesOut)
	{
		dispID;
		ATLTRACE(_T("IPerPropertyBrowsingImpl::GetPredefinedStrings\n"));
		if (pCaStringsOut == NULL || pCaCookiesOut == NULL)
			return E_POINTER;

		pCaStringsOut->cElems = 0;
		pCaStringsOut->pElems = NULL;
		pCaCookiesOut->cElems = 0;
		pCaCookiesOut->pElems = NULL;
		return S_OK;
	}
	STDMETHOD(GetPredefinedValue)(DISPID /*dispID*/, DWORD /*dwCookie*/, VARIANT* /*pVarOut*/)
	{
		ATLTRACENOTIMPL(_T("IPerPropertyBrowsingImpl::GetPredefinedValue"));
	}
};

//////////////////////////////////////////////////////////////////////////////
// IViewObjectExImpl
template <class T>
class ATL_NO_VTABLE IViewObjectExImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IViewObjectExImpl)

	// IViewObject
	//
	STDMETHOD(Draw)(DWORD dwDrawAspect, LONG lindex, void *pvAspect,
					DVTARGETDEVICE *ptd, HDC hicTargetDev, HDC hdcDraw,
					LPCRECTL prcBounds, LPCRECTL prcWBounds,
					BOOL (__stdcall * /*pfnContinue*/)(DWORD dwContinue),
					DWORD /*dwContinue*/)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IViewObjectExImpl::Draw\n"));
		return pT->IViewObject_Draw(dwDrawAspect, lindex, pvAspect, ptd, hicTargetDev, hdcDraw,
			prcBounds, prcWBounds);
	}

	STDMETHOD(GetColorSet)(DWORD /* dwDrawAspect */,LONG /* lindex */, void* /* pvAspect */, DVTARGETDEVICE* /* ptd */, HDC /* hicTargetDev */, LOGPALETTE** /* ppColorSet */)
	{
		ATLTRACENOTIMPL(_T("IViewObjectExImpl::GetColorSet"));
	}
	STDMETHOD(Freeze)(DWORD /* dwDrawAspect */, LONG /* lindex */, void* /* pvAspect */,DWORD* /* pdwFreeze */)
	{
		ATLTRACENOTIMPL(_T("IViewObjectExImpl::Freeze"));
	}
	STDMETHOD(Unfreeze)(DWORD /* dwFreeze */)
	{
		ATLTRACENOTIMPL(_T("IViewObjectExImpl::Unfreeze"));
	}
	STDMETHOD(SetAdvise)(DWORD /* aspects */, DWORD /* advf */, IAdviseSink* pAdvSink)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IViewObjectExImpl::SetAdvise\n"));
		pT->m_spAdviseSink = pAdvSink;
		return S_OK;
	}
	STDMETHOD(GetAdvise)(DWORD* /* pAspects */, DWORD* /* pAdvf */, IAdviseSink** ppAdvSink)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IViewObjectExImpl::GetAdvise\n"));
		if (ppAdvSink != NULL)
		{
			*ppAdvSink = pT->m_spAdviseSink;
			if (pT->m_spAdviseSink)
				pT->m_spAdviseSink->AddRef();
		}
		return S_OK;
	}

	// IViewObject2
	//
	STDMETHOD(GetExtent)(DWORD /* dwDrawAspect */, LONG /* lindex */, DVTARGETDEVICE* /* ptd */, LPSIZEL lpsizel)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IViewObjectExImpl::GetExtent\n"));
		*lpsizel = pT->m_sizeExtent;
		return S_OK;
	}

	// IViewObjectEx
	//
	STDMETHOD(GetRect)(DWORD /* dwAspect */, LPRECTL /* pRect */)
	{
		ATLTRACENOTIMPL(_T("IViewObjectExImpl::GetRect"));
	}
	STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
	{
		ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
		*pdwStatus =
//          VIEWSTATUS_DVASPECTOPAQUE | VIEWSTATUS_DVASPECTTRANSPARENT |
//          VIEWSTATUS_SOLIDBKGND |
			VIEWSTATUS_OPAQUE;

		return S_OK;
	}
	STDMETHOD(QueryHitPoint)(DWORD dwAspect, LPCRECT pRectBounds, POINT ptlLoc, LONG /* lCloseHint */, DWORD *pHitResult)
	{
		ATLTRACE(_T("IViewObjectExImpl::QueryHitPoint\n"));
		if (dwAspect == DVASPECT_CONTENT)
		{
			*pHitResult = PtInRect(pRectBounds, ptlLoc) ? HITRESULT_HIT : HITRESULT_OUTSIDE;
			return S_OK;
		}
		ATLTRACE(_T("Wrong DVASPECT\n"));
		return E_FAIL;
	}
	STDMETHOD(QueryHitRect)(DWORD dwAspect, LPCRECT pRectBounds, LPCRECT prcLoc, LONG /* lCloseHint */, DWORD* pHitResult)
	{
		ATLTRACE(_T("IViewObjectExImpl::QueryHitRect\n"));
		if (dwAspect == DVASPECT_CONTENT)
		{
			RECT rc;
			*pHitResult = UnionRect(&rc, pRectBounds, prcLoc) ? HITRESULT_HIT : HITRESULT_OUTSIDE;
			return S_OK;
		}
		ATLTRACE(_T("Wrong DVASPECT\n"));
		return E_FAIL;
	}
	STDMETHOD(GetNaturalExtent)(DWORD dwAspect, LONG /* lindex */, DVTARGETDEVICE* /* ptd */, HDC /* hicTargetDev */, DVEXTENTINFO* pExtentInfo , LPSIZEL psizel)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IViewObjectExImpl::GetNaturalExtent\n"));
		HRESULT hRes = E_FAIL;
		if (dwAspect == DVASPECT_CONTENT)
		{
			if (pExtentInfo->dwExtentMode == DVEXTENT_CONTENT)
			{
				*psizel = pT->m_sizeNatural;
				hRes = S_OK;
			}
		}
		return hRes;
	}

public:
};

//////////////////////////////////////////////////////////////////////////////
// IOleInPlaceObjectWindowlessImpl
//
template <class T>
class ATL_NO_VTABLE IOleInPlaceObjectWindowlessImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleInPlaceObjectWindowlessImpl)

	// IOleWindow
	//

	// Change IOleInPlaceActiveObject::GetWindow as well
	STDMETHOD(GetWindow)(HWND* phwnd)
	{
		ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::GetWindow\n"));
		T* pT = static_cast<T*>(this);
		HRESULT hRes = E_POINTER;

		if (pT->m_bWasOnceWindowless)
			return E_FAIL;

		if (phwnd != NULL)
		{
			*phwnd = pT->m_hWnd;
			hRes = (*phwnd == NULL) ? E_UNEXPECTED : S_OK;
		}
		return hRes;
	}
	STDMETHOD(ContextSensitiveHelp)(BOOL /* fEnterMode */)
	{
		ATLTRACENOTIMPL(_T("IOleInPlaceObjectWindowlessImpl::ContextSensitiveHelp"));
	}

	// IOleInPlaceObject
	//
	STDMETHOD(InPlaceDeactivate)(void)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::InPlaceDeactivate\n"));
		return pT->IOleInPlaceObject_InPlaceDeactivate();
	}
	STDMETHOD(UIDeactivate)(void)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::UIDeactivate\n"));
		return pT->IOleInPlaceObject_UIDeactivate();
	}
	STDMETHOD(SetObjectRects)(LPCRECT prcPos,LPCRECT prcClip)
	{
		T* pT = static_cast<T*>(this);
		ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::SetObjectRects\n"));
		return pT->IOleInPlaceObject_SetObjectRects(prcPos, prcClip);
	}
	STDMETHOD(ReactivateAndUndo)(void)
	{
		ATLTRACENOTIMPL(_T("IOleInPlaceObjectWindowlessImpl::ReactivateAndUndo"));
	}

	// IOleInPlaceObjectWindowless
	//
	STDMETHOD(OnWindowMessage)(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
	{
		ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::OnWindowMessage\n"));
		T* pT = static_cast<T*>(this);
		return pT->ProcessWindowMessage(pT->m_hWnd, msg, wParam, lParam, *plResult);
	}

	STDMETHOD(GetDropTarget)(IDropTarget** /* ppDropTarget */)
	{
		ATLTRACENOTIMPL(_T("IOleInPlaceObjectWindowlessImpl::GetDropTarget"));
	}
};


//////////////////////////////////////////////////////////////////////////////
// IOleInPlaceActiveObjectImpl
//
template <class T>
class ATL_NO_VTABLE IOleInPlaceActiveObjectImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleInPlaceActiveObjectImpl)

	// IOleWindow
	//

	// Change IOleInPlaceObjectWindowless::GetWindow as well
	STDMETHOD(GetWindow)(HWND *phwnd)
	{
		ATLTRACE(_T("IOleInPlaceActiveObjectImpl::GetWindow\n"));
		T* pT = static_cast<T*>(this);
		HRESULT hRes = E_POINTER;

		if (pT->m_bWasOnceWindowless)
			return E_FAIL;

		if (phwnd != NULL)
		{
			*phwnd = pT->m_hWnd;
			hRes = (*phwnd == NULL) ? E_UNEXPECTED : S_OK;
		}
		return hRes;
	}
	STDMETHOD(ContextSensitiveHelp)(BOOL /* fEnterMode */)
	{
		ATLTRACENOTIMPL(_T("IOleInPlaceActiveObjectImpl::ContextSensitiveHelp"));
	}

	// IOleInPlaceActiveObject
	//
	STDMETHOD(TranslateAccelerator)(LPMSG /* lpmsg */)
	{
		ATLTRACE(_T("IOleInPlaceActiveObjectImpl::TranslateAccelerator\n"));
		return E_NOTIMPL;
	}
	STDMETHOD(OnFrameWindowActivate)(BOOL /* fActivate */)
	{
		ATLTRACE(_T("IOleInPlaceActiveObjectImpl::OnFrameWindowActivate\n"));
		return S_OK;
	}
	STDMETHOD(OnDocWindowActivate)(BOOL /* fActivate */)
	{
		ATLTRACE(_T("IOleInPlaceActiveObjectImpl::OnDocWindowActivate\n"));
		return S_OK;
	}
	STDMETHOD(ResizeBorder)(LPCRECT /* prcBorder */, IOleInPlaceUIWindow* /* pUIWindow */, BOOL /* fFrameWindow */)
	{
		ATLTRACE(_T("IOleInPlaceActiveObjectImpl::ResizeBorder\n"));
		return S_OK;
	}
	STDMETHOD(EnableModeless)(BOOL /* fEnable */)
	{
		ATLTRACE(_T("IOleInPlaceActiveObjectImpl::EnableModeless\n"));
		return S_OK;
	}
};


//////////////////////////////////////////////////////////////////////////////
// ISpecifyPropertyPagesImpl
template <class T>
class ATL_NO_VTABLE ISpecifyPropertyPagesImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(ISpecifyPropertyPagesImpl)

	// ISpecifyPropertyPages
	//
	STDMETHOD(GetPages)(CAUUID* pPages)
	{
		ATLTRACE(_T("ISpecifyPropertyPagesImpl::GetPages\n"));
		T* pT = static_cast<T*>(this);
		ATL_PROPMAP_ENTRY* pMap = T::GetPropertyMap();
		return pT->ISpecifyPropertyPages_GetPages(pPages, pMap);
	}
};

//////////////////////////////////////////////////////////////////////////////
// IPointerInactiveImpl
template <class T>
class ATL_NO_VTABLE IPointerInactiveImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPointerInactiveImpl)

	// IPointerInactive
	//
	STDMETHOD(GetActivationPolicy)(DWORD *pdwPolicy)
	{
		ATLTRACENOTIMPL(_T("IPointerInactiveImpl::GetActivationPolicy"));
	}
	STDMETHOD(OnInactiveMouseMove)(LPCRECT pRectBounds, long x, long y, DWORD dwMouseMsg)
	{
		ATLTRACENOTIMPL(_T("IPointerInactiveImpl::OnInactiveMouseMove"));
	}
	STDMETHOD(OnInactiveSetCursor)(LPCRECT pRectBounds, long x, long y, DWORD dwMouseMsg, BOOL fSetAlways)
	{
		ATLTRACENOTIMPL(_T("IPointerInactiveImpl::OnInactiveSetCursor"));
	}
};

//////////////////////////////////////////////////////////////////////////////
// IRunnableObjectImpl
template <class T>
class ATL_NO_VTABLE IRunnableObjectImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IRunnableObjectImpl)

	// IRunnableObject
	//
	STDMETHOD(GetRunningClass)(LPCLSID lpClsid)
	{
		ATLTRACE(_T("IRunnableObjectImpl::GetRunningClass\n"));
		T* pT = static_cast<T*>(this);
		*lpClsid = GUID_NULL;
		return E_UNEXPECTED;
	}
	STDMETHOD(Run)(LPBINDCTX)
	{
		ATLTRACE(_T("IRunnableObjectImpl::Run\n"));
		return S_OK;
	}
	virtual BOOL STDMETHODCALLTYPE IsRunning()
	{
		ATLTRACE(_T("IRunnableObjectImpl::IsRunning\n"));
		return TRUE;
	}
	STDMETHOD(LockRunning)(BOOL /*fLock*/, BOOL /*fLastUnlockCloses*/)
	{
		ATLTRACE(_T("IRunnableObjectImpl::LockRunning\n"));
		return S_OK;
	}
	STDMETHOD(SetContainedObject)(BOOL /*fContained*/)
	{
		ATLTRACE(_T("IRunnableObjectImpl::SetContainedObject\n"));
		return S_OK;
	}
};


//////////////////////////////////////////////////////////////////////////////
// IDataObjectImpl
template <class T>
class ATL_NO_VTABLE IDataObjectImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IDataObjectImpl)

	// IDataObject
	//
	STDMETHOD(GetData)(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
	{
		ATLTRACE(_T("IDataObjectImpl::GetData\n"));
		T* pT = (T*) this;
		return pT->IDataObject_GetData(pformatetcIn, pmedium);
	}
	STDMETHOD(GetDataHere)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */)
	{
		ATLTRACENOTIMPL(_T("IDataObjectImpl::GetDataHere"));
	}
	STDMETHOD(QueryGetData)(FORMATETC* /* pformatetc */)
	{
		ATLTRACENOTIMPL(_T("IDataObjectImpl::QueryGetData"));
	}
	STDMETHOD(GetCanonicalFormatEtc)(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */)
	{
		ATLTRACENOTIMPL(_T("IDataObjectImpl::GetCanonicalFormatEtc"));
	}
	STDMETHOD(SetData)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */)
	{
		ATLTRACENOTIMPL(_T("IDataObjectImpl::SetData"));
	}
	STDMETHOD(EnumFormatEtc)(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */)
	{
		ATLTRACENOTIMPL(_T("IDataObjectImpl::EnumFormatEtc"));
	}
	STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink,
		DWORD *pdwConnection)
	{
		ATLTRACE(_T("IDataObjectImpl::DAdvise\n"));
		T* pT = static_cast<T*>(this);
		HRESULT hr = S_OK;
		if (pT->m_spDataAdviseHolder == NULL)
			hr = CreateDataAdviseHolder(&pT->m_spDataAdviseHolder);

		if (hr == S_OK)
			hr = pT->m_spDataAdviseHolder->Advise((IDataObject*)this, pformatetc, advf, pAdvSink, pdwConnection);

		return hr;
	}
	STDMETHOD(DUnadvise)(DWORD dwConnection)
	{
		ATLTRACE(_T("IDataObjectImpl::DUnadvise\n"));
		T* pT = static_cast<T*>(this);
		HRESULT hr = S_OK;
		if (pT->m_spDataAdviseHolder == NULL)
			hr = OLE_E_NOCONNECTION;
		else
			hr = pT->m_spDataAdviseHolder->Unadvise(dwConnection);
		return hr;
	}
	STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise)
	{
		ATLTRACE(_T("IDataObjectImpl::EnumDAdvise\n"));
		T* pT = static_cast<T*>(this);
		HRESULT hr = E_FAIL;
		if (pT->m_spDataAdviseHolder != NULL)
			hr = pT->m_spDataAdviseHolder->EnumAdvise(ppenumAdvise);
		return hr;
	}
};

//////////////////////////////////////////////////////////////////////////////
// IPropertyNotifySinkCP
template <class T, class CDV = CComDynamicUnkArray >
class ATL_NO_VTABLE IPropertyNotifySinkCP :
	public IConnectionPointImpl<T, &IID_IPropertyNotifySink, CDV>
{
public:
	typedef CFirePropNotifyEvent _ATL_PROP_NOTIFY_EVENT_CLASS;
};


//////////////////////////////////////////////////////////////////////////////
// IObjectSafety
//

template <class T>
class ATL_NO_VTABLE IObjectSafetyImpl
{
public:
	IObjectSafetyImpl()
	{
		m_dwSafety = 0;
	}

	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IObjectSafetyImpl)

	// IObjectSafety
	//
	STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
	{
		ATLTRACE(_T("IObjectSafetyImpl::GetInterfaceSafetyOptions\n"));
		if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL)
			return E_POINTER;
		HRESULT hr = S_OK;
		if (riid == IID_IDispatch)
		{
			*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
			*pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
		}
		else
		{
			*pdwSupportedOptions = 0;
			*pdwEnabledOptions = 0;
			hr = E_NOINTERFACE;
		}
		return hr;
	}
	STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
	{
		ATLTRACE(_T("IObjectSafetyImpl::SetInterfaceSafetyOptions\n"));
		// If we're being asked to set our safe for scripting option then oblige
		if (riid == IID_IDispatch)
		{
			// Store our current safety level to return in GetInterfaceSafetyOptions
			m_dwSafety = dwEnabledOptions & dwOptionSetMask;
			return S_OK;
		}
		return E_NOINTERFACE;
	}

	DWORD m_dwSafety;
};


template <class T>
class ATL_NO_VTABLE IOleLinkImpl
{
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleLinkImpl)

	STDMETHOD(SetUpdateOptions)(DWORD /* dwUpdateOpt */)
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::SetUpdateOptions"));
	}

	STDMETHOD(GetUpdateOptions)(DWORD* /* pdwUpdateOpt */)
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::GetUpdateOptions"));
	}

	STDMETHOD(SetSourceMoniker)(IMoniker* /* pmk */, REFCLSID /* rclsid */)
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::SetSourceMoniker"));
	}

	STDMETHOD(GetSourceMoniker)(IMoniker** /* ppmk */)
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::GetSourceMoniker"));
	};

	STDMETHOD(SetSourceDisplayName)(LPCOLESTR /* pszStatusText */)
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::SetSourceDisplayName"));
	}

	STDMETHOD(GetSourceDisplayName)(LPOLESTR *ppszDisplayName)
	{
		ATLTRACE(_T("IOleLink::GetSourceDisplayName\n"));
		*ppszDisplayName = NULL;
		return E_FAIL;
	}

	STDMETHOD(BindToSource)(DWORD /* bindflags */, IBindCtx* /* pbc */)
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::BindToSource\n"));
	};

	STDMETHOD(BindIfRunning)()
	{
		ATLTRACE(_T("IOleLinkImpl::BindIfRunning\n"));
		return S_OK;
	};

	STDMETHOD(GetBoundSource)(IUnknown** /* ppunk */)
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::GetBoundSource"));
	};

	STDMETHOD(UnbindSource)()
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::UnbindSource"));
	};

	STDMETHOD(Update)(IBindCtx* /* pbc */)
	{
		ATLTRACENOTIMPL(_T("IOleLinkImpl::Update"));
	};
};


template <class T>
class ATL_NO_VTABLE IBindStatusCallbackImpl
{
public:
	// IUnknown
	//
	STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
	_ATL_DEBUG_ADDREF_RELEASE_IMPL(IBindStatusCallbackImpl)

	// IBindStatusCallback
	//
	STDMETHOD(OnStartBinding)(DWORD /* dwReserved */, IBinding *pBinding)
	{
		ATLTRACE(_T("IBindStatusCallbackImpl::OnStartBinding\n"));
		return S_OK;
	}

	STDMETHOD(GetPriority)(LONG* /* pnPriority */)
	{
		ATLTRACENOTIMPL(_T("IBindStatusCallbackImpl::GetPriority"));
	}

	STDMETHOD(OnLowResource)(DWORD /* reserved */)
	{
		ATLTRACE(_T("IBindStatusCallbackImpl::OnLowResource\n"));
		return S_OK;
	}

	STDMETHOD(OnProgress)(ULONG /* ulProgress */, ULONG /* ulProgressMax */, ULONG /* ulStatusCode */, LPCWSTR /* szStatusText */)
	{
		ATLTRACE(_T("IBindStatusCallbackImpl::OnProgress\n"));
		return S_OK;
	}

	STDMETHOD(OnStopBinding)(HRESULT /* hresult */, LPCWSTR /* szError */)
	{
		ATLTRACE(_T("IBindStatusCallbackImpl::OnStopBinding\n"));
		return S_OK;
	}

	STDMETHOD(GetBindInfo)(DWORD* /* pgrfBINDF */, BINDINFO* /* pBindInfo */)
	{
		ATLTRACE(_T("IBindStatusCallbackImpl::GetBindInfo\n"));
		return S_OK;
	}

	STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
	{
		ATLTRACE(_T("IBindStatusCallbackImpl::OnDataAvailable\n"));
		return S_OK;
	}

	STDMETHOD(OnObjectAvailable)(REFIID /* riid */, IUnknown* /* punk */)
	{
		ATLTRACE(_T("IBindStatusCallbackImpl::OnObjectAvailable\n"));
		return S_OK;
	}
};


template <class T>
class ATL_NO_VTABLE CBindStatusCallback :
	public CComObjectRootEx<T::_ThreadModel::ThreadModelNoCS>,
	public IBindStatusCallbackImpl<T>
{
	typedef void (T::*ATL_PDATAAVAILABLE)(CBindStatusCallback<T>* pbsc, BYTE* pBytes, DWORD dwSize);

public:

BEGIN_COM_MAP(CBindStatusCallback<T>)
	COM_INTERFACE_ENTRY_IID(IID_IBindStatusCallback, IBindStatusCallbackImpl<T>)
END_COM_MAP()


	CBindStatusCallback()
	{
		m_pT = NULL;
		m_pFunc = NULL;
	}
	~CBindStatusCallback()
	{
		ATLTRACE(_T("~CBindStatusCallback\n"));
	}

	STDMETHOD(OnStartBinding)(DWORD dwReserved, IBinding *pBinding)
	{
		ATLTRACE(_T("CBindStatusCallback::OnStartBinding\n"));
		m_spBinding = pBinding;
		return S_OK;
	}

	STDMETHOD(GetPriority)(LONG *pnPriority)
	{
		ATLTRACENOTIMPL(_T("CBindStatusCallback::GetPriority"));
	}

	STDMETHOD(OnLowResource)(DWORD reserved)
	{
		ATLTRACENOTIMPL(_T("CBindStatusCallback::OnLowResource"));
	}

	STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
	{
		ATLTRACENOTIMPL(_T("CBindStatusCallback::OnProgress"));
	}

	STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR szError)
	{
		ATLTRACE(_T("CBindStatusCallback::OnStopBinding\n"));
		m_spBinding.Release();
		return S_OK;
	}

	STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo)
	{
		ATLTRACE(_T("CBindStatusCallback::GetBindInfo\n"));
		*pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE |
			BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE;
		pbindInfo->cbSize = sizeof(BINDINFO);
		pbindInfo->szExtraInfo = NULL;
		memset(&pbindInfo->stgmedData, 0, sizeof(STGMEDIUM));
		pbindInfo->grfBindInfoF = 0;
		pbindInfo->dwBindVerb = BINDVERB_GET;
		pbindInfo->szCustomVerb = NULL;
		return S_OK;
	}

	STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
	{
		ATLTRACE(_T("CBindStatusCallback::OnDataAvailable\n"));
		HRESULT hr = S_OK;

		// Get the Stream passed
		if (BSCF_FIRSTDATANOTIFICATION & grfBSCF)
		{
			if (!m_spStream && pstgmed->tymed == TYMED_ISTREAM)
			{
				m_spStream = pstgmed->pstm;
				if (m_spStream)
					m_spStream->AddRef();
			}
		}

		DWORD dwRead = dwSize - m_dwTotalRead; // Minimum amount available that hasn't been read
		DWORD dwActuallyRead = 0;            // Placeholder for amount read during this pull

		// If there is some data to be read then go ahead and read them
		if (m_spStream)
		{
			if (dwRead > 0)
			{
				BYTE* pBytes = NULL;
				ATLTRY(pBytes = new BYTE[dwRead + 1]);
				if (pBytes == NULL)
					return S_FALSE;
				hr = m_spStream->Read(pBytes, dwRead, &dwActuallyRead);
				if (SUCCEEDED(hr))
				{
					pBytes[dwActuallyRead] = 0;
					if (dwActuallyRead>0)
					{
						(m_pT->*m_pFunc)(this, pBytes, dwActuallyRead);
						m_dwTotalRead += dwActuallyRead;
					}
				}
				delete[] pBytes;
			}
		}

		if (BSCF_LASTDATANOTIFICATION & grfBSCF)
			m_spStream.Release();
		return hr;
	}

	STDMETHOD(OnObjectAvailable)(REFIID riid, IUnknown *punk)
	{
		ATLTRACENOTIMPL(_T("CBindStatusCallback::OnObjectAvailable"));
	}

	HRESULT _StartAsyncDownload(BSTR bstrURL, IUnknown* pUnkContainer, BOOL bRelative)
	{
		m_dwTotalRead = 0;
		m_dwAvailableToRead = 0;
		HRESULT hr = S_OK;
		CComQIPtr<IServiceProvider, &IID_IServiceProvider> spServiceProvider(pUnkContainer);
		CComPtr<IBindHost> spBindHost;
		if (spServiceProvider)
			spServiceProvider->QueryService(SID_IBindHost, IID_IBindHost, (void**)&spBindHost);

		if (spBindHost == NULL)
		{
			if (bRelative)
				return E_NOINTERFACE;  // relative asked for, but no IBindHost
			hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);
			if (SUCCEEDED(hr))
				hr = CreateBindCtx(0, &m_spBindCtx);

			if (SUCCEEDED(hr))
				hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);
			else
				m_spMoniker.Release();

			if (SUCCEEDED(hr))
			{
				IStream* pStream;
				hr = m_spMoniker->BindToStorage(m_spBindCtx, 0, IID_IStream, (void**)&pStream);
			}
		}
		else
		{
			hr = CreateBindCtx(0, &m_spBindCtx);
			if (SUCCEEDED(hr))
				hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);

			if (SUCCEEDED(hr))
			{
				if (bRelative)
					hr = spBindHost->CreateMoniker(bstrURL, m_spBindCtx, &m_spMoniker, 0);
				else
					hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);
			}

			if (SUCCEEDED(hr))
			{
				IStream* pStream;
				hr = spBindHost->MonikerBindToStorage(m_spMoniker, NULL, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), IID_IStream, (void**)&pStream);
				ATLTRACE(_T("Bound"));
			}
		}
		return hr;
	}

	HRESULT StartAsyncDownload(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
	{
		m_pT = pT;
		m_pFunc = pFunc;
		return  _StartAsyncDownload(bstrURL, pUnkContainer, bRelative);
	}

	static HRESULT Download(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
	{
		CComObject<CBindStatusCallback<T> > *pbsc;
		HRESULT hRes = CComObject<CBindStatusCallback<T> >::CreateInstance(&pbsc);
		if (FAILED(hRes))
			return hRes;
		return pbsc->StartAsyncDownload(pT, pFunc, bstrURL, pUnkContainer, bRelative);
	}

	CComPtr<IMoniker> m_spMoniker;
	CComPtr<IBindCtx> m_spBindCtx;
	CComPtr<IBinding> m_spBinding;
	CComPtr<IStream> m_spStream;
	T* m_pT;
	ATL_PDATAAVAILABLE m_pFunc;
	DWORD m_dwTotalRead;
	DWORD m_dwAvailableToRead;
};

#define IMPLEMENT_STOCKPROP(type, fname, pname, dispid) \
	HRESULT STDMETHODCALLTYPE put_##fname(type pname) \
	{ \
		T* pT = (T*) this; \
		if (pT->FireOnRequestEdit(dispid) == S_FALSE) \
			return S_FALSE; \
		pT->m_##pname = pname; \
		pT->FireOnChanged(dispid); \
		pT->FireViewChange(); \
		return S_OK; \
	} \
	HRESULT STDMETHODCALLTYPE get_##fname(type* p##pname) \
	{ \
		T* pT = (T*) this; \
		*p##pname = pT->m_##pname; \
		return S_OK; \
	}

#define IMPLEMENT_SMARTPTR_STOCKPROP(type, fname, pname, dispid) \
	HRESULT STDMETHODCALLTYPE put_##fname(type pname) \
	{ \
		T* pT = (T*) this; \
		if (pT->FireOnRequestEdit(dispid) == S_FALSE) \
			return S_FALSE; \
		pT->m_##pname = pname; \
		pT->FireOnChanged(dispid); \
		pT->FireViewChange(); \
		return S_OK; \
	} \
	HRESULT STDMETHODCALLTYPE get_##fname(type* p##pname) \
	{ \
		T* pT = (T*) this; \
		*p##pname = pT->m_##pname; \
		if (*p##pname != NULL)\
			(*p##pname)->AddRef();\
		return S_OK; \
	}

#define IMPLEMENT_BSTR_STOCKPROP(fname, pname, dispid) \
	HRESULT STDMETHODCALLTYPE put_##fname(BSTR pname) \
	{ \
		T* pT = (T*) this; \
		if (pT->FireOnRequestEdit(dispid) == S_FALSE) \
			return S_FALSE; \
		*(&(pT->m_##pname)) = SysAllocString(pname); \
		pT->FireOnChanged(dispid); \
		pT->FireViewChange(); \
		return S_OK; \
	} \
	HRESULT STDMETHODCALLTYPE get_##fname(BSTR* p##pname) \
	{ \
		T* pT = (T*) this; \
		*p##pname = SysAllocString(pT->m_##pname); \
		return S_OK; \
	}

template < class T, class InterfaceName, const IID* piid, const GUID* plibid>
class ATL_NO_VTABLE CStockPropImpl : public IDispatchImpl< InterfaceName, piid, plibid >
{
public:
	IMPLEMENT_SMARTPTR_STOCKPROP(IFontDisp*, Font, pFont, DISPID_FONT)
	IMPLEMENT_SMARTPTR_STOCKPROP(IPictureDisp*, MouseIcon, pMouseIcon, DISPID_MOUSEICON)
	IMPLEMENT_SMARTPTR_STOCKPROP(IPictureDisp*, Picture, pPicture, DISPID_PICTURE)
	IMPLEMENT_STOCKPROP(OLE_COLOR, BackColor, clrBackColor, DISPID_BACKCOLOR)
	IMPLEMENT_STOCKPROP(OLE_COLOR, BorderColor, clrBorderColor, DISPID_BORDERCOLOR)
	IMPLEMENT_STOCKPROP(OLE_COLOR, FillColor, clrFillColor, DISPID_FILLCOLOR)
	IMPLEMENT_STOCKPROP(OLE_COLOR, ForeColor, clrForeColor, DISPID_FORECOLOR)
	IMPLEMENT_STOCKPROP(VARIANT_BOOL, AutoSize, bAutoSize, DISPID_AUTOSIZE)
	IMPLEMENT_STOCKPROP(VARIANT_BOOL, Valid, bValid, DISPID_VALID)
	IMPLEMENT_STOCKPROP(VARIANT_BOOL, Enabled, bEnabled, DISPID_ENABLED)
	IMPLEMENT_STOCKPROP(VARIANT_BOOL, TabStop, bTabStop, DISPID_TABSTOP)
	IMPLEMENT_STOCKPROP(VARIANT_BOOL, BorderVisible, bBorderVisible, DISPID_BORDERVISIBLE)
	IMPLEMENT_BSTR_STOCKPROP(Text, bstrText, DISPID_TEXT)
	IMPLEMENT_BSTR_STOCKPROP(Caption, bstrCaption, DISPID_CAPTION)
	HRESULT STDMETHODCALLTYPE put_Window(long /*hWnd*/)
	{
		return E_FAIL;
	}
	HRESULT STDMETHODCALLTYPE get_Window(long* phWnd)
	{
		T* pT = (T*) this;
		*phWnd = (long)pT->m_hWnd;
		return S_OK;
	}
	IMPLEMENT_STOCKPROP(long, BackStyle, nBackStyle, DISPID_BACKSTYLE)
	IMPLEMENT_STOCKPROP(long, BorderStyle, nBorderStyle, DISPID_BORDERSTYLE)
	IMPLEMENT_STOCKPROP(long, BorderWidth, nBorderWidth, DISPID_BORDERWIDTH)
	IMPLEMENT_STOCKPROP(long, DrawMode, nDrawMode, DISPID_DRAWMODE)
	IMPLEMENT_STOCKPROP(long, DrawStyle, nDrawStyle, DISPID_DRAWSTYLE)
	IMPLEMENT_STOCKPROP(long, DrawWidth, nDrawWidth, DISPID_DRAWWIDTH)
	IMPLEMENT_STOCKPROP(long, FillStyle, nFillStyle, DISPID_FILLSTYLE)
	IMPLEMENT_STOCKPROP(long, Appearance, nAppearance, DISPID_APPEARANCE)
	IMPLEMENT_STOCKPROP(long, MousePointer, nMousePointer, DISPID_MOUSEPOINTER)
	IMPLEMENT_STOCKPROP(long, ReadyState, nReadyState, DISPID_READYSTATE)
};

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

#endif // __ATLCTL_H__
