今度はExplorerのコンテキストメニューを追加します。
ここで扱う「コンテキストメニュー」は,ExplorerでファイルをDrag&Dropしたときに表示される,「ここにコピー」などが入っているものです。
次のようなCOM実装を行うと,Drag&Dropされたファイル名を取得して処理を行うことができるようになります。
1. Wizard を使って,IDL に実装クラスを定義する。
import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(70C82BD2-E659-41A4-AAE8-27DC030F82BA), helpstring("IMyCtxExt インターフェイス"), pointer_default(unique) ] interface IMyCtxExt : IUnknown{ }; ................................................... [ uuid(7A7CEC4E-FD39-4983-9A3F-C791214721BC), version(1.0), helpstring("CtxExtTest 1.0 タイプ ライブラリ") ] library HardLinkColumnLib { importlib("stdole2.tlb"); [ uuid(D2E45164-9547-4356-8513-937EAF39421D), helpstring("MyCtxExt Class") ] coclass MyCtxExt { [default] interface IMyCtxExt; }; .................................................... };
2. IShellExtInit, IContextMenu を継承(実装)する COM クラスを定義する。
class ATL_NO_VTABLE CMyCtxExt : public CComObjectRootEx, public CComCoClass , public IShellExtInit, public IContextMenu { std::vector selectedFiles; // 選択されたファイルを保存 ................................................ // IShellExtInit STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY); // IContextMenu STDMETHODIMP GetCommandString(UINT, UINT, UINT*, LPSTR, UINT) { return E_NOTIMPL; } // 今回は使用していない STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO); STDMETHODIMP QueryContextMenu(HMENU, UINT, UINT, UINT, UINT); };
3. Initialize メソッドでは,処理対象として選択されたファイル一覧を取得。
// Initialize // 選択されたファイルのリストを取得する HRESULT CMyCtxExt::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID) { // パラメータとして与えられたDataObjectから,Dropファイル情報を取得する FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stg = { TYMED_HGLOBAL }; if(FAILED(pDataObj->GetData(&fmt, &stg))) return E_INVALIDARG; HDROP hdrop = (HDROP)::GlobalLock(stg.hGlobal); if(hdrop == NULL) return E_INVALIDARG; // 選択されたファイル一覧を取得し,インスタンスに保存する selectedFiles.clear(); UINT uNumFiles = ::DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0); for(UINT uFile = 0; uFile < uNumFiles; ++uFile) { TCHAR szFile[MAX_PATH]; if(!DragQueryFile(hdrop, uFile, szFile, MAX_PATH)) continue; selectedFiles.push_back(tstring(szFile)); }レジストリへの登録は,次のスクリプトで。 // パラメータ情報を開放し,処理結果を返す // 対象ファイルが1つ以上あれば OK ::GlobalUnlock(stg.hGlobal); return selectedFiles.size() > 0 ? S_OK : E_FAIL; }
4. QueryContextMenu では,表示するメニューの追加を行う
// QueryContextMenu // コンテキストメニューを設定する HRESULT CLinkCtxExt::QueryContextMenu(HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags) { // デフォルトメニュー表示が指示されている場合は何もしない if(uFlags & CMF_DEFAULTONLY) return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0); // コンテキストメニューに自身のメニューを追加する ::InsertMenu(hmenu, uMenuIndex, MF_STRING|MF_BYPOSITION, uidFirstCmd, "My Context Menu"); return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1); }
5. InvokeCommand に実施に行う処理を記述する。対象となるファイルリストは,Initialize で取得しておいたもの。
6. レジストリへの登録は,次のスクリプトで。
HKCR { NoRemove CLSID { ForceRemove {D2E45164-9547-4356-8513-937EAF39421D} = s 'MyCtx Extension Class' { InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Apartment' } } } NoRemove * { NoRemove shellex { NoRemove ContextMenuHandlers { ForceRemove MyCtxExt = s '{D2E45164-9547-4356-8513-937EAF39421D}' } } } NoRemove Directory { NoRemove shellex { NoRemove ContextMenuHandlers { ForceRemove MyCtxExt = s '{D2E45164-9547-4356-8513-937EAF39421D}' } } } NoRemove Folder { NoRemove shellex { NoRemove ContextMenuHandlers { ForceRemove MyCtxExt = s '{D2E45164-9547-4356-8513-937EAF39421D}' } } } }
タグ:Windows