
Microsoft WordやInternet Explorerなど多くのアプリケーションではドラッグ・アンド・ドロップによってほかのアプリケーションとデータのやり取りができるように作られている。自分で作成したアプリケーションでドラッグ・アンド・ドロップによりデータを受け取るにはIDropTargetを利用した処理を実装する。
ここではIDropTargetの実装はが簡単にできるようにCDnpDropTargetImplという補助クラスを作成した。図の例ではInternet Explorerからドラッグ・アンド・ドロップされたデータのうち、テキストデータをメッセージボックスに表示している。
自分の好みに応じた処理を実装したい場合はCDropTarget内を変更する。そのほかの部分は多くの場合、共通に利用できる。
■ATL/WTLアプリケーションうインザードでダイアログベースのプロジェクトを作成する
■自動生成したソースコードのうち「プロジェクト名.cpp」のファイルを編集して、OleInitializeとOleUninitializeが実行されるようにする
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
(...省略...)
//ドラッグアンドドロップ用
::OleInitialize(NULL);
int nRet = Run(lpstrCmdLine, nCmdShow);
//ドラッグアンドドロップ用
::OleUninitialize();
_Module.Term();
::CoUninitialize();
return nRet;
}
■MainDlg.hの先頭に以下のようなドラッグアンドドロップのメイン処理の書かれたクラス宣言を追加する
#include "DnpDropTargetImpl.h"
class CDropTarget : public CDnpDropTargetImpl
{
protected:
virtual bool OnDrop(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
{
//テキストのみ処理
if(IsSupport(CF_TEXT))
{
bool ret;
CAtlString strData;
//テキストデータを取得
ret = GetTextData(strData);
if(ret)
{
//キー状態に応じてドラッグ・アンド・ドロップ元の動作を変えれる。
//移動(DROPEFFECT_MOVE)にすると、ドロップ元のデータは消える。
if(grfKeyState & MK_SHIFT)
*pdwEffect = DROPEFFECT_MOVE;
else
*pdwEffect = DROPEFFECT_COPY;
//取得文字列をメッセージボックスに表示
::MessageBox(NULL,strData,_T(""),MB_OK);
return true;
}
}
return false;
}
virtual bool OnDragOver(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
{
//ドラッグ元がテキスト形式(CF_TEXT)をサポートしている場合のみ処理
if(IsSupport(CF_TEXT))
{
//マウスカーソルの形を指定する。キー状態に応じて設定可能
//使用可能なキーは以下の通り
//MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON
//ここではシフトキーのみ監視
if(grfKeyState & MK_SHIFT)
*pdwEffect = DROPEFFECT_MOVE;
else
*pdwEffect = DROPEFFECT_COPY;
return true;
}
return false;
}
};
■MainDlg.hのクラスCMainDlg内にメンバ変数を追加する
//メンバ変数追加
CComPtr<IDropTarget> _pIDropTarget;
■MainDlg.hのクラスCMainDlgにあるOnInitDialog関数内にドラッグアンドドロップの登録処理を追加する
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
(...省略...)
HRESULT hr;
CComObject<CDropTarget>* pCDropTarget;
pCDropTarget = NULL;
hr = CComObject<CDropTarget>::CreateInstance(&pCDropTarget);
if(SUCCEEDED(hr))
{
_pIDropTarget = NULL;
hr = pCDropTarget->QueryInterface(IID_IDropTarget,(void**)&_pIDropTarget);
}
//ドラッグ・アンド・ドロップの受け入れ登録
if(SUCCEEDED(hr))
hr = ::RegisterDragDrop(m_hWnd,_pIDropTarget);
ATLASSERT(SUCCEEDED(hr)); //失敗した場合は起動時にOleInitializeかCoInitializeを実行していない可能性大
return TRUE;
}
■MainDlg.hのクラスCMainDlgにあるCloseDialog関数内でRevokeDragDropを実行してドラッグアンドドロップの登録解除処理をする
void CloseDialog(int nVal)
{
//ドラッグ・アンド・ドロップの受け入れ登録解除
::RevokeDragDrop(m_hWnd);
DestroyWindow();
::PostQuitMessage(nVal);
}
■ドラッグ・アンド・ドロップ用のクラス定義をファイル名「DnpDropTargetImpl.h」として保存
#pragma once
#include "atlstr.h"
//
//
//■使用例
//
//#include "DnpDropTargetImpl.h"
//
//
/////
/////プログラム起動時にCoInitializeとOleInitializeが実行されるようにする
/////プログラム終了時にOleUninitializeとCoUninitializeが実行されるようにする
/////
//int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
//{
// HRESULT hRes = ::CoInitialize(NULL);
//
// (...省略...)
//
// //ドラッグアンドドロップ用
// ::OleInitialize(NULL);
//
// (メイン処理)
//
// //ドラッグアンドドロップ用
// ::OleUninitialize();
//
// ::CoUninitialize();
//
// return nRet;
//}
//
//
////
////ドラッグアンドドロップの挙動を実装したクラスを作成
////
//class CDropTarget : public CDnpDropTargetImpl
//{
//protected:
// virtual bool OnDrop(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
// {
// //テキストのみ処理
// if(IsSupport(CF_TEXT))
// {
// bool ret;
// CAtlString strData;
//
// //テキストデータを取得
// ret = GetTextData(strData);
// if(ret)
// {
// //キー状態に応じてドラッグ・アンド・ドロップ元の動作を変えれる。
// //移動(DROPEFFECT_MOVE)にすると、ドロップ元のデータは消える。
// if(grfKeyState & MK_SHIFT)
// *pdwEffect = DROPEFFECT_MOVE;
// else
// *pdwEffect = DROPEFFECT_COPY;
//
// //取得文字列をメッセージボックスに表示
// ::MessageBox(NULL,strData,_T(""),MB_OK);
//
//
// return true;
// }
// }
//
// return false;
// }
//
// virtual bool OnDragOver(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
// {
// //ドラッグ元がテキスト形式(CF_TEXT)をサポートしている場合のみ処理
// if(IsSupport(CF_TEXT))
// {
// //マウスカーソルの形を指定する。キー状態に応じて設定可能
// //使用可能なキーは以下の通り
// //MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON
//
// //ここではシフトキーのみ監視
// if(grfKeyState & MK_SHIFT)
// *pdwEffect = DROPEFFECT_MOVE;
// else
// *pdwEffect = DROPEFFECT_COPY;
//
// return true;
// }
//
// return false;
// }
//};
//
//
////
////ドラッグアンドドロップを受け入れたいウインドウに処理を追加
////
//class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,
// public CMessageFilter, public CIdleHandler
//{
//
// //メンバ変数追加
// CComPtr<IDropTarget> _pIDropTarget;
//
//public:
// (...省略...)
//
// //ウインドウ初期化時に登録処理を追加
// LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// {
// (...省略...)
//
//
// HRESULT hr;
// CComObject<CDropTarget>* pCDropTarget;
//
// pCDropTarget = NULL;
// hr = CComObject<CDropTarget>::CreateInstance(&pCDropTarget);
//
// if(SUCCEEDED(hr))
// {
// _pIDropTarget = NULL;
// hr = pCDropTarget->QueryInterface(IID_IDropTarget,(void**)&_pIDropTarget);
// }
//
// //ドラッグ・アンド・ドロップの受け入れ登録
// if(SUCCEEDED(hr))
// hr = ::RegisterDragDrop(m_hWnd,_pIDropTarget);
//
// ATLASSERT(SUCCEEDED(hr)); //失敗した場合は起動時にOleInitializeかCoInitializeを実行していない可能性大
//
// return TRUE;
// }
//
// //ウインドウ終了時に登録解除処理を追加
// void CloseDialog(int nVal)
// {
// //ドラッグ・アンド・ドロップの受け入れ登録解除
// ::RevokeDragDrop(m_hWnd);
//
// DestroyWindow();
// ::PostQuitMessage(nVal);
// }
//};
//
//
class CDnpDropTargetImpl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CDnpDropTargetImpl>,
public IDropTarget
{
protected:
IDataObject* _pIDataObject;
public:
BEGIN_COM_MAP(CDnpDropTargetImpl)
COM_INTERFACE_ENTRY(IDropTarget)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
CDnpDropTargetImpl()
{
_pIDataObject = NULL;
}
///////////////////////////////////////
// IDropTargetの実装
//
//
//ドラッグ・アンド・ドロップの開始
//
STDMETHOD(DragEnter)(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
{
//_pIDataObjectにIDataObjectを保存しておく
if(_pIDataObject)
_pIDataObject->Release();
_pIDataObject = NULL;
pDataObject->QueryInterface(IID_IDataObject,(void**)&_pIDataObject);
//以下共通処理
return DragOver(grfKeyState,pt,pdwEffect);
}
//
//ドラッグ・アンド・ドロップ中
//
STDMETHOD(DragOver)(DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
{
if(_pIDataObject == NULL || pdwEffect == NULL)
return E_FAIL;
*pdwEffect = DROPEFFECT_NONE;
return OnDragOver(_pIDataObject,grfKeyState,pt,pdwEffect) ? S_OK : E_FAIL;
}
//
//ドラッグ・アンド・ドロップ終了
//
STDMETHOD(DragLeave)(void)
{
//_pIDataObjectが必要なくなったので解放
if(_pIDataObject)
_pIDataObject->Release();
_pIDataObject = NULL;
return S_OK;
}
//
//ドラッグ・アンド・ドロップ終了
//
STDMETHOD(Drop)(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
{
bool ret;
//_pIDataObjectを終了時のものに変更
if(_pIDataObject)
_pIDataObject->Release();
_pIDataObject = NULL;
//GetTextData()などをOnDrop内で使えるようにQueryInterfaceが必要
pDataObject->QueryInterface(IID_IDataObject,(void**)&_pIDataObject);
ret = OnDrop(_pIDataObject,grfKeyState,pt,pdwEffect);
if(_pIDataObject)
_pIDataObject->Release();
_pIDataObject = NULL;
return ret ? S_OK : E_FAIL;
}
//
// IDropTargetの実装ここまで
///////////////////////////////////////
protected:
virtual bool OnDrop(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect) = 0;
virtual bool OnDragOver(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect) = 0;
public:
//
//テキストデータの取得
//
bool GetTextData(CAtlString& strData)
{
HRESULT hr;
FORMATETC sFormatEtc;
STGMEDIUM sStgMedium;
strData = _T("");
if(_pIDataObject == NULL)
return false;
::ZeroMemory(&sFormatEtc,sizeof(FORMATETC));
sFormatEtc.cfFormat = CF_TEXT;
sFormatEtc.dwAspect = DVASPECT_CONTENT;
sFormatEtc.lindex = -1;
sFormatEtc.tymed = TYMED_HGLOBAL;
hr = _pIDataObject->GetData(&sFormatEtc,&sStgMedium);
if(FAILED(hr))
return false;
strData = (char*)::GlobalLock(sStgMedium.hGlobal);
::GlobalUnlock(sStgMedium.hGlobal);
::ReleaseStgMedium(&sStgMedium);
return true;
}
//
//対応形式のチェック
//
bool IsSupport(CLIPFORMAT nFormat)
{
ULONG i;
ULONG nCount;
HRESULT hr;
FORMATETC pFormatEtc[256];
IEnumFORMATETC* pIEnumFORMATETC;
pIEnumFORMATETC = NULL;
hr = _pIDataObject->EnumFormatEtc(DATADIR_GET,&pIEnumFORMATETC);
if(FAILED(hr) || pIEnumFORMATETC == NULL)
return false;
hr = pIEnumFORMATETC->Next(256,pFormatEtc,&nCount);
if(FAILED(hr))
return false;
for(i = 0; i < nCount; i++)
{
if(pFormatEtc[i].cfFormat == nFormat)
return true;
}
return false;
}
};
プロジェクトファイルをダウンロード