EternalWindows
シェル拡張 / ドロップ ハンドラ
サンプルはこちら

ドロップ ハンドラは、特定の拡張子を持ったファイルへのドロップを検出したい場合に使用します。 たとえば次の図では、sample.bhyにsample.txtをドロップしようとしています。

ドロップ ハンドラのオブジェクトは、IDropTargetとIPersistFileを実装する必要があります。 IDropTargetはファイルがドロップされたファイルを特定するために使用し、 IPersistFileはドロップされるファイル名を特定するために使用します。

class CDropTarget : public IDropTarget, public IPersistFile
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
	STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
	STDMETHODIMP DragLeave();
	STDMETHODIMP Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);

	STDMETHODIMP GetClassID(CLSID *pClassID);
	STDMETHODIMP IsDirty();
	STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode);
	STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember);
	STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName);
	STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName);

	CDropTarget();
	~CDropTarget();

private:
	LONG  m_cRef;
	WCHAR m_szFileName[256];
};

DragEnterからDropまでがIDropTargetのメソッドであり、 GetClassIDからGetCurFileまでがIPersistFileのメソッドになります。 Windows Vista以降では、IPersistFileではなくIInitializeWithFileなどを実装するべきですが、 下位互換性の事を考えてIPersistFileを実装するようにしています。

IDropTargetの中で最も重要なメソッドはDropです。 このメソッドはファイルをドロップした際に呼ばれるため、 ドロップされたファイルを特定する必要があります。

STDMETHODIMP CDropTarget::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
	int       i, nCount;
	TCHAR     szBuf[256];
	HRESULT   hr;
	FORMATETC formatetc;
	STGMEDIUM medium;

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

	hr = pDataObj->GetData(&formatetc, &medium);
	if (FAILED(hr))
		return E_FAIL;

	nCount = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0);
	for (i = 0; i < nCount; i++) {
		DragQueryFile((HDROP)medium.hGlobal, i, szBuf, sizeof(szBuf) / sizeof(TCHAR));
		MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
	}
	
	ReleaseStgMedium(&medium);
	
	return S_OK;
}

第1引数のIDataObjectは、データを様々なフォーマットで管理しています。 FORMATETC構造体は、データをどのようなフォーマットで取得するかを表すものであり、 cfFormatにCF_HDROPを指定した場合は、データをHDROP型で取得することができます。 STGMEDIUM構造体はデータを受け取るために存在し、 これはIDataObject::GetDataの呼び出しで初期化されます。 今回の場合、STGMEDIUM.hGlobalにはHDROP型のハンドルが格納されることになりますから、 これをDragQueryFileに指定してドロップされたファイルの数を取得することができます。 このとき第2引数は-1を指定します。 ファイルの数を取得したら、この数だけDragQueryFileを呼び出してドロップされたファイルのパスを取得します。

IDropTargetの残りのメソッドは、特に具体的な処理は必要ありません。

STDMETHODIMP CDropTarget::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
	return E_NOTIMPL;
}

STDMETHODIMP CDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
	return E_NOTIMPL;
}

STDMETHODIMP CDropTarget::DragLeave()
{
	return E_NOTIMPL;
}

DragEnterは、ドラッグ中のファイルが特定のファイルの中に入った際に呼ばれますが、 特に行うことはないためE_NOTIMPLを返しています。 DragOverは、通常は呼ばれないためE_NOTIMPLで問題ありません。 DragLeaveは、ドラッグ中のファイルが特定のファイルの外に出た際に呼ばれますが、 このときも特に行うことがないのでE_NOTIMPLを返すことになります。

IPersistFileの中で呼ばれるメソッドはLoadだけです。 このメソッドは、ドラッグ中のファイルが特定のファイルの中に入った際に呼ばれ、 S_OKを返した場合はIDropTargetのDragEnterが呼ばれます。

STDMETHODIMP CDropTarget::Load(LPCOLESTR pszFileName, DWORD dwMode)
{
	lstrcpyW(m_szFileName, pszFileName);

	return S_OK
}

pszFileNameにドロップされることになるファイルのパスが格納されています。 これは後で必要になることが予想されるため、メンバ変数に中身をコピーしています。

ドロップ ハンドラを登録するには、次に示すレジストリキーを作成することになります。

HKEY_CLASSES_ROOT
 <特定の拡張子> (既定) = オブジェクトのProgID

HKEY_CLASSES_ROOT
  <オブジェクトのProgID>
    shellex
      DropHandler (既定) = オブジェクトのCLSID

ドロップ ハンドラのDLLは、ファイルにアイコンが触れた際にシェルによってロードされ、 自動でアンロードされることはありません。 登録したDLLを置き換える場合は、登録を解除してからシェルを再起動する必要があります。


戻る