#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 : nullptr; if ( *ppvObject != nullptr ) ( (LPUNKNOWN)*ppvObject )->AddRef(); return *ppvObject == nullptr ? 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 != nullptr ) *pceltFetched=0; ULONG cReturn = celt; if ( celt <= 0 || lpFormatEtc == nullptr || m_nIndex >= (int)m_vFormatEtc.size() ) return S_FALSE; if ( pceltFetched == nullptr && celt != 1 ) // pceltFetched can be nullptr 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 != nullptr ) *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 == nullptr ) 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 : nullptr; if ( *ppvObject != nullptr ) ( (LPUNKNOWN)*ppvObject )->AddRef(); return *ppvObject == nullptr ? 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 : nullptr; if ( *ppvObject != nullptr ) ( (LPUNKNOWN)*ppvObject )->AddRef(); return *ppvObject == nullptr ? 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 == nullptr || pmedium == nullptr ) 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 == nullptr ) 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 == nullptr ? E_INVALIDARG : DATA_S_SAMEFORMATETC; } STDMETHOD(SetData)( FORMATETC __RPC_FAR *pformatetc, STGMEDIUM __RPC_FAR *pmedium, BOOL bRelease ) { if ( pformatetc == nullptr || pmedium == nullptr ) 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 == nullptr ) return E_POINTER; switch ( dwDirection ) { case DATADIR_GET: *ppenumFormatEtc = new CEnumFormatEtc( m_vFormatEtc ); ( (CEnumFormatEtc*)*ppenumFormatEtc )->AddRef(); return S_OK; default: *ppenumFormatEtc = nullptr; 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 = nullptr; if ( MedSrc.pUnkForRelease != nullptr ) { 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 = nullptr; ZeroMemory( &m_FormatEtc, sizeof( FORMATETC ) ); ZeroMemory( &m_StgMedium, sizeof( STGMEDIUM ) ); if ( FAILED( CoCreateInstance( CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (LPVOID*)&m_pDropTargetHelper ) ) ) m_pDropTargetHelper = nullptr; } virtual ~CDropTarget() { if ( m_pDropTargetHelper != nullptr ) { m_pDropTargetHelper->Release(); m_pDropTargetHelper = nullptr; } } 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 : nullptr; if ( *ppvObject != nullptr ) ( (LPUNKNOWN)*ppvObject )->AddRef(); return *ppvObject == nullptr ? 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 == nullptr ) return E_INVALIDARG; if ( m_pDropTargetHelper != nullptr ) 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 == nullptr ) 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 = nullptr; } 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 == nullptr ? DROPEFFECT_NONE : m_pDelegate->OnDragEnter( FormatEtc, StgMedium, dwKeyState, point ); } virtual DWORD OnDragOver( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwKeyState, CPoint point ) { return m_pDelegate == nullptr ? DROPEFFECT_NONE : m_pDelegate->OnDragOver( FormatEtc, StgMedium, dwKeyState, point ); } virtual BOOL OnDrop( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwEffect, CPoint point ) { return m_pDelegate == nullptr ? FALSE : m_pDelegate->OnDrop( FormatEtc, StgMedium, dwEffect, point ); } virtual void OnDragLeave() { if ( m_pDelegate != nullptr ) 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 == nullptr ? FALSE : m_pDelegate->OnRenderData( FormatEtc, pStgMedium, bDropComplete ); } }; template < class T > class CDragDrop { public: CDragDrop() { m_pDropSource = nullptr; m_pDataObject = nullptr; m_pDropTarget = nullptr; m_hTargetWnd = nullptr; } virtual ~CDragDrop() { if ( m_pDropSource != nullptr ) m_pDropSource->Release(); if ( m_pDataObject != nullptr ) 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 ) ) ) { delete m_pDropTarget; m_pDropTarget = nullptr; 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 = nullptr; return ( RevokeDragDrop( m_hTargetWnd ) == S_OK ); } BOOL AddTargetFormat( CLIPFORMAT cfFormat ) { if ( m_pDropTarget == nullptr ) return FALSE; m_pDropTarget->AddSupportedFormat( cfFormat ); return TRUE; } BOOL AddSourceFormat( CLIPFORMAT cfFormat ) { if ( m_pDataObject == nullptr ) 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 == nullptr ) 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 = nullptr, DWORD dwValidEffects = DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK ) { if ( m_pDataObject == nullptr ) return DROPEFFECT_NONE; IDragSourceHelper *pDragSourceHelper = nullptr; // Instantiate drag source helper object if ( pDragImage != nullptr ) { if ( FAILED( CoCreateInstance( CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER, IID_IDragSourceHelper, (LPVOID*)&pDragSourceHelper ) ) ) pDragSourceHelper = nullptr; if ( pDragSourceHelper != nullptr ) 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 != nullptr ) pDragSourceHelper->Release(); return dwEffects; } };