エクスプローラへドラッグアンドドロップする方法を紹介します。
ドラッグアンドドロップは、ドラッグアンドドロップ元(ソース)からドラッグアンドドロップ先(ターゲット)へデータオブジェクト(IDataObject)を転送する手続きです。
ドラッグアンドドロップを行うには、OLEのDoDragDropを呼び出します。
HRESULT DoDragDrop(IDataObject* pDataObj, IDropSource* pDropSrc,
DWORD dwOKEffect, DWORD* pdwEffect)
引数pDataObjは、転送用のデータオブジェクトを指定します。
引数pDropSrcは、操作中のユーザーへのフィードバックを返すドロップソースオブジェクトを指定します。
引数dwOKEffectは、ソースが認めるエフェクトです。以下の値の論理和(OR)を指定します。
引数pdwEffectは、DoDrawDropが制御を返したときの最後のエフェクトを受け取る出力引数です。
コードの例です。
OleInitialize(NULL);
IDataObject* dataObject = …;
TDropSource* source = …;
DWORD dropEffect;
HRESULT hr = DoDragDrop(dataObject, source, DROPEFFECT_COPY | DROPEFFECT_MOVE, &dropEffect);
if (dropEffect == DROPEFFECT_MOVE) {
//移動したときは元データを削除
}
OleUninitialize();
ドロップソースオブジェクトは、IDropSourceを実装します。
IUnknownの仮想関数に加えて、QueryContinueDrag関数とGiveFeedback関数を実装します。
QueryContinueDrag関数は、ドラッグアンドドロップ処理を継続するか終了するかを返します。
HRESULT __stdcall QueryContinueDrag(BOOL fEsc, DWORD grfKeyState)
引数fEscは、ESCキーの状態を示します。ESCが押されているときはTRUEになります。
引数grfKeyStateは、Ctrlキー・Altキー・Shiftキーの状態と、左・中・右マウスボタンの状態を示します。
QueryContinueDrag関数の実装例です。
HRESULT __stdcall QueryContinueDrag(BOOL fEsc, DWORD grfKeyState)
{
// ESCが押されたか、両方のボタンが押されている場合は中止する
if (fEsc ||
(MK_LBUTTON | MK_RBUTTON) == (grfKeyState & (MK_LBUTTON | MK_RBUTTON))) {
return DRAGDROP_S_CANCEL;
}
//マウスの左ボタンが離された場合はドロップ処理へ
if ((grfKeyState & (MK_LBUTTON | MK_RBUTTON)) == 0) {
return DRAGDROP_S_DROP;
}
//それ以外はD&D継続
return S_OK;
}
GiveFeedback関数は、マウスカーソルの形状を設定し、ユーザーにフィードバックを提供します。
DRAGDROP_S_USEDEFAULTCURSORSを返すと、標準のマウスカーソルになります。
GiveFeedback関数の実装例です。
HRESULT __stdcall GiveFeedback(DWORD dwEffect)
{
return DRAGDROP_S_USEDEFAULTCURSORS;
}
IDropSourceの実装例です。
class TDropSource : public IDropSource
{
public:
TDropSource() : FRefCount(0) {
}
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv)
{
IUnknown *punk = NULL;
if (riid == IID_IUnknown) {
punk = static_cast<IUnknown*>(this);
} else if (riid == IID_IDropSource) {
punk = static_cast<IDropSource*>(this);
}
*ppv = punk;
if (punk) {
punk->AddRef();
return S_OK;
} else {
return E_NOINTERFACE;
}
}
ULONG __stdcall AddRef()
{
InterlockedIncrement(&FRefCount);
return (ULONG)FRefCount;
}
ULONG __stdcall Release()
{
ULONG ret = (ULONG)InterlockedDecrement(&FRefCount);
if (ret == 0) {
delete this;
}
return ret;
}
/**
* ドラッグアンドドロップ処理を継続するか終了するかを返します。
*/
HRESULT __stdcall QueryContinueDrag(BOOL fEsc, DWORD grfKeyState)
{
// ESCが押されたか、両方のボタンが押されている場合は中止する
if (fEsc ||
(MK_LBUTTON | MK_RBUTTON) == (grfKeyState & (MK_LBUTTON | MK_RBUTTON))) {
return DRAGDROP_S_CANCEL;
}
//マウスの左ボタンが離された場合はドロップ処理へ
if ((grfKeyState & (MK_LBUTTON | MK_RBUTTON)) == 0) {
return DRAGDROP_S_DROP;
}
//それ以外はD&D継続
return S_OK;
}
/**
* マウスカーソルの形状を設定し、ユーザーにフィードバックを提供します。
*/
HRESULT __stdcall GiveFeedback(DWORD dwEffect)
{
//デフォルトのカーソルを使う
return DRAGDROP_S_USEDEFAULTCURSORS;
}
private:
LONG FRefCount;
};
データオブジェクトはIDataObjectを実装します。
ドラッグアンドドロップ処理の場合は、IShellFolder::GetUIObjectOfを使用して、 対象ファイルの項目識別子から、IDataObjectを得ます。
IDataObjectを取得する例です。
/**
* ファイル名からIDataObjectインターフェイスを取得
* @param FileName ファイル名
* @return 失敗したときはNULL
*/
IDataObject* GetFilePathDataObject(const UnicodeString& FileName)
{
//ファイル名をフォルダをファイル名に分けておく
UnicodeString dir = ExtractFileDir(FileName);
UnicodeString file = ExtractFileName(FileName);
//デスクトップのIShellFolderインターフェイスを取得
IShellFolder* desktop = NULL;
if (FAILED(SHGetDesktopFolder(&desktop))) { return NULL; }
//対象ファイルの親フォルダの項目識別子を得る
ITEMIDLIST* dirIDList;
DWORD eaten = 0, attributes = 0;
if (FAILED(desktop->ParseDisplayName(NULL, NULL, dir.c_str(), &eaten,
&dirIDList, &attributes))) {
desktop->Release();
return NULL;
}
//親フォルダのIShellFolderインターフェイスを取得
IShellFolder* targetFolder;
if (FAILED(desktop->BindToObject(dirIDList, NULL, IID_IShellFolder,
(void**)&targetFolder))) {
desktop->Release();
return NULL;
}
//対象ファイルの項目識別子を得る
LPITEMIDLIST filePidl;
if (FAILED(targetFolder->ParseDisplayName(NULL, NULL, file.c_str(), &eaten,
&filePidl, &attributes))) {
targetFolder->Release();
desktop->Release();
return NULL;
}
//対象ファイルの項目識別子から、IDataObjectインターフェイスを得る
IDataObject* dataObject = NULL;
targetFolder->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&filePidl,
IID_IDataObject, NULL, (void**)&dataObject);
targetFolder->Release();
desktop->Release();
return dataObject;
}
サンプルは何もないフォーム上からエクスプローラにドラッグアンドドロップすることで、ファイルをコピーします。
Uni1.h
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
void __fastcall FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
int X, int Y);
void __fastcall FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y);
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private: // ユーザー宣言
TPoint FMouseDownPt;
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
Unit1.cpp
// ---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include <shlobj.h>
// ---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
// ---------------------------------------------------------------------------
class TDropSource : public IDropSource
{
public:
TDropSource() : FRefCount(0) {
}
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv)
{
IUnknown *punk = NULL;
if (riid == IID_IUnknown) {
punk = static_cast<IUnknown*>(this);
} else if (riid == IID_IDropSource) {
punk = static_cast<IDropSource*>(this);
}
*ppv = punk;
if (punk) {
punk->AddRef();
return S_OK;
} else {
return E_NOINTERFACE;
}
}
ULONG __stdcall AddRef()
{
InterlockedIncrement(&FRefCount);
return (ULONG)FRefCount;
}
ULONG __stdcall Release()
{
ULONG ret = (ULONG)InterlockedDecrement(&FRefCount);
if (ret == 0) {
delete this;
}
return ret;
}
/**
* ドラッグアンドドロップ処理を継続するか終了するかを返します。
*/
HRESULT __stdcall QueryContinueDrag(BOOL fEsc, DWORD grfKeyState)
{
// ESCが押されたか、両方のボタンが押されている場合は中止する
if (fEsc ||
(MK_LBUTTON | MK_RBUTTON) == (grfKeyState & (MK_LBUTTON | MK_RBUTTON))) {
return DRAGDROP_S_CANCEL;
}
//マウスの左ボタンが離された場合はドロップ処理へ
if ((grfKeyState & (MK_LBUTTON | MK_RBUTTON)) == 0) {
return DRAGDROP_S_DROP;
}
//それ以外はD&D継続
return S_OK;
}
/**
* マウスカーソルの形状を設定し、ユーザーにフィードバックを提供します。
*/
HRESULT __stdcall GiveFeedback(DWORD dwEffect)
{
//デフォルトのカーソルを使う
return DRAGDROP_S_USEDEFAULTCURSORS;
}
private:
LONG FRefCount;
};
// ---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
OleInitialize(NULL);
}
// ---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
OleUninitialize();
}
// ---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
int X, int Y)
{
if (Button == mbLeft) {
FMouseDownPt = TPoint(X, Y);
}
}
//---------------------------------------------------------------------------
/**
* ファイル名からIDataObjectインターフェイスを取得
* @param FileName ファイル名
* @return 失敗したときはNULL
*/
IDataObject* GetFilePathDataObject(const UnicodeString& FileName)
{
//ファイル名をフォルダをファイル名に分けておく
UnicodeString dir = ExtractFileDir(FileName);
UnicodeString file = ExtractFileName(FileName);
//デスクトップのIShellFolderインターフェイスを取得
IShellFolder* desktop = NULL;
if (FAILED(SHGetDesktopFolder(&desktop))) { return NULL; }
//対象ファイルの親フォルダの項目識別子を得る
ITEMIDLIST* dirIDList;
DWORD eaten = 0, attributes = 0;
if (FAILED(desktop->ParseDisplayName(NULL, NULL, dir.c_str(), &eaten,
&dirIDList, &attributes))) {
desktop->Release();
return NULL;
}
//親フォルダのIShellFolderインターフェイスを取得
IShellFolder* targetFolder;
if (FAILED(desktop->BindToObject(dirIDList, NULL, IID_IShellFolder,
(void**)&targetFolder))) {
desktop->Release();
return NULL;
}
//対象ファイルの項目識別子を得る
LPITEMIDLIST filePidl;
if (FAILED(targetFolder->ParseDisplayName(NULL, NULL, file.c_str(), &eaten,
&filePidl, &attributes))) {
targetFolder->Release();
desktop->Release();
return NULL;
}
//対象ファイルの項目識別子から、IDataObjectインターフェイスを得る
IDataObject* dataObject = NULL;
targetFolder->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&filePidl,
IID_IDataObject, NULL, (void**)&dataObject);
targetFolder->Release();
desktop->Release();
return dataObject;
}
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
//左ボタンが押された状態で、マウスの位置が閾値を超えたらドラッグ開始
if (ControlState.Contains(csLButtonDown ) == false ||
((abs(X - FMouseDownPt.x) < Mouse->DragThreshold)) ||
(abs(Y - FMouseDownPt.y) < Mouse->DragThreshold)) {
return;
}
Perform(WM_LBUTTONUP, 0, 0);
// OLEドラッグ&ドロップ開始
IDataObject* dataObject = GetFilePathDataObject(L"C:\sample\test.txt");
if (dataObject == NULL) {
return;
}
DWORD dropEffect;
TDropSource* dropSource = new TDropSource();
dropSource->AddRef();
DoDragDrop(dataObject, dropSource, DROPEFFECT_COPY, &dropEffect);
dataObject->Release();
dropSource->Release();
}
OLE Drag&Drop エクスプローラにファイル名を受け渡す
参考にさせていただきました。
Inside OLE 改訂新版
図書館にあったので借りて読みました。OLE関連の情報が手に入りにくくなった現在、貴重な情報源です。