转载于http://www.cnblogs.com/chunyou128/archive/2012/02/20/2360607.html
1.参考内容:
2.拖动涉及:源、目标、数据对象
2.1 鼠标左键按下并且移动,触发拖动。
构建源和数据对象。调用拖放函数DoDragDrop。
case WM_MOUSEMOVE:
// if the mouse is held down then start a drag-drop
if(fMouseDown)
{
IDataObject *pDataObject;
IDropSource *pDropSource;
DWORD dwEffect;
DWORD dwResult;
FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stgmed = { TYMED_HGLOBAL, { 0 }, 0 };
// transfer the current selection into the IDataObject
stgmed.hGlobal = CopySelection(hwndEdit);
// Create IDataObject and IDropSource COM objects
CreateDropSource(&pDropSource);
CreateDataObject(&fmtetc, &stgmed, 1, &pDataObject);
//
// ** ** ** The drag-drop operation starts here! ** ** **
//
dwResult = DoDragDrop(pDataObject, pDropSource, DROPEFFECT_COPY|DROPEFFECT_MOVE, &dwEffect);
// success!
if(dwResult == DRAGDROP_S_DROP)
{
if(dwEffect & DROPEFFECT_MOVE)
{
// remove selection from edit control
}
}
// cancelled
else if(dwResult == DRAGDROP_S_CANCEL)
{
}
pDataObject->Release();
pDropSource->Release();
ReleaseCapture();
fMouseDown = FALSE;
fDidDragDrop = TRUE;
}
2.拖放的目标程序,需要调用RegisterDragDrop,为拖放注册。
void RegisterDropWindow(HWND hwnd, IDropTarget **ppDropTarget)
{
CDropTarget *pDropTarget = new CDropTarget(hwnd);
// acquire a strong lock
CoLockObjectExternal(pDropTarget, TRUE, FALSE);
// tell OLE that the window is a drop target
RegisterDragDrop(hwnd, pDropTarget);
*ppDropTarget = pDropTarget;
}
case WM_CREATE:
hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_WANTRETURN|WS_VSCROLL,
0,0,0,0, hwnd, 0, hInstance, 0);
SendMessage(hwndEdit, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), 0);
// make the Edit control into a DropTarget
RegisterDropWindow(hwndEdit, &pDropTarget);
注意:如果你的程序是直接继承自IDropTarget,CoLockObjectExternal(this, TRUE, FALSE);这么写的话,紧接着的下面一句(RegisterDropWindow(hwndEdit, &pDropTarget);)可能就不会执行。换句话说,拖放可能会失败。
3.所有的一切,都由DoDragDrop发轫。这个函数,会调用一些虚函数。如:CDataObject::EnumFormatEtc
4.CDropTarget::Drop中响应事件操作。
注意: 初始化 、反初始化
OleInitialize(0);
OleUninitialize();
源文件
dropsource.h
//
// DROPSOURCE.H
//
// Implementation of the IDropSource COM interface
//
// By J Brown 2004
//
// www.catch22.net
//
#include <windows.h>
HANDLE StringToHandle(char *szText, int nTextLen);
HRESULT CreateDropSource(IDropSource **ppDropSource);
class CDropSource : public IDropSource
{
public:
//
// IUnknown members
//
HRESULT __stdcall QueryInterface (REFIID iid, void ** ppvObject);
ULONG __stdcall AddRef (void);
ULONG __stdcall Release (void);
//
// IDropSource members
//
HRESULT __stdcall QueryContinueDrag (BOOL fEscapePressed, DWORD grfKeyState);
HRESULT __stdcall GiveFeedback (DWORD dwEffect);
//
// Constructor / Destructor
//
CDropSource();
~CDropSource();
private:
//
// private members and functions
//
LONG m_lRefCount;
};
dropsource.cpp
//
// DROPSOURCE.CPP
//
// Implementation of the IDropSource COM interface
//
// By J Brown 2004
//
// www.catch22.net
//
#include "../stdafx.h"
#include "dropsource.h"
HANDLE StringToHandle(char *szText, int nTextLen)
{
void *ptr;
// if text length is -1 then treat as a nul-terminated string
if(nTextLen == -1)
nTextLen = lstrlen(szText) + 1;
// allocate and lock a global memory buffer. Make it fixed
// data so we don't have to use GlobalLock
ptr = (void *)GlobalAlloc(GMEM_FIXED, nTextLen);
// copy the string into the buffer
memcpy(ptr, szText, nTextLen);
return ptr;
}
//
// Constructor
//
CDropSource::CDropSource()
{
m_lRefCount = 1;
}
//
// Destructor
//
CDropSource::~CDropSource()
{
}
//
// IUnknown::AddRef
//
ULONG __stdcall CDropSource::AddRef(void)
{
// increment object reference count
return InterlockedIncrement(&m_lRefCount);
}
//
// IUnknown::Release
//
ULONG __stdcall CDropSource::Release(void)
{
// decrement object reference count
LONG count = InterlockedDecrement(&m_lRefCount);
if(count == 0)
{
delete this;
return 0;
}
else
{
return count;
}
}
//
// IUnknown::QueryInterface
//
HRESULT __stdcall CDropSource::QueryInterface(REFIID iid, void **ppvObject)
{
// check to see what interface has been requested
if(iid == IID_IDropSource || iid == IID_IUnknown)
{
AddRef();
*ppvObject = this;
return S_OK;
}
else
{
*ppvObject = 0;
return E_NOINTERFACE;
}
}
//
// CDropSource::QueryContinueDrag
//
// Called by OLE whenever Escape/Control/Shift/Mouse buttons have changed
//
HRESULT __stdcall CDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
// if the <Escape> key has been pressed since the last call, cancel the drop
if(fEscapePressed == TRUE)
return DRAGDROP_S_CANCEL;
// if the <LeftMouse> button has been released, then do the drop!
if((grfKeyState & MK_LBUTTON) == 0)
return DRAGDROP_S_DROP;
// continue with the drag-drop
return S_OK;
}
//
// CDropSource::GiveFeedback
//
// Return either S_OK, or DRAGDROP_S_USEDEFAULTCURSORS to instruct OLE to use the
// default mouse cursor images
//
HRESULT __stdcall CDropSource::GiveFeedback(DWORD dwEffect)
{
return DRAGDROP_S_USEDEFAULTCURSORS;
}
//
// Helper routine to create an IDropSource object
//
HRESULT CreateDropSource(IDropSource **ppDropSource)
{
if(ppDropSource == 0)
return E_INVALIDARG;
*ppDropSource = new CDropSource();
return (*ppDropSource) ? S_OK : E_OUTOFMEMORY;
}
droptarget.h
//
// DROPTARGET.H
//
// By J Brown 2004
//
// www.catch22.net
//
#pragma once
#include <windows.h>
void DropData(HWND hwnd, IDataObject *pDataObject);
//
// This is our definition of a class which implements
// the IDropTarget interface
//
class CDropTarget : public IDropTarget
{
public:
// IUnknown implementation
HRESULT __stdcall QueryInterface (REFIID iid, void ** ppvObject);
ULONG __stdcall AddRef (void);
ULONG __stdcall Release (void);
// IDropTarget implementation
HRESULT __stdcall DragEnter (IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
HRESULT __stdcall DragOver (DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
HRESULT __stdcall DragLeave (void);
HRESULT __stdcall Drop (IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
virtual void DropData(HWND hwnd, IDataObject *pDataObject) = 0;
virtual const HWND GetWnd() = 0;
// Constructor
CDropTarget(/*HWND hwnd*/);
~CDropTarget();
private:
// internal helper function
DWORD DropEffect(DWORD grfKeyState, POINTL pt, DWORD dwAllowed);
bool QueryDataObject(IDataObject *pDataObject);
// Private member variables
LONG m_lRefCount;
bool m_fAllowDrop;
IDataObject *m_pDataObject;
};
droptarget.cpp
//
// DROPTARGET.CPP
//
// By J Brown 2004
//
// www.catch22.net
//
#include "../stdafx.h"
#include "droptarget.h"
//
// Constructor for the CDropTarget class
//
CDropTarget::CDropTarget(/*HWND hwnd*/)
{
m_lRefCount = 1;
m_fAllowDrop = false;
}
//
// Destructor for the CDropTarget class
//
CDropTarget::~CDropTarget()
{
}
//
// Position the edit control's caret under the mouse
//
void PositionCursor(HWND hwndEdit, POINTL pt)
{
DWORD curpos;
// get the character position of mouse
ScreenToClient(hwndEdit, (POINT *)&pt);
curpos = SendMessage(hwndEdit, EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y));
// set cursor position
SendMessage(hwndEdit, EM_SETSEL, LOWORD(curpos), LOWORD(curpos));
}
//
// IUnknown::QueryInterface
//
HRESULT __stdcall CDropTarget::QueryInterface (REFIID iid, void ** ppvObject)
{
if(iid == IID_IDropTarget || iid == IID_IUnknown)
{
AddRef();
*ppvObject = this;
return S_OK;
}
else
{
*ppvObject = 0;
return E_NOINTERFACE;
}
}
//
// IUnknown::AddRef
//
ULONG __stdcall CDropTarget::AddRef(void)
{
return InterlockedIncrement(&m_lRefCount);
}
//
// IUnknown::Release
//
ULONG __stdcall CDropTarget::Release(void)
{
LONG count = InterlockedDecrement(&m_lRefCount);
if(count == 0)
{
return 0;
}
else
{
return count;
}
}
//
// QueryDataObject private helper routine
//
bool CDropTarget::QueryDataObject(IDataObject *pDataObject)
{
FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
// does the data object support CF_TEXT using a HGLOBAL?
return pDataObject->QueryGetData(&fmtetc) == S_OK ? true : false;
}
//
// DropEffect private helper routine
//
DWORD CDropTarget::DropEffect(DWORD grfKeyState, POINTL pt, DWORD dwAllowed)
{
DWORD dwEffect = 0;
// 1. check "pt" -> do we allow a drop at the specified coordinates?
// 2. work out that the drop-effect should be based on grfKeyState
if(grfKeyState & MK_CONTROL)
{
dwEffect = dwAllowed & DROPEFFECT_COPY;
}
else if(grfKeyState & MK_SHIFT)
{
dwEffect = dwAllowed & DROPEFFECT_MOVE;
}
// 3. no key-modifiers were specified (or drop effect not allowed), so
// base the effect on those allowed by the dropsource
if(dwEffect == 0)
{
if(dwAllowed & DROPEFFECT_COPY) dwEffect = DROPEFFECT_COPY;
if(dwAllowed & DROPEFFECT_MOVE) dwEffect = DROPEFFECT_MOVE;
}
return dwEffect;
}
//
// IDropTarget::DragEnter
//
//
//
HRESULT __stdcall CDropTarget::DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
// does the dataobject contain data we want?
m_fAllowDrop = QueryDataObject(pDataObject);
if(m_fAllowDrop)
{
// get the dropeffect based on keyboard state
*pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
SetFocus(GetWnd());
PositionCursor(GetWnd(), pt);
}
else
{
*pdwEffect = DROPEFFECT_NONE;
}
return S_OK;
}
//
// IDropTarget::DragOver
//
//
//
HRESULT __stdcall CDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
if(m_fAllowDrop)
{
*pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
PositionCursor(GetWnd(), pt);
}
else
{
*pdwEffect = DROPEFFECT_NONE;
}
return S_OK;
}
//
// IDropTarget::DragLeave
//
HRESULT __stdcall CDropTarget::DragLeave(void)
{
return S_OK;
}
//
// IDropTarget::Drop
//
//
HRESULT __stdcall CDropTarget::Drop(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
PositionCursor(GetWnd(), pt);
if(m_fAllowDrop)
{
DropData(GetWnd(), pDataObject);
*pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
}
else
{
*pdwEffect = DROPEFFECT_NONE;
}
return S_OK;
}
void DropData(HWND hwnd, IDataObject *pDataObject)
{
// construct a FORMATETC object
FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stgmed;
// See if the dataobject contains any TEXT stored as a HGLOBAL
if(pDataObject->QueryGetData(&fmtetc) == S_OK)
{
// Yippie! the data is there, so go get it!
if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
{
// we asked for the data as a HGLOBAL, so access it appropriately
PVOID data = GlobalLock(stgmed.hGlobal);
SetWindowText(hwnd, (char *)data);
GlobalUnlock(stgmed.hGlobal);
// release the data using the COM API
ReleaseStgMedium(&stgmed);
}
}
}
dataobject.h
//
// DATAOBJECT.H
//
// Implementation of the IDataObject COM interface
//
// By J Brown 2004
//
// www.catch22.net
//
#include <windows.h>
// defined in enumformat.cpp
HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC *pFormatEtc, IEnumFORMATETC **ppEnumFormatEtc);
HRESULT CreateDataObject (FORMATETC *fmtetc, STGMEDIUM *stgmeds, UINT count, IDataObject **ppDataObject);
class CDataObject : public IDataObject
{
public:
//
// IUnknown members
//
HRESULT __stdcall QueryInterface (REFIID iid, void ** ppvObject);
ULONG __stdcall AddRef (void);
ULONG __stdcall Release (void);
//
// IDataObject members
//
HRESULT __stdcall GetData (FORMATETC *pFormatEtc, STGMEDIUM *pMedium);
HRESULT __stdcall GetDataHere (FORMATETC *pFormatEtc, STGMEDIUM *pMedium);
HRESULT __stdcall QueryGetData (FORMATETC *pFormatEtc);
HRESULT __stdcall GetCanonicalFormatEtc (FORMATETC *pFormatEct, FORMATETC *pFormatEtcOut);
HRESULT __stdcall SetData (FORMATETC *pFormatEtc, STGMEDIUM *pMedium, BOOL fRelease);
HRESULT __stdcall EnumFormatEtc (DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc);
HRESULT __stdcall DAdvise (FORMATETC *pFormatEtc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection);
HRESULT __stdcall DUnadvise (DWORD dwConnection);
HRESULT __stdcall EnumDAdvise (IEnumSTATDATA **ppEnumAdvise);
//
// Constructor / Destructor
//
CDataObject(FORMATETC *fmt, STGMEDIUM *stgmed, int count);
~CDataObject();
private:
int LookupFormatEtc(FORMATETC *pFormatEtc);
//
// any private members and functions
//
LONG m_lRefCount;
FORMATETC *m_pFormatEtc;
STGMEDIUM *m_pStgMedium;
LONG m_nNumFormats;
};
dataobject.cpp
//
// DATAOBJECT.CPP
//
// Implementation of the IDataObject COM interface
//
// By J Brown 2004
//
// www.catch22.net
//
#include "../stdafx.h"
#include "dataobject.h"
//
// Constructor
//
CDataObject::CDataObject(FORMATETC *fmtetc, STGMEDIUM *stgmed, int count)
{
m_lRefCount = 1;
m_nNumFormats = count;
m_pFormatEtc = new FORMATETC[count];
m_pStgMedium = new STGMEDIUM[count];
for(int i = 0; i < count; i++)
{
m_pFormatEtc[i] = fmtetc[i];
m_pStgMedium[i] = stgmed[i];
}
}
//
// Destructor
//
CDataObject::~CDataObject()
{
// cleanup
if(m_pFormatEtc) delete[] m_pFormatEtc;
if(m_pStgMedium) delete[] m_pStgMedium;
OutputDebugString("oof\n");
}
//
// IUnknown::AddRef
//
ULONG __stdcall CDataObject::AddRef(void)
{
// increment object reference count
return InterlockedIncrement(&m_lRefCount);
}
//
// IUnknown::Release
//
ULONG __stdcall CDataObject::Release(void)
{
// decrement object reference count
LONG count = InterlockedDecrement(&m_lRefCount);
if(count == 0)
{
delete this;
return 0;
}
else
{
return count;
}
}
//
// IUnknown::QueryInterface
//
HRESULT __stdcall CDataObject::QueryInterface(REFIID iid, void **ppvObject)
{
// check to see what interface has been requested
if(iid == IID_IDataObject || iid == IID_IUnknown)
{
AddRef();
*ppvObject = this;
return S_OK;
}
else
{
*ppvObject = 0;
return E_NOINTERFACE;
}
}
HGLOBAL DupMem(HGLOBAL hMem)
{
// lock the source memory object
DWORD len = GlobalSize(hMem);
PVOID source = GlobalLock(hMem);
// create a fixed "global" block - i.e. just
// a regular lump of our process heap
PVOID dest = GlobalAlloc(GMEM_FIXED, len);
memcpy(dest, source, len);
GlobalUnlock(hMem);
return dest;
}
int CDataObject::LookupFormatEtc(FORMATETC *pFormatEtc)
{
for(int i = 0; i < m_nNumFormats; i++)
{
if((pFormatEtc->tymed & m_pFormatEtc[i].tymed) &&
pFormatEtc->cfFormat == m_pFormatEtc[i].cfFormat &&
pFormatEtc->dwAspect == m_pFormatEtc[i].dwAspect)
{
return i;
}
}
return -1;
}
//
// IDataObject::GetData
//
HRESULT __stdcall CDataObject::GetData (FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
{
int idx;
//
// try to match the requested FORMATETC with one of our supported formats
//
if((idx = LookupFormatEtc(pFormatEtc)) == -1)
{
return DV_E_FORMATETC;
}
//
// found a match! transfer the data into the supplied storage-medium
//
pMedium->tymed = m_pFormatEtc[idx].tymed;
pMedium->pUnkForRelease = 0;
switch(m_pFormatEtc[idx].tymed)
{
case TYMED_HGLOBAL:
pMedium->hGlobal = DupMem(m_pStgMedium[idx].hGlobal);
//return S_OK;
break;
default:
return DV_E_FORMATETC;
}
return S_OK;
}
//
// IDataObject::GetDataHere
//
HRESULT __stdcall CDataObject::GetDataHere (FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
{
// GetDataHere is only required for IStream and IStorage mediums
// It is an error to call GetDataHere for things like HGLOBAL and other clipboard formats
//
// OleFlushClipboard
//
return DATA_E_FORMATETC;
}
//
// IDataObject::QueryGetData
//
// Called to see if the IDataObject supports the specified format of data
//
HRESULT __stdcall CDataObject::QueryGetData (FORMATETC *pFormatEtc)
{
return (LookupFormatEtc(pFormatEtc) == -1) ? DV_E_FORMATETC : S_OK;
}
//
// IDataObject::GetCanonicalFormatEtc
//
HRESULT __stdcall CDataObject::GetCanonicalFormatEtc (FORMATETC *pFormatEct, FORMATETC *pFormatEtcOut)
{
// Apparently we have to set this field to NULL even though we don't do anything else
pFormatEtcOut->ptd = NULL;
return E_NOTIMPL;
}
//
// IDataObject::SetData
//
HRESULT __stdcall CDataObject::SetData (FORMATETC *pFormatEtc, STGMEDIUM *pMedium, BOOL fRelease)
{
return E_NOTIMPL;
}
//
// IDataObject::EnumFormatEtc
//
HRESULT __stdcall CDataObject::EnumFormatEtc (DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
{
if(dwDirection == DATADIR_GET)
{
// for Win2k+ you can use the SHCreateStdEnumFmtEtc API call, however
// to support all Windows platforms we need to implement IEnumFormatEtc ourselves.
return CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc, ppEnumFormatEtc);
}
else
{
// the direction specified is not support for drag+drop
return E_NOTIMPL;
}
}
//
// IDataObject::DAdvise
//
HRESULT __stdcall CDataObject::DAdvise (FORMATETC *pFormatEtc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
return OLE_E_ADVISENOTSUPPORTED;
}
//
// IDataObject::DUnadvise
//
HRESULT __stdcall CDataObject::DUnadvise (DWORD dwConnection)
{
return OLE_E_ADVISENOTSUPPORTED;
}
//
// IDataObject::EnumDAdvise
//
HRESULT __stdcall CDataObject::EnumDAdvise (IEnumSTATDATA **ppEnumAdvise)
{
return OLE_E_ADVISENOTSUPPORTED;
}
//
// Helper function
//
HRESULT CreateDataObject (FORMATETC *fmtetc, STGMEDIUM *stgmeds, UINT count, IDataObject **ppDataObject)
{
if(ppDataObject == 0)
return E_INVALIDARG;
*ppDataObject = new CDataObject(fmtetc, stgmeds, count);
return (*ppDataObject) ? S_OK : E_OUTOFMEMORY;
}
enumformat.h
//
// ENUMFORMAT.H
//
// By J Brown 2004
//
// www.catch22.net
//
// Implementation of the IEnumFORMATETC interface
//
// For Win2K and above look at the SHCreateStdEnumFmtEtc API call!!
//
// Apparently the order of formats in an IEnumFORMATETC object must be
// the same as those that were stored in the clipboard
//
//
#include <windows.h>
class CEnumFormatEtc : public IEnumFORMATETC
{
public:
//
// IUnknown members
//
HRESULT __stdcall QueryInterface (REFIID iid, void ** ppvObject);
ULONG __stdcall AddRef (void);
ULONG __stdcall Release (void);
//
// IEnumFormatEtc members
//
HRESULT __stdcall Next (ULONG celt, FORMATETC * rgelt, ULONG * pceltFetched);
HRESULT __stdcall Skip (ULONG celt);
HRESULT __stdcall Reset (void);
HRESULT __stdcall Clone (IEnumFORMATETC ** ppEnumFormatEtc);
//
// Construction / Destruction
//
CEnumFormatEtc(FORMATETC *pFormatEtc, int nNumFormats);
~CEnumFormatEtc();
private:
LONG m_lRefCount; // Reference count for this COM interface
ULONG m_nIndex; // current enumerator index
ULONG m_nNumFormats; // number of FORMATETC members
FORMATETC * m_pFormatEtc; // array of FORMATETC objects
};
enumformat.cpp
//
// ENUMFORMAT.CPP
//
// By J Brown 2004
//
// www.catch22.net
//
// Implementation of the IEnumFORMATETC interface
//
// For Win2K and above look at the SHCreateStdEnumFmtEtc API call!!
//
// Apparently the order of formats in an IEnumFORMATETC object must be
// the same as those that were stored in the clipboard
//
//
#include "../stdafx.h"
#include "enumformat.h"
//
// "Drop-in" replacement for SHCreateStdEnumFmtEtc. Called by CDataObject::EnumFormatEtc
//
HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC *pFormatEtc, IEnumFORMATETC **ppEnumFormatEtc)
{
if(nNumFormats == 0 || pFormatEtc == 0 || ppEnumFormatEtc == 0)
return E_INVALIDARG;
*ppEnumFormatEtc = new CEnumFormatEtc(pFormatEtc, nNumFormats);
return (*ppEnumFormatEtc) ? S_OK : E_OUTOFMEMORY;
}
//
// Helper function to perform a "deep" copy of a FORMATETC
//
static void DeepCopyFormatEtc(FORMATETC *dest, FORMATETC *source)
{
// copy the source FORMATETC into dest
*dest = *source;
if(source->ptd)
{
// allocate memory for the DVTARGETDEVICE if necessary
dest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(sizeof(DVTARGETDEVICE));
// copy the contents of the source DVTARGETDEVICE into dest->ptd
*(dest->ptd) = *(source->ptd);
}
}
//
// Constructor
//
CEnumFormatEtc::CEnumFormatEtc(FORMATETC *pFormatEtc, int nNumFormats)
{
m_lRefCount = 1;
m_nIndex = 0;
m_nNumFormats = nNumFormats;
m_pFormatEtc = new FORMATETC[nNumFormats];
// copy the FORMATETC structures
for(int i = 0; i < nNumFormats; i++)
{
DeepCopyFormatEtc(&m_pFormatEtc[i], &pFormatEtc[i]);
}
}
//
// Destructor
//
CEnumFormatEtc::~CEnumFormatEtc()
{
if(m_pFormatEtc)
{
for(ULONG i = 0; i < m_nNumFormats; i++)
{
if(m_pFormatEtc[i].ptd)
CoTaskMemFree(m_pFormatEtc[i].ptd);
}
delete[] m_pFormatEtc;
}
}
//
// IUnknown::AddRef
//
ULONG __stdcall CEnumFormatEtc::AddRef(void)
{
// increment object reference count
return InterlockedIncrement(&m_lRefCount);
}
//
// IUnknown::Release
//
ULONG __stdcall CEnumFormatEtc::Release(void)
{
// decrement object reference count
LONG count = InterlockedDecrement(&m_lRefCount);
if(count == 0)
{
delete this;
return 0;
}
else
{
return count;
}
}
//
// IUnknown::QueryInterface
//
HRESULT __stdcall CEnumFormatEtc::QueryInterface(REFIID iid, void **ppvObject)
{
// check to see what interface has been requested
if(iid == IID_IEnumFORMATETC || iid == IID_IUnknown)
{
AddRef();
*ppvObject = this;
return S_OK;
}
else
{
*ppvObject = 0;
return E_NOINTERFACE;
}
}
//
// IEnumFORMATETC::Next
//
// If the returned FORMATETC structure contains a non-null "ptd" member, then
// the caller must free this using CoTaskMemFree (stated in the COM documentation)
//
HRESULT __stdcall CEnumFormatEtc::Next(ULONG celt, FORMATETC *pFormatEtc, ULONG * pceltFetched)
{
ULONG copied = 0;
// validate arguments
if(celt == 0 || pFormatEtc == 0)
return E_INVALIDARG;
// copy FORMATETC structures into caller's buffer
while(m_nIndex < m_nNumFormats && copied < celt)
{
DeepCopyFormatEtc(&pFormatEtc[copied], &m_pFormatEtc[m_nIndex]);
copied++;
m_nIndex++;
}
// store result
if(pceltFetched != 0)
*pceltFetched = copied;
// did we copy all that was requested?
return (copied == celt) ? S_OK : S_FALSE;
}
//
// IEnumFORMATETC::Skip
//
HRESULT __stdcall CEnumFormatEtc::Skip(ULONG celt)
{
m_nIndex += celt;
return (m_nIndex <= m_nNumFormats) ? S_OK : S_FALSE;
}
//
// IEnumFORMATETC::Reset
//
HRESULT __stdcall CEnumFormatEtc::Reset(void)
{
m_nIndex = 0;
return S_OK;
}
//
// IEnumFORMATETC::Clone
//
HRESULT __stdcall CEnumFormatEtc::Clone(IEnumFORMATETC ** ppEnumFormatEtc)
{
HRESULT hResult;
// make a duplicate enumerator
hResult = CreateEnumFormatEtc(m_nNumFormats, m_pFormatEtc, ppEnumFormatEtc);
if(hResult == S_OK)
{
// manually set the index state
((CEnumFormatEtc *) *ppEnumFormatEtc)->m_nIndex = m_nIndex;
}
return hResult;
}