IDropTarget を実装する

""

参考リンク:
 [IDropTargetを実装する](http://www4.ocn.ne.jp/~snao/droptarg.html)
 [7.IDropTargetとOLEドラッグ&ドロップ - COM研究室](http://atata.sakura.ne.jp/com/chap7.html)


## まずは定型

```
#include <shlobj.h>

class TDropTargetBase : public IDropTarget
{
protected:
	unsigned int refCount;

	TDropTargetBase();
	~TDropTargetBase();

	virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
	virtual ULONG __stdcall AddRef();
	virtual ULONG __stdcall Release();

};

TDropTargetBase::TDropTargetBase() {
	refCount = 0;
};

TDropTargetBase::~TDropTargetBase() {
};

HRESULT TDropTargetBase::QueryInterface(const IID& iid, void** ppv){
	if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropTarget))
		*ppv = static_cast<IDropTarget *>(this);
	else
	{
		*ppv = NULL;
		return E_NOINTERFACE;
	}

	AddRef();

	return S_OK;
}

ULONG TDropTargetBase::AddRef(){
	refCount++;
	return refCount;
}

ULONG TDropTargetBase::Release(){
	refCount--;
	if (refCount == 0) {
		delete this;
	}
	return refCount;
}
```

## IDropTarget::DragEnter, DragOver, DragLeave, Drop を実装する。
```
//	TDropTargetBase は、この説明で定型の部分を分離したかっただけで、本当はクラス分けする必要性は皆無。

class TDropTarget : public TDropTargetBase
{
public:
	static void CreateInstance(IDropTarget **);
private:
	virtual HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
	virtual HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
	virtual HRESULT __stdcall DragLeave();
	virtual HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect);
};

void TDropTarget::CreateInstance(IDropTarget **pp) {
	if (pp != NULL) {
		TDropTarget *p = new TDropTarget();
		p->QueryInterface(IID_IDropTarget, (void **)pp);
	}
}

#include <tchar.h>
#include <string>
typedef std::basic_string<TCHAR> tstring;

static void OutputDebugEffectString(DWORD *pdwEffect) {
	tstring sz;
	sz += TEXT("DROPEFFECT_");
	if (*pdwEffect == DROPEFFECT_NONE) {
		sz += TEXT("NONE");
	}
	else {
		if (*pdwEffect & DROPEFFECT_COPY) {
			sz += TEXT("COPY/");
		}
		if (*pdwEffect & DROPEFFECT_MOVE) {
			sz += TEXT("MOVE/");
		}
		if (*pdwEffect & DROPEFFECT_LINK) {
			sz += TEXT("LINK/");
		}
		if (*pdwEffect & DROPEFFECT_LINK) {
			sz += TEXT("SCROLL/");
		}
		sz.pop_back();
	}

	OutputDebugString(sz.data());
}

HRESULT TDropTarget::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect){
	OutputDebugString(TEXT("DragOver"));
	OutputDebugEffectString(pdwEffect);
	OutputDebugString(TEXT("\r\n"));
	return S_OK;
}

HRESULT TDropTarget::DragOver(DWORD keyState, POINTL pt, DWORD* effect){
	OutputDebugString(TEXT("DragOver"));
	OutputDebugString(TEXT("\r\n"));
	return S_OK;
}

HRESULT TDropTarget::DragLeave(){
	OutputDebugString(TEXT("DragLeave"));
	OutputDebugString(TEXT("\r\n"));
	return S_OK;
}

HRESULT TDropTarget::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect){
	OutputDebugString(TEXT("Drop"));
	OutputDebugEffectString(pdwEffect);
	OutputDebugString(TEXT("\r\n"));
	return S_OK;
}
```

## ウィンドウに割り当てる。

```
(ウィンドウプロシージャ内部)
	case WM_CREATE:
		...
		{
			IDropTarget *pDropTarget;
			TDropTarget::CreateInstance(&pDropTarget);
			RegisterDragDrop(hwnd, pDropTarget);	// OLE機能にIDropTargetを登録
			pDropTarget->Release();
		}
		...
		break;
	case WM_CLOSE:
		...
		RevokeDragDrop(hwnd);
		...
		break;

```

この時点で、もうファイルやテキストのドロップなどができる状態。

## DragEnter, Drop をちゃんと実装する。

```
HRESULT TDropTarget::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect){
	HRESULT    hr;
	FORMATETC  formatetc;

	// ドロップされようとしてるデータが CF_HDROP フォーマットをサポートしているかどうかを調べる
	formatetc.cfFormat = CF_HDROP;
	formatetc.ptd      = NULL;
	formatetc.dwAspect = DVASPECT_CONTENT;
	formatetc.lindex   = -1;
	formatetc.tymed    = TYMED_HGLOBAL;

	hr = pDataObj->QueryGetData(&formatetc);
	if(hr != S_OK){
		*pdwEffect = DROPEFFECT_NONE;
	}else{
		*pdwEffect = DROPEFFECT_COPY;
	}

	return (*pdwEffect == DROPEFFECT_NONE) ? DRAGDROP_S_CANCEL:
		S_OK;
	// ちゃんと DRAGDROP_S_CANCEL を返してやらないと、DROPEFFECT_NONE にも関わらず
	// Drop できてしまう。
}


static HRESULT GetDroppedFiles(IDataObject *pDataObj, std::list<tstring> *pDroppedFiles) {
	HRESULT hr;

	FORMATETC  formatetc;
	formatetc.cfFormat = CF_HDROP;
	formatetc.ptd      = NULL;
	formatetc.dwAspect = DVASPECT_CONTENT;
	formatetc.lindex   = -1;
	formatetc.tymed    = TYMED_HGLOBAL;

	hr = pDataObj->QueryGetData(&formatetc);
	if (FAILED(hr)) {
		goto fail;
	}

	STGMEDIUM medium;
	hr = pDataObj->GetData(&formatetc, &medium);
	if (FAILED(hr)) {
		goto fail;
	}

	HDROP p = (HDROP)GlobalLock(medium.hGlobal);

	int num = DragQueryFile(p, 0xFFFFFFFF, NULL, 0);
	for (int iFile = 0; iFile < num; iFile++) {
		int size = DragQueryFile(p, iFile, NULL, 0);

		TCHAR * buf = new TCHAR[size + 1];

		// assert(size == 
			DragQueryFile(p, iFile, buf, size + 1)
		// )
			;
		
		pDroppedFiles->push_back(tstring(buf));
		delete[] buf;
	}

	GlobalUnlock(medium.hGlobal);
	ReleaseStgMedium(&medium);
fail:
	return hr;
}

HRESULT TDropTarget::Drop(IDataObject* pDataObj, DWORD /*grfKeyState*/, POINTL /*pt*/, DWORD* pdwEffect) {
	HRESULT rc;

	OutputDebugString(TEXT("Drop : "));
	OutputDebugEffectString(pdwEffect);
	OutputDebugString(TEXT("\r\n"));

	std::list<tstring> droppedFiles;

	HRESULT hr = GetDroppedFiles(pDataObj, &droppedFiles);
	if (SUCCEEDED(hr) == false)
	{
		goto fail;
	}
	else
	{
		tstring debugString;

		for(std::list<tstring>::iterator ite = droppedFiles.begin();
			ite != droppedFiles.end();
			ite++) 
		{
			debugString += *ite;
			debugString += TEXT("\r\n");
		}

		OutputDebugString(debugString.data());
	}

	rc = S_OK;
	goto cleanUp;
fail:
	rc = E_FAIL;
	OutputDebugString(TEXT("Something wrong."));
cleanUp:
	return rc;
}

```