www.pudn.com > vistaListCtrlDemo.zip > DragDrop.h


 
#pragma once 
 
#include  
#include  
 
using namespace std; 
 
class CEnumFormatEtc : public IEnumFORMATETC 
{ 
public: 
	CEnumFormatEtc( const vector < FORMATETC >& vFormatEtc ) 
	{ 
		m_nRefCount = 0; 
		m_nIndex = 0; 
		m_vFormatEtc = vFormatEtc; 
	} 
	 
protected: 
	vector < FORMATETC > m_vFormatEtc; 
	int m_nRefCount; 
	int m_nIndex; 
 
public: 
	// IUnknown members 
	STDMETHOD(QueryInterface)( REFIID refiid, void FAR* FAR* ppvObject ) 
	{ 
		*ppvObject = ( refiid == IID_IUnknown || refiid == IID_IEnumFORMATETC ) ? this : NULL; 
		 
		if ( *ppvObject != NULL ) 
			( (LPUNKNOWN)*ppvObject )->AddRef(); 
		 
		return *ppvObject == NULL ? E_NOINTERFACE : S_OK; 
	} 
	 
	STDMETHOD_(ULONG, AddRef)( void ) 
	{ 
		return ++m_nRefCount; 
	} 
	 
	STDMETHOD_(ULONG, Release)( void ) 
	{ 
		int nRefCount = --m_nRefCount; 
		if ( nRefCount == 0 ) 
			delete this; 
		return nRefCount; 
	} 
	 
	// IEnumFORMATETC members 
	STDMETHOD(Next)( ULONG celt, LPFORMATETC lpFormatEtc, ULONG FAR *pceltFetched ) 
	{ 
		if ( pceltFetched != NULL ) 
			*pceltFetched=0; 
 
		ULONG cReturn = celt; 
 
		if ( celt <= 0 || lpFormatEtc == NULL || m_nIndex >= (int)m_vFormatEtc.size() ) 
			return S_FALSE; 
 
		if ( pceltFetched == NULL && celt != 1 ) // pceltFetched can be NULL only for 1 item request 
			return S_FALSE; 
 
		while ( m_nIndex < (int)m_vFormatEtc.size() && cReturn > 0 ) 
		{ 
			*lpFormatEtc++ = m_vFormatEtc[ m_nIndex++ ]; 
			cReturn--; 
		} 
		 
		if ( pceltFetched != NULL ) 
			*pceltFetched = celt - cReturn; 
 
		return cReturn == 0 ? S_OK : S_FALSE; 
	} 
	 
	STDMETHOD(Skip)( ULONG celt ) 
	{ 
		if ( ( m_nIndex + (int)celt ) >= (int)m_vFormatEtc.size() ) 
			return S_FALSE; 
		m_nIndex += celt; 
		return S_OK; 
	} 
	 
	STDMETHOD(Reset)( void ) 
	{ 
		m_nIndex = 0; 
		return S_OK; 
	} 
	 
	STDMETHOD(Clone)( IEnumFORMATETC FAR * FAR* ppCloneEnumFormatEtc ) 
	{ 
		if ( ppCloneEnumFormatEtc == NULL ) 
			return E_POINTER; 
 
		*ppCloneEnumFormatEtc = new CEnumFormatEtc( m_vFormatEtc ); 
		( (CEnumFormatEtc*)*ppCloneEnumFormatEtc )->AddRef(); 
		( (CEnumFormatEtc*)*ppCloneEnumFormatEtc )->m_nIndex = m_nIndex; 
		 
		return S_OK; 
	} 
}; 
 
class CDropSource : public IDropSource 
{ 
public: 
	CDropSource() 
	{ 
		m_nRefCount = 0; 
	} 
 
protected: 
	int m_nRefCount; 
	 
public: 
	// IUnknown members 
	STDMETHOD(QueryInterface)( REFIID refiid, void FAR* FAR* ppvObject ) 
	{ 
		*ppvObject = ( refiid == IID_IUnknown || refiid == IID_IDropSource ) ? this : NULL; 
		 
		if ( *ppvObject != NULL ) 
			( (LPUNKNOWN)*ppvObject )->AddRef(); 
		 
		return *ppvObject == NULL ? E_NOINTERFACE : S_OK; 
	} 
	 
	STDMETHOD_(ULONG, AddRef)( void ) 
	{ 
		return ++m_nRefCount; 
	} 
	 
	STDMETHOD_(ULONG, Release)( void ) 
	{ 
		int nRefCount = --m_nRefCount; 
		if ( nRefCount == 0 ) 
			delete this; 
		return nRefCount; 
	} 
	 
	// IDropSource members 
	STDMETHOD(QueryContinueDrag)( BOOL bEscapePressed, DWORD dwKeyState ) 
	{ 
		if ( bEscapePressed ) 
			return DRAGDROP_S_CANCEL; 
			 
		if ( !( dwKeyState & ( MK_LBUTTON | MK_RBUTTON ) ) ) 
			return DRAGDROP_S_DROP; 
		 
		return S_OK; 
	} 
	 
    STDMETHOD(GiveFeedback)( DWORD dwEffect ) 
    { 
		return DRAGDROP_S_USEDEFAULTCURSORS; 
    } 
}; 
 
class CDataObject : public IDataObject 
{ 
public: 
	CDataObject( CDropSource *pDropSource ) 
	{ 
		m_nRefCount = 0; 
		m_pDropSource = pDropSource; 
		m_bSwappedButtons = GetSystemMetrics( SM_SWAPBUTTON ); 
	} 
	 
	virtual ~CDataObject() 
	{ 
		for ( vector < STGMEDIUM >::iterator posStgMedium = m_vStgMedium.begin(); posStgMedium != m_vStgMedium.end(); posStgMedium++ ) 
			ReleaseStgMedium( &( *posStgMedium ) ); 
	} 
 
protected: 
	CDropSource *m_pDropSource; 
	int m_nRefCount; 
	BOOL m_bSwappedButtons; 
	 
	vector < FORMATETC > m_vFormatEtc; 
	vector < STGMEDIUM > m_vStgMedium; 
 
public: 
	// IUnknown members 
	STDMETHOD(QueryInterface)( REFIID refiid, void FAR* FAR* ppvObject ) 
	{ 
		*ppvObject = ( refiid == IID_IUnknown || refiid == IID_IDataObject ) ? this : NULL; 
		 
		if ( *ppvObject != NULL ) 
			( (LPUNKNOWN)*ppvObject )->AddRef(); 
		 
		return *ppvObject == NULL ? E_NOINTERFACE : S_OK; 
	} 
	 
	STDMETHOD_(ULONG, AddRef)( void ) 
	{ 
		return ++m_nRefCount; 
	} 
	 
	STDMETHOD_(ULONG, Release)( void ) 
	{ 
		int nRefCount = --m_nRefCount; 
		if ( nRefCount == 0 ) 
			delete this; 
		return nRefCount; 
	} 
	 
	// IDataObject members 
	STDMETHOD(GetData)( FORMATETC __RPC_FAR *pformatetcIn, STGMEDIUM __RPC_FAR *pmedium ) 
	{ 
		if ( pformatetcIn == NULL || pmedium == NULL ) 
			return E_INVALIDARG; 
 
		ZeroMemory( pmedium, sizeof( STGMEDIUM ) ); 
		 
		for ( int nFormatEtc = 0; nFormatEtc < (int)m_vFormatEtc.size(); nFormatEtc++ ) 
		{ 
			if ( pformatetcIn->tymed & m_vFormatEtc[ nFormatEtc ].tymed && 
				 pformatetcIn->dwAspect == m_vFormatEtc[ nFormatEtc ].dwAspect && 
				 pformatetcIn->cfFormat == m_vFormatEtc[ nFormatEtc ].cfFormat ) 
			{ 
				if ( m_vStgMedium[ nFormatEtc ].tymed == TYMED_NULL ) 
					return OnRenderData( m_vFormatEtc[ nFormatEtc ], pmedium, ( GetAsyncKeyState( m_bSwappedButtons ? VK_RBUTTON : VK_LBUTTON ) >= 0 ) ) ? S_OK : DV_E_FORMATETC; 
				 
				CopyMedium( pmedium, m_vStgMedium[ nFormatEtc ], m_vFormatEtc[ nFormatEtc ] ); 
				return S_OK; 
			} 
		} 
		 
		return DV_E_FORMATETC; 
	} 
	 
	STDMETHOD(GetDataHere)( FORMATETC __RPC_FAR *pformatetc, STGMEDIUM __RPC_FAR *pmedium ) 
	{ 
		return E_NOTIMPL;	 
	} 
	 
	STDMETHOD(QueryGetData)( FORMATETC __RPC_FAR *pformatetc ) 
	{ 
		if ( pformatetc == NULL ) 
			return E_INVALIDARG; 
 
		if ( !( pformatetc->dwAspect & DVASPECT_CONTENT ) ) 
			return DV_E_DVASPECT; 
		 
		HRESULT hResult = DV_E_TYMED; 
		for ( int nFormatEtc = 0; nFormatEtc < (int)m_vFormatEtc.size(); nFormatEtc++ ) 
		{ 
			if ( !( pformatetc->tymed & m_vFormatEtc[ nFormatEtc ].tymed ) ) 
			{ 
				hResult = DV_E_TYMED; 
				continue; 
			} 
			 
			if ( pformatetc->cfFormat == m_vFormatEtc[ nFormatEtc ].cfFormat ) 
				return S_OK; 
			 
			hResult = DV_E_CLIPFORMAT; 
		} 
 
		return hResult; 
	} 
	 
	STDMETHOD(GetCanonicalFormatEtc)( FORMATETC __RPC_FAR *pformatectIn, FORMATETC __RPC_FAR *pformatetcOut ) 
	{ 
		return pformatetcOut == NULL ? E_INVALIDARG : DATA_S_SAMEFORMATETC; 
	} 
     
    STDMETHOD(SetData)( FORMATETC __RPC_FAR *pformatetc, STGMEDIUM __RPC_FAR *pmedium, BOOL bRelease ) 
    { 
		if ( pformatetc == NULL || pmedium == NULL ) 
			return E_INVALIDARG; 
 
		m_vFormatEtc.push_back( *pformatetc ); 
		 
		STGMEDIUM StgMedium = *pmedium; 
		  
		if ( !bRelease ) 
			CopyMedium( &StgMedium, *pmedium, *pformatetc ); 
 
		m_vStgMedium.push_back( StgMedium ); 
 
		return S_OK; 
    } 
     
    STDMETHOD(EnumFormatEtc)( DWORD dwDirection, IEnumFORMATETC __RPC_FAR *__RPC_FAR *ppenumFormatEtc ) 
    { 
		if ( ppenumFormatEtc == NULL ) 
			return E_POINTER; 
 
		switch ( dwDirection ) 
		{ 
			case DATADIR_GET:	*ppenumFormatEtc = new CEnumFormatEtc( m_vFormatEtc ); 
								( (CEnumFormatEtc*)*ppenumFormatEtc )->AddRef(); 
								return S_OK; 
			default:			*ppenumFormatEtc = NULL; 
								return E_NOTIMPL; 
		} 
    } 
     
    STDMETHOD(DAdvise)( FORMATETC __RPC_FAR *pformatetc, DWORD advf, IAdviseSink __RPC_FAR *pAdvSink, DWORD __RPC_FAR *pdwConnection ) 
    { 
		return OLE_E_ADVISENOTSUPPORTED; 
    } 
     
    STDMETHOD(DUnadvise)( DWORD dwConnection ) 
    { 
		return E_NOTIMPL; 
    } 
     
    STDMETHOD(EnumDAdvise)( IEnumSTATDATA __RPC_FAR *__RPC_FAR *ppenumAdvise ) 
    { 
		return OLE_E_ADVISENOTSUPPORTED; 
    } 
        
	void CopyMedium( STGMEDIUM *pMedDest, STGMEDIUM& MedSrc, FORMATETC& FmtSrc ) 
	{ 
		switch( MedSrc.tymed ) 
		{ 
			case TYMED_HGLOBAL:		pMedDest->hGlobal = (HGLOBAL)OleDuplicateData( MedSrc.hGlobal, FmtSrc.cfFormat, NULL ); 
									break; 
			case TYMED_GDI:			pMedDest->hBitmap = (HBITMAP)OleDuplicateData( MedSrc.hBitmap, FmtSrc.cfFormat, NULL ); 
									break; 
			case TYMED_MFPICT:		pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData( MedSrc.hMetaFilePict, FmtSrc.cfFormat, NULL ); 
									break; 
			case TYMED_ENHMF:		pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData( MedSrc.hEnhMetaFile, FmtSrc.cfFormat, NULL ); 
									break; 
			case TYMED_FILE:		pMedDest->lpszFileName = (LPOLESTR)OleDuplicateData( MedSrc.lpszFileName, FmtSrc.cfFormat, NULL ); 
									break; 
			case TYMED_ISTREAM:		pMedDest->pstm = MedSrc.pstm; 
									MedSrc.pstm->AddRef(); 
									break; 
			case TYMED_ISTORAGE:	pMedDest->pstg = MedSrc.pstg; 
									MedSrc.pstg->AddRef(); 
									break; 
		} 
		 
		pMedDest->tymed = MedSrc.tymed; 
		pMedDest->pUnkForRelease = NULL; 
		 
		if ( MedSrc.pUnkForRelease != NULL ) 
		{ 
			pMedDest->pUnkForRelease = MedSrc.pUnkForRelease; 
			MedSrc.pUnkForRelease->AddRef(); 
		}	 
	} 
	 
	virtual BOOL OnRenderData( FORMATETC& FormatEtc, STGMEDIUM *pStgMedium, BOOL bDropComplete ) 
	{ 
		return FALSE; 
	} 
}; 
 
class CDropTarget : public IDropTarget 
{ 
public:	 
	CDropTarget( HWND hTargetWnd ) 
	{ 
		m_hTargetWnd = hTargetWnd; 
		m_nRefCount = 0; 
		m_bAllowDrop = FALSE; 
		m_pDropTargetHelper = NULL; 
		ZeroMemory( &m_FormatEtc, sizeof( FORMATETC ) ); 
		ZeroMemory( &m_StgMedium, sizeof( STGMEDIUM ) ); 
		 
		if ( FAILED( CoCreateInstance( CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (LPVOID*)&m_pDropTargetHelper ) ) ) 
			m_pDropTargetHelper = NULL; 
	} 
	 
	virtual ~CDropTarget() 
	{ 
		if ( m_pDropTargetHelper != NULL ) 
		{ 
			m_pDropTargetHelper->Release(); 
			m_pDropTargetHelper = NULL; 
		} 
	} 
 
protected: 
	HWND m_hTargetWnd;	 
	int m_nRefCount; 
	struct IDropTargetHelper *m_pDropTargetHelper; 
	vector < FORMATETC > m_vFormatEtc; 
	BOOL m_bAllowDrop; 
	FORMATETC m_FormatEtc; 
	STGMEDIUM m_StgMedium; 
	 
public: 
	// IUnknown members 
	STDMETHOD(QueryInterface)( REFIID refiid, void FAR* FAR* ppvObject ) 
	{ 
		*ppvObject = ( refiid == IID_IUnknown || refiid == IID_IDropTarget ) ? this : NULL; 
		 
		if ( *ppvObject != NULL ) 
			( (LPUNKNOWN)*ppvObject )->AddRef(); 
		 
		return *ppvObject == NULL ? E_NOINTERFACE : S_OK; 
	} 
	 
	STDMETHOD_(ULONG, AddRef)( void ) 
	{ 
		return ++m_nRefCount; 
	} 
	 
	STDMETHOD_(ULONG, Release)( void ) 
	{ 
		int nRefCount = --m_nRefCount; 
		if ( nRefCount == 0 ) 
			delete this; 
		return nRefCount; 
	} 
 
	STDMETHOD(DragEnter)( IDataObject __RPC_FAR *pDataObject, DWORD dwKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect ) 
	{ 
		if ( pDataObject == NULL ) 
			return E_INVALIDARG; 
 
		if ( m_pDropTargetHelper != NULL ) 
			m_pDropTargetHelper->DragEnter( m_hTargetWnd, pDataObject, (LPPOINT)&pt, *pdwEffect ); 
		 
		ZeroMemory( &m_FormatEtc, sizeof( FORMATETC ) ); 
		if ( m_StgMedium.tymed != TYMED_NULL ) 
			ReleaseStgMedium( &m_StgMedium ); 
		ZeroMemory( &m_StgMedium, sizeof( STGMEDIUM ) ); 
		 
		for ( int nFormatEtc = 0; nFormatEtc < (int)m_vFormatEtc.size(); nFormatEtc++ ) 
		{ 
			STGMEDIUM StgMedium; 
			m_bAllowDrop = ( pDataObject->GetData( &m_vFormatEtc[ nFormatEtc ], &StgMedium ) == S_OK ); 
		 
			if ( m_bAllowDrop ) 
			{ 
				// store drag data for later use in DragOver 
				m_FormatEtc = m_vFormatEtc[ nFormatEtc ]; 
				m_StgMedium = StgMedium; 
				 
				// get client cursor position 
				CWindow hWnd( m_hTargetWnd ); 
				CPoint point( pt.x, pt.y ); 
				hWnd.ScreenToClient( &point ); 
					 
				*pdwEffect = OnDragEnter( m_FormatEtc, m_StgMedium, dwKeyState, point ); 
				 
				break; 
			} 
		} 
		 
		QueryDrop( dwKeyState, pdwEffect ); 
		 
		return S_OK;	 
	} 
	 
	STDMETHOD(DragOver)( DWORD dwKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect ) 
	{ 
		if ( m_pDropTargetHelper ) 
			m_pDropTargetHelper->DragOver( (LPPOINT)&pt, *pdwEffect ); 
		 
		if ( m_bAllowDrop && m_FormatEtc.cfFormat != CF_NULL && m_StgMedium.tymed != TYMED_NULL ) 
		{ 
			// get client cursor position 
			CWindow hWnd( m_hTargetWnd ); 
			CPoint point( pt.x, pt.y ); 
			hWnd.ScreenToClient( &point ); 
				 
			*pdwEffect = OnDragOver( m_FormatEtc, m_StgMedium, dwKeyState, point ); 
		} 
		 
		QueryDrop( dwKeyState, pdwEffect ); 
		 
		return S_OK; 
	} 
	 
	STDMETHOD(DragLeave)( void ) 
	{ 
		if ( m_pDropTargetHelper ) 
			m_pDropTargetHelper->DragLeave(); 
		 
		OnDragLeave(); 
 
		m_bAllowDrop = FALSE; 
		 
		ZeroMemory( &m_FormatEtc, sizeof( FORMATETC ) ); 
		if ( m_StgMedium.tymed != TYMED_NULL ) 
			ReleaseStgMedium( &m_StgMedium ); 
		ZeroMemory( &m_StgMedium, sizeof( STGMEDIUM ) ); 
		 
		return S_OK; 
	} 
	 
	STDMETHOD(Drop)( IDataObject __RPC_FAR *pDataObject, DWORD dwKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect ) 
    { 
		if ( pDataObject == NULL ) 
			return E_INVALIDARG;	 
 
		if ( m_pDropTargetHelper ) 
			m_pDropTargetHelper->Drop( pDataObject, (LPPOINT)&pt, *pdwEffect ); 
 
		if ( m_bAllowDrop && m_FormatEtc.cfFormat != CF_NULL && QueryDrop( dwKeyState, pdwEffect ) ) 
		{ 
			STGMEDIUM StgMedium; 
			if ( pDataObject->GetData( &m_FormatEtc, &StgMedium ) == S_OK ) 
			{ 
				// get client cursor position 
				CWindow hWnd( m_hTargetWnd ); 
				CPoint point( pt.x, pt.y ); 
				hWnd.ScreenToClient( &point ); 
				 
				if ( !OnDrop( m_FormatEtc, StgMedium, *pdwEffect, point ) ) 
					*pdwEffect = DROPEFFECT_NONE; 
				 
				ReleaseStgMedium( &StgMedium ); 
			} 
		} 
		 
		m_bAllowDrop = FALSE; 
		 
		ZeroMemory( &m_FormatEtc, sizeof( FORMATETC ) ); 
		if ( m_StgMedium.tymed != TYMED_NULL ) 
			ReleaseStgMedium( &m_StgMedium ); 
		ZeroMemory( &m_StgMedium, sizeof( STGMEDIUM ) ); 
		 
		return S_OK; 
    }     
     
    void AddSupportedFormat( FORMATETC& FormatEtc ) 
	{ 
		m_vFormatEtc.push_back( FormatEtc ); 
	} 
	 
	void AddSupportedFormat( CLIPFORMAT cfFormat ) 
	{ 
		FORMATETC FormatEtc; 
		ZeroMemory( &FormatEtc, sizeof( FORMATETC ) ); 
		 
		FormatEtc.cfFormat = cfFormat; 
		FormatEtc.dwAspect = DVASPECT_CONTENT; 
		FormatEtc.lindex = -1; 
		FormatEtc.tymed = TYMED_HGLOBAL; 
		 
		AddSupportedFormat( FormatEtc ); 
	} 
	 
	BOOL QueryDrop( DWORD dwKeyState, LPDWORD pdwEffect ) 
	{ 
		DWORD dwEffects = *pdwEffect;  
 
		if ( !m_bAllowDrop ) 
		{ 
			*pdwEffect = DROPEFFECT_NONE; 
			return FALSE; 
		} 
		 
		*pdwEffect = ( dwKeyState & MK_CONTROL ) ? ( ( dwKeyState & MK_SHIFT ) ? DROPEFFECT_LINK : DROPEFFECT_COPY ) : ( ( dwKeyState & MK_SHIFT ) ? DROPEFFECT_MOVE : 0 ); 
		if ( *pdwEffect == 0 )  
		{ 
			if ( dwEffects & DROPEFFECT_COPY ) 
				*pdwEffect = DROPEFFECT_COPY; 
			else if ( dwEffects & DROPEFFECT_MOVE ) 
				*pdwEffect = DROPEFFECT_MOVE;  
			else if (dwEffects & DROPEFFECT_LINK ) 
				*pdwEffect = DROPEFFECT_LINK;  
			else  
				*pdwEffect = DROPEFFECT_NONE; 
		}  
		else if ( !( *pdwEffect & dwEffects ) ) 
			*pdwEffect = DROPEFFECT_NONE; 
 
		return ( *pdwEffect != DROPEFFECT_NONE ); 
	} 
 
	virtual DWORD OnDragEnter( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwKeyState, CPoint point ) 
	{ 
		return FALSE; 
	} 
	 
	virtual DWORD OnDragOver( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwKeyState, CPoint point ) 
	{ 
		return FALSE; 
	} 
	 
	virtual BOOL OnDrop( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwEffect, CPoint point ) 
	{ 
		return FALSE; 
	} 
	 
	virtual void OnDragLeave() 
	{ 
	} 
}; 
 
template < class T > 
class CDropTargetT : public CDropTarget 
{ 
public: 
	CDropTargetT( HWND hTargetWnd ) : CDropTarget( hTargetWnd ) 
	{ 
		m_pDelegate = NULL; 
	} 
 
protected: 
	T *m_pDelegate; 
 
public:	 
	BOOL Register( T *pDelegate ) 
	{ 
		m_pDelegate = pDelegate; 
		return TRUE; 
	} 
	 
	virtual DWORD OnDragEnter( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwKeyState, CPoint point ) 
	{ 
		return m_pDelegate == NULL ? DROPEFFECT_NONE : m_pDelegate->OnDragEnter( FormatEtc, StgMedium, dwKeyState, point ); 
	} 
	 
	virtual DWORD OnDragOver( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwKeyState, CPoint point ) 
	{ 
		return m_pDelegate == NULL ? DROPEFFECT_NONE : m_pDelegate->OnDragOver( FormatEtc, StgMedium, dwKeyState, point ); 
	} 
	 
	virtual BOOL OnDrop( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwEffect, CPoint point ) 
	{ 
		return m_pDelegate == NULL ? FALSE : m_pDelegate->OnDrop( FormatEtc, StgMedium, dwEffect, point ); 
	} 
	 
	virtual void OnDragLeave() 
	{ 
		if ( m_pDelegate != NULL ) 
			m_pDelegate->OnDragLeave(); 
	} 
}; 
 
template < class T > 
class CDataObjectT : public CDataObject 
{ 
public: 
	CDataObjectT( CDropSource *pDropSource ) : CDataObject( pDropSource ) 
	{ 
		m_pDelegate = FALSE; 
	} 
 
protected: 
	T *m_pDelegate; 
 
public:	 
	BOOL Register( T *pDelegate ) 
	{ 
		m_pDelegate = pDelegate; 
		return TRUE; 
	} 
	 
	virtual BOOL OnRenderData( FORMATETC& FormatEtc, STGMEDIUM *pStgMedium, BOOL bDropComplete ) 
	{ 
		return m_pDelegate == NULL ? FALSE : m_pDelegate->OnRenderData( FormatEtc, pStgMedium, bDropComplete ); 
	} 
}; 
 
template < class T > 
class CDragDrop 
{ 
public: 
	CDragDrop() 
	{ 
		m_pDropSource = NULL; 
		m_pDataObject = NULL; 
		m_pDropTarget = NULL; 
		m_hTargetWnd = NULL; 
	} 
		 
	virtual ~CDragDrop() 
	{ 
		if ( m_pDropSource != NULL ) 
			m_pDropSource->Release(); 
		if ( m_pDataObject != NULL ) 
			m_pDataObject->Release(); 
	} 
 
protected: 
	CDropSource *m_pDropSource; 
	CDataObjectT< T > *m_pDataObject; 
	CDropTargetT< T > *m_pDropTarget; 
	HWND m_hTargetWnd; 
 
public: 
	BOOL Register( T *pDelegate, BOOL bDropSource = TRUE ) 
	{ 
		m_hTargetWnd = pDelegate->m_hWnd; 
		 
		// instantiate new drop target object 
		m_pDropTarget = new CDropTargetT< T >( m_hTargetWnd ); 
		m_pDropTarget->Register( pDelegate ); 
		 
		// register drop target 
		if ( FAILED( RegisterDragDrop( m_hTargetWnd, m_pDropTarget ) ) ) 
		{ 
			m_pDropTarget = NULL; 
			return FALSE; 
		} 
		 
		// is this a drop target only? 
		if ( !bDropSource ) 
			return TRUE; 
		 
		// instantiate new drop source object 
		m_pDropSource = new CDropSource; 
		m_pDropSource->AddRef(); 
		 
		m_pDataObject = new CDataObjectT< T >( m_pDropSource ); 
		m_pDataObject->AddRef(); 
		 
		// register drop source delegate for data render 
		return m_pDataObject->Register( pDelegate ); 
	} 
	 
	BOOL Revoke() 
	{ 
		m_pDropTarget = NULL; 
		return ( RevokeDragDrop( m_hTargetWnd ) == S_OK ); 
	} 
	 
	BOOL AddTargetFormat( CLIPFORMAT cfFormat ) 
	{ 
		if ( m_pDropTarget == NULL ) 
			return FALSE; 
		m_pDropTarget->AddSupportedFormat( cfFormat ); 
		return TRUE; 
	}	 
	 
	BOOL AddSourceFormat( CLIPFORMAT cfFormat ) 
	{ 
		if ( m_pDataObject == NULL ) 
			return FALSE; 
			 
		FORMATETC FormatEtc; 
		ZeroMemory( &FormatEtc, sizeof( FORMATETC ) ); 
		 
		FormatEtc.cfFormat = cfFormat; 
		FormatEtc.dwAspect = DVASPECT_CONTENT; 
		FormatEtc.lindex = -1; 
		FormatEtc.tymed = TYMED_HGLOBAL; 
		 
		STGMEDIUM StgMedium; 
		ZeroMemory( &StgMedium, sizeof( STGMEDIUM ) ); 
		 
		return SUCCEEDED( m_pDataObject->SetData( &FormatEtc, &StgMedium, TRUE ) ); 
	} 
	 
	BOOL SetClipboard( FORMATETC& FormatEtc, STGMEDIUM& StgMedium ) 
	{ 
		if ( m_pDataObject == NULL ) 
			return DROPEFFECT_NONE; 
		 
		if ( FAILED( m_pDataObject->SetData( &FormatEtc, &StgMedium, TRUE ) ) ) 
			return DROPEFFECT_NONE; 
		 
		return ( OleSetClipboard( m_pDataObject ) == S_OK ); 
	} 
	 
	BOOL FlushClipboard() 
	{ 
		return ( OleFlushClipboard() == S_OK ); 
	} 
	 
	DWORD DoDragDrop( SHDRAGIMAGE *pDragImage = NULL, DWORD dwValidEffects = DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK ) 
	{ 
		if ( m_pDataObject == NULL ) 
			return DROPEFFECT_NONE; 
			 
		IDragSourceHelper *pDragSourceHelper = NULL; 
		 
		// instantiate drag source helper object 
		if ( pDragImage != NULL ) 
		{ 
			if ( FAILED( CoCreateInstance( CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDragSourceHelper, (LPVOID*)&pDragSourceHelper ) ) ) 
				pDragSourceHelper = NULL; 
			 
			if ( pDragSourceHelper != NULL ) 
				pDragSourceHelper->InitializeFromBitmap( pDragImage, m_pDataObject ); 
		} 
		 
		DWORD dwEffects = DROPEFFECT_NONE; 
		dwEffects = ::DoDragDrop( m_pDataObject, m_pDropSource, dwValidEffects, &dwEffects ) == DRAGDROP_S_DROP ? DROPEFFECT_NONE : dwEffects; 
		 
		// destroy drag source helper object 
		if ( pDragSourceHelper != NULL ) 
			pDragSourceHelper->Release(); 
		 
		return dwEffects; 
	}	 
};