EternalWindows
OLE D&D / ドロップソース

ドラッグされたデータを受け取る側をドロップターゲットと呼ぶのに対し、 データをドラッグしてドロップする側をドロップソースと呼びます。 今回のアプリケーションはウインドウに2つの図形を描画しており、 この図形のどちらかをD&Dできるようになっています。

ドロップソースとなるアプリケーションは前節のものでも構いませんし、上図のようにWordでも構いません。 実際にドロップを行うと、ドロップターゲットのウインドウに図形が描画されるはずです。

ドロップソースが実装すべきインターフェースは、IDropSourceとIDataObjectです。 IDropSourceはドラッグを継続するかどうかなどに関するメソッドを持っており、 IDataObjectはドロップされることになるデータを管理するメソッドを持っています。 それぞれのインターフェースを実装したオブジェクトを作成したら、 DoDragDropでドラッグを開始することができます。

HRESULT DoDragDrop(
  LPDATAOBJECT pDataObj,
  LPDROPSOURCE pDropSource,
  DWORD dwOKEffects,
  LPDWORD pdwEffect
);

pDataObjは、IDataObjectを実装したオブジェクトのアドレスを指定します。 pDropSourceは、IDropSourceを実装したオブジェクトのアドレスを指定します。 dwOKEffectsは、ドロップの際に許可するエフェクトを指定します。 pdwEffectは、IDropTarget::DropがpdwEffectに設定した値が返ります。 ドラッグがキャンセルされた場合は、DROPEFFECT_NONEが返ります。

DoDragDropを呼び出すと、OLEによってマウスカーソルの動きが追跡されるようになります。 たとえば、カーソルがドロップターゲットのウインドウの中に入った場合はIDropTarget::DragEnterが呼ばれることになります。 このメソッドではエフェクトを返すようになっていたため、 ドロップソースはこのエフェクトに応じてカーソルの形を変えなければなりません。 よって、この機会を与えるためにIDropSource::GiveFeedbackが呼ばれます。 また、ユーザーがEscキーを押した場合やマウスの左ボタンが離された場合は、 IDropSource::QueryContinueDragが呼ばれます。 このメソッドではドラッグを継続するかどうかを決定することができ、 キャンセルやドロップの実行を示す定数を返した場合は、DoDragDropが制御を返すことになります。

アプリケーションが用意したデータをどのようにドロップさせるかによって、 DoDragDropの第3引数は異なります。 たとえば、データの移動のみを許可したい場合はDROPEFFECT_MOVEを指定しますが、 データの移動とコピーを許可したい場合はDROPEFFECT_MOVE | DROPEFFECT_COPYを指定します。 DROPEFFECT_COPYが指定されていることによって、ユーザーはドラッグ中にCtrlキーを押すことによって、 データをコピーすることができます。

今回のプログラムは、ウインドウ上に描画された図形をWordにドロップします。

#include <windows.h>

class CDropSource : public IDropSource
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);
	STDMETHODIMP GiveFeedback(DWORD dwEffect);

	CDropSource();
	~CDropSource();

private:
	LONG m_cRef;
};

class CDataObject : public IDataObject, public IEnumFORMATETC
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
	STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium);
	STDMETHODIMP QueryGetData(FORMATETC *pformatetc);
	STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut);
	STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease);
	STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc);
	STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection);
	STDMETHODIMP DUnadvise(DWORD dwConnection);
	STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise);
	
	STDMETHODIMP Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched);
	STDMETHODIMP Skip(ULONG celt);
	STDMETHODIMP Reset(VOID);
	STDMETHODIMP Clone(IEnumFORMATETC **ppenum);
	
	CDataObject();
	~CDataObject();

private:
	LONG         m_cRef;
	ULONG        m_uEnumCount;
	HENHMETAFILE m_hEnhMetaFile;
};

struct DRAGDATA {
	RECT rc;
	BOOL bRectangle;
};
typedef struct DRAGDATA DRAGDATA;
typedef struct DRAGDATA *LPDRAGDATA;

BOOL SetDragData(IDataObject *pDataObject, LPDRAGDATA lpDragData);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	const int          nDragDataMaxCount = 2;
	static DRAGDATA    dragData[nDragDataMaxCount] = {0};
	static IDropSource *pDropSource = NULL;
	static IDataObject *pDataObject = NULL;
	

	switch (uMsg) {

	case WM_CREATE:
		OleInitialize(NULL);

		pDropSource = new CDropSource();
		pDataObject = new CDataObject();

		dragData[0].bRectangle = TRUE;
		SetRect(&dragData[0].rc, 30, 30, 150, 120);

		dragData[1].bRectangle = FALSE;
		SetRect(&dragData[1].rc, 200, 30, 320, 120);

		return 0;
	
	case WM_PAINT: {
		int         i;
		HDC         hdc;
		RECT        rc;
		PAINTSTRUCT ps;

		hdc = BeginPaint(hwnd, &ps);

		for (i = 0; i < nDragDataMaxCount; i++) {
			rc = dragData[i].rc;
			if (dragData[i].bRectangle)
				Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
			else
				Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom);
		}

		EndPaint(hwnd, &ps);

		return 0;
	}
	
	case WM_LBUTTONDOWN: {
		int   i;
		POINT pt;
		DWORD dwEffect;

		pt.x = LOWORD(lParam);
		pt.y = HIWORD(lParam);

		for (i = 0; i < nDragDataMaxCount; i++) {
			if (PtInRect(&dragData[i].rc, pt)) {
				SetDragData(pDataObject, &dragData[i]);
				DoDragDrop(pDataObject, pDropSource, DROPEFFECT_COPY, &dwEffect);
				break;
			}
		}
		
		return 0;
	}

	case WM_DESTROY:
		if (pDataObject != NULL)
			pDataObject->Release();
		if (pDropSource != NULL)
			pDropSource->Release();
		OleUninitialize();
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL SetDragData(IDataObject *pDataObject, LPDRAGDATA lpDragData)
{
	HRESULT      hr;
	HDC          hdc;
	RECT         rc;
	HENHMETAFILE hEnhMetaFile;
	FORMATETC    formatetc;
	STGMEDIUM    medium;
	
	hdc = CreateEnhMetaFile(NULL, NULL, NULL, NULL);

	rc = lpDragData->rc;
	if (lpDragData->bRectangle)
		Rectangle(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top);
	else
		Ellipse(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top);

	hEnhMetaFile = CloseEnhMetaFile(hdc);

	formatetc.cfFormat = CF_ENHMETAFILE;
	formatetc.ptd      = NULL;
	formatetc.dwAspect = DVASPECT_CONTENT;
	formatetc.lindex   = -1;
	formatetc.tymed    = TYMED_ENHMF;

	medium.tymed = TYMED_ENHMF;
	medium.hEnhMetaFile = hEnhMetaFile;
	medium.pUnkForRelease = NULL;

	hr = pDataObject->SetData(&formatetc, &medium, TRUE);

	return hr == S_OK;
}


// CDropSource


CDropSource::CDropSource()
{
	m_cRef = 1;
}

CDropSource::~CDropSource()
{
}

STDMETHODIMP CDropSource::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDropSource))
		*ppvObject = static_cast<IDropSource *>(this);
	else
		return E_NOINTERFACE;

	AddRef();

	return S_OK;
}

STDMETHODIMP_(ULONG) CDropSource::AddRef()
{
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CDropSource::Release()
{
	if (InterlockedDecrement(&m_cRef) == 0) {
		delete this;
		return 0;
	}

	return m_cRef;
}

STDMETHODIMP CDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
	if (fEscapePressed)
		return DRAGDROP_S_CANCEL;

	if ((grfKeyState & MK_LBUTTON) == 0)
		return DRAGDROP_S_DROP;

	return S_OK;
}

STDMETHODIMP CDropSource::GiveFeedback(DWORD dwEffect)
{
	return DRAGDROP_S_USEDEFAULTCURSORS;
}


// CDataObject


CDataObject::CDataObject()
{
	m_cRef = 1;
	m_uEnumCount = 0;
	m_hEnhMetaFile = NULL;
}

CDataObject::~CDataObject()
{
	if (m_hEnhMetaFile != NULL)
		DeleteEnhMetaFile(m_hEnhMetaFile);
}

STDMETHODIMP CDataObject::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDataObject))
		*ppvObject = static_cast<IDataObject *>(this);
	else if (IsEqualIID(riid, IID_IEnumFORMATETC))
		*ppvObject = static_cast<IEnumFORMATETC *>(this);
	else
		return E_NOINTERFACE;

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CDataObject::AddRef()
{
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CDataObject::Release()
{
	if (InterlockedDecrement(&m_cRef) == 0) {
		delete this;
		return 0;
	}

	return m_cRef;
}

STDMETHODIMP CDataObject::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
{
	if (pformatetcIn->cfFormat == CF_ENHMETAFILE) {
		pmedium->tymed = TYMED_ENHMF;
		pmedium->hEnhMetaFile = m_hEnhMetaFile;
		pmedium->pUnkForRelease = NULL;
	}
	else
		return E_FAIL;

	return S_OK;
}

STDMETHODIMP CDataObject::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
{
	return E_NOTIMPL;
}

STDMETHODIMP CDataObject::QueryGetData(FORMATETC *pformatetc)
{
	if (pformatetc->cfFormat == CF_ENHMETAFILE)
		return S_OK;

	return S_FALSE;
}

STDMETHODIMP CDataObject::GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut)
{
	return E_NOTIMPL;
}

STDMETHODIMP CDataObject::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
{
	if (pformatetc->cfFormat == CF_ENHMETAFILE) {
		if (m_hEnhMetaFile != NULL) {
			DeleteEnhMetaFile(m_hEnhMetaFile);
			m_hEnhMetaFile = NULL;
		}
		
		m_hEnhMetaFile = CopyEnhMetaFile(pmedium->hEnhMetaFile, NULL);

		if (fRelease)
			DeleteEnhMetaFile(pmedium->hEnhMetaFile);
	}
	else
		return E_FAIL;
	
	return S_OK;
}

STDMETHODIMP CDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
{
	if (dwDirection == DATADIR_GET)
		return QueryInterface(IID_PPV_ARGS(ppenumFormatEtc));
	else
		return E_NOTIMPL;
}

STDMETHODIMP CDataObject::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
	return E_NOTIMPL;
}

STDMETHODIMP CDataObject::DUnadvise(DWORD dwConnection)
{
	return E_NOTIMPL;
}

STDMETHODIMP CDataObject::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
{
	return E_NOTIMPL;
}

STDMETHODIMP CDataObject::Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched)
{
	FORMATETC formatetc[] = {
		{CF_ENHMETAFILE, NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF}
	};

	if (m_uEnumCount >= 1)
		return S_FALSE;
	
	*rgelt = formatetc[m_uEnumCount];

	if (pceltFetched != NULL)
		*pceltFetched = 1;
	
	m_uEnumCount++;

	return S_OK;
}

STDMETHODIMP CDataObject::Skip(ULONG celt)
{
	return E_NOTIMPL;
}

STDMETHODIMP CDataObject::Reset(VOID)
{
	m_uEnumCount = 0;

	return S_OK;
}

STDMETHODIMP CDataObject::Clone(IEnumFORMATETC **ppenum)
{
	return E_NOTIMPL;
}

DoDragDropを呼び出すには、IDropSourceを実装したオブジェクトとIDataObjectを実装したオブジェクトが必要になるため、 それぞれをWM_CREATEで作成しています。 また、ドラッグできるデータ(長方形と楕円)をDRAGDATA構造体の配列に格納し、 いつでもクリックできるようにしています。

マウスの左ボタンが押された場合は、次の処理が実行されます。

case WM_LBUTTONDOWN: {
	int   i;
	POINT pt;
	DWORD dwEffect;

	pt.x = LOWORD(lParam);
	pt.y = HIWORD(lParam);

	for (i = 0; i < nDragDataMaxCount; i++) {
		if (PtInRect(&dragData[i].rc, pt)) {
			SetDragData(pDataObject, &dragData[i]);
			DoDragDrop(pDataObject, pDropSource, DROPEFFECT_COPY, &dwEffect);
			break;
		}
	}
	
	return 0;
}

ウインドウに描画された図形が押されたかどうかをPtInRectで確認し、 もし押されている場合はその図形をドラッグの対象とみなします。 SetDragDataという自作関数では図形のデータをIDataObjectに設定し、 これが完了したらDoDragDropでドラッグを開始します。 第3引数にDROPEFFECT_COPYを指定していることから、データはコピーとしてドラッグされることになります。 SetDragDataの内部は次のようになっています。

BOOL SetDragData(IDataObject *pDataObject, LPDRAGDATA lpDragData)
{
	HRESULT      hr;
	HDC          hdc;
	RECT         rc;
	HENHMETAFILE hEnhMetaFile;
	FORMATETC    formatetc;
	STGMEDIUM    medium;
	
	hdc = CreateEnhMetaFile(NULL, NULL, NULL, NULL);

	rc = lpDragData->rc;
	if (lpDragData->bRectangle)
		Rectangle(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top);
	else
		Ellipse(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top);

	hEnhMetaFile = CloseEnhMetaFile(hdc);

	formatetc.cfFormat = CF_ENHMETAFILE;
	formatetc.ptd      = NULL;
	formatetc.dwAspect = DVASPECT_CONTENT;
	formatetc.lindex   = -1;
	formatetc.tymed    = TYMED_ENHMF;

	medium.tymed = TYMED_ENHMF;
	medium.hEnhMetaFile = hEnhMetaFile;
	medium.pUnkForRelease = NULL;

	hr = pDataObject->SetData(&formatetc, &medium, TRUE);

	return hr == S_OK;
}

データが実際にドロップされた際には、ドロップターゲットがIDataObject;;GetDataを呼び出すことから、 IDataObjectに設定するデータはドロップターゲットがサポートするものでなければなりません。 Wordの場合はデータを拡張メタファイルとして受け取れるようになっているため、 今回は拡張メタファイルのデータをIDataObjectに設定しています。 拡張メタファイルを作成するにはCreateEnhMetaFileでデバイスコンテキストのハンドルを取得し、 これにドロップ対象の図形を描画してからCloseEnhMetaFileを呼び出せば、 拡張メタファイルのハンドルを取得することができます。 IDataObject::SetDataの第1引数は設定したいデータのフォーマットであり、 cfFormatメンバにはCF_ENHMETAFILEを指定しておくようにします。 第2引数は設定したいデータを格納したSTGMEDIUM構造体のアドレスであり、 hEnhMetaFileメンバに拡張メタファイルのハンドルを指定します。

データのドラッグ中に、ドラッグの継続に関わる動作が行われた場合はIDropSource::QueryContinueDragが呼ばれます。

STDMETHODIMP CDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
	if (fEscapePressed)
		return DRAGDROP_S_CANCEL;

	if ((grfKeyState & MK_LBUTTON) == 0)
		return DRAGDROP_S_DROP;

	return S_OK;
}

fEscapePressedがTRUEである場合は、Escキーが押された事を意味します。 このときには、DRAGDROP_S_CANCELを返すことでドラッグを中断させるのが一般的です。 grfKeyStateは現在のキーの状態が格納されており、 これにMK_LBUTTONが含まれていないということは、 マウスの左ボタンが離されたことを意味します。 このときには、ドロップが発生したとみなすべきですから、DRAGDROP_S_DROPを返すようにしています。 これにより、IDropTarget::Dropが呼ばれることになります。

OLEはデータがドロップターゲットのウインドウに入った場合にIDropTarget::DragEnterを呼び出しますが、 こうしたメソッドではサポートするフォーマットや現在のキーの状態などを考慮して、特定のエフェクトを返します。 このエフェクトを考慮してカーソルの形を返すべき際には、IDropSource::GiveFeedbackが呼ばれます。

STDMETHODIMP CDropSource::GiveFeedback(DWORD dwEffect)
{
	return DRAGDROP_S_USEDEFAULTCURSORS;
}

このメソッドではdwEffectを考慮してカーソルの形を変えることができるのですが、 そうした独自のカーソルを使用するつもりがない場合はDRAGDROP_S_USEDEFAULTCURSORSを返すだけで構いません。 この場合、OLEによってカーソルの形が適切に変更されることになります。 たとえば、dwEffectにDROPEFFECT_COPYが格納されている場合は、カーソルに+の記号が表示されます。

続いて、IDataObjectのメソッドについて見ていきます。 今回のアプリケーションはIDataObjectに拡張メタファイルを渡す目的で、SetDataを呼び出します。

STDMETHODIMP CDataObject::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
{
	if (pformatetc->cfFormat == CF_ENHMETAFILE) {
		if (m_hEnhMetaFile != NULL) {
			DeleteEnhMetaFile(m_hEnhMetaFile);
			m_hEnhMetaFile = NULL;
		}
		
		m_hEnhMetaFile = CopyEnhMetaFile(pmedium->hEnhMetaFile, NULL);

		if (fRelease)
			DeleteEnhMetaFile(pmedium->hEnhMetaFile);
	}
	else
		return E_FAIL;
	
	return S_OK;
}

渡された拡張メタファイルはm_hEnhMetaFileとして保存することになりますが、 既にこれが初期化されている場合は先に開放処理を行います。 fReleaseがTRUEである場合は渡された拡張メタファイルの開放をメソッドに任せるということなので、 開放処理を行っています。

データを受け取ることになるドロップターゲットは、データがドロップされた段階でIDataObject::GetDataを呼び出します。

STDMETHODIMP CDataObject::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
{
	if (pformatetcIn->cfFormat == CF_ENHMETAFILE) {
		pmedium->tymed = TYMED_ENHMF;
		pmedium->hEnhMetaFile = m_hEnhMetaFile;
		pmedium->pUnkForRelease = NULL;
	}
	else
		return E_FAIL;

	return S_OK;
}

今回のアプリケーションは、データを拡張メタファイル形式で扱うことになっているため、 CF_ENHMETAFILEが指定されたときのみデータを返すことになります。 hEnhMetaFileメンバを初期化する場合は、tymedメンバにTYMED_ENHMFを指定します。

ドロップターゲットは、IDataObjectが特定のフォーマットをサポートしているかを調べるべく、 IDataObject::QueryGetDataを呼び出すことがあります。

STDMETHODIMP CDataObject::QueryGetData(FORMATETC *pformatetc)
{
	if (pformatetc->cfFormat == CF_ENHMETAFILE)
		return S_OK;

	return S_FALSE;
}

GetDataでも述べたように、今回サポートするフォーマットはCF_ENHMETAFILEのみですから、 この場合にS_OKを返すようにします。 本来ならば、FORMATETC構造体の他のメンバも比較するべきかもしれませんが、 今回はcfFormatメンバのみを考慮しています。

ドロップターゲットは、IDataObjectがサポートしているフォーマットを列挙したいと思うかもしれません。 この要望に応えるためには、CDataObjectがIEnumFORMATETCを実装しており、 EnumFormatEtcでIEnumFORMATETCを返すようになっていなければなりません。 そして、IEnumFORMATETC::Nextでサポートするフォーマットを返します。

STDMETHODIMP CDataObject::Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched)
{
	FORMATETC formatetc[] = {
		{CF_ENHMETAFILE, NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF}
	};

	if (m_uEnumCount >= 1)
		return S_FALSE;
	
	*rgelt = formatetc[m_uEnumCount];

	if (pceltFetched != NULL)
		*pceltFetched = 1;
	
	m_uEnumCount++;

	return S_OK;
}

*rgeltに、返すことになるフォーマットを格納します。 今回はサポートするフォーマットが1つであるため、配列の要素数は1となっており、 列挙は1回だけ行われることになります。

IDropSourceNotifyについて

Windows Vistaからのドロップソースは、IDropSourceNotifyを実装することでカーソルの動きを追跡できるようになります。 IDropSourceNotifyを実装する場合は、クラスの定義が次のようになります。

class CDropSource : public IDropSource, public IDropSourceNotify
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);
	STDMETHODIMP GiveFeedback(DWORD dwEffect);
	
	STDMETHODIMP DragEnterTarget(HWND hwndTarget);
	STDMETHODIMP DragLeaveTarget();

	CDropSource();
	~CDropSource();

private:
	LONG m_cRef;
};

IDropSourceNotifyを実装しているため、QueryInterfaceではIDropSourceNotifyを返すようにします。 DragEnterTargetとDragLeaveTargetが、IDropSourceNotifyのメソッドになります。

STDMETHODIMP CDropSource::DragEnterTarget(HWND hwndTarget)
{
	return S_OK;
}

STDMETHODIMP CDropSource::DragLeaveTarget()
{
	return S_OK;
}

DragEnterTargetはカーソルが何からのウインドウの中に入った場合に呼ばれ、 DragLeaveTargetはカーソルがウインドウの外に出た場合に呼ばれます。



戻る