やりたい事は、選択→右クリック→項目指定 で呼び出される動作をコマンドラインからエミュレートしたい(呼び出されるアプリに渡す引数を指定してバッチ処理できるようにしたい)ということです。
レジストリを探索して問題のアプリを呼び出してるっぽいGetClassObjectをExportしているDLLファイルは特定できたのですが、このDLLをどう扱えば所望の動作になるのかわかりません。
動作環境はOffice/WSH/Powershellのみ使える環境とします。(VC等は使えない環境)
・Explorer上で選択されたファイルを右クリックして項目を選択したときに内部的にどういう動作が行われるのか詳しく解説しているサイトはないでしょうか。(shell拡張を作成するページには「こうすればこうなる」というケースしか書かれていないので、ファイル名を引数に取って~を呼び出す場合は~」という情報が見つけられません)
エクスプローラで実行されている処理を(ざっくりですが)C++コードで示します。
これがOffice/wsh/PowerShellで実現可能かどうかは知りません。探した限りではそのような情報は見つけられませんでした。
DLL関数のロードと構造体の参照渡しをサポートした言語なら理論的には可能なはずですが、
道のりはかなり険しいかと思います。
(本来アプリケーションがGetClassObjectを直接呼び出すことは推奨されておらず、
通常はSDKに用意されたCoCreateInstance関数を使用します。)
IClassFactory *pIFactory; IShellExtInit* pIShellInit; IContextMenu* pContextMenu; REFCLSID clsid = CLASS_ID;//拡張オブジェクトのクラスID HKEY hKeyPlogID = PROG_ID;//ターゲットとなるファイルのファイルクラスが記述されたレジストリキー。 ITEMIDLIST* pItemIdList = FILE;//ターゲットとなるファイルのパスを表すアイテムIDリスト。 //COMサーバーの起動 HRESULT hr = GetClassObject(clsid, IID_IClassFactory, (LPVOID *)&pIFactory); //シェルオブジェクトの生成及びインターフェースの取得。 pIFactory->CreateInstance(NULL, IID_IShellExtInit, (LPVOID *)&pIShellInit); pIShellInit->QueryInterface(NULL, IID_IContextMenu, (LPVOID *)&pContextMenu); //メニュー項目の取得 HMENU hMenu = CreateMenu(); pIShellInit->Initialize(pItemIdList,NULL, hKeyPlogID); pContextMenu->QueryContextMenu( hMenu, 0, 1, 0xffff, CMF_NORMAL ); pIFactory->Release(); pIShellInit->release(); pContextMenu->release();
上記コードによってhMenuにコンテキストメニューの項目が追加されます。
コマンドを実行するには以下のようにします。
//iCommand: 実行するメニュー項目のオフセット void exec(int iCommand,IContextMenu* pContextMenu){ CMINVOKECOMMANDINFO cmi; cmi.cbSize = sizeof(CMINVOKECOMMANDINFO); cmi.fMask = 0; cmi.lpVerb = (LPCSTR)MAKEINTRESOURCE(iCommand - 1); cmi.hwnd = NULL; cmi.lpParameters = NULL; cmi.lpDirectory = NULL; cmi.nShow = SW_SHOWNORMAL; cmi.dwHotKey = 0; cmi.hIcon = NULL; pContextMenu->InvokeCommand(&cmi); }
流れとしては
1. GetClassObjectでCOMサーバーのインスタンスを生成し、それに関連付けられたIClassFactoryを取得。
2. IClassFactory.createInstanceでシェルオブジェクトを生成し、それに関連付けられたIShellExtInitを取得。
3. IShellExtInit.QueryInterfaceでシェルオブジェクトに関連付けられたIContextMenuを取得。
4. IShellExtInit.Initializeでシェルオブジェクトにターゲットファイルをセットする。
5. IContextMenu.QueryContextMenuでメニュー項目の取得。
6. IContextMenu.InvokeCommandで実行。
となります。
*アイテムIDリストはエクスプローラにおけるパスの内部表現です。エクスプローラはデスクトップやコントロールパネルなどの
ファイルでもフォルダでもないアイテムや、本来のディレクトリ構造とは異なる階層を扱うのでこんなややこしいことをしています。
参考:
エクスプローラの「元に戻す」を実行するC++コード
こっちはもうちょっと簡単な方法でやってますね。
その他参考になりそうなところ
Emulating CoCreateInstance() - CodeProject CoCreateInstanceの内部でやっていること。
進め!中級プログラマー シェル拡張に使われるクラスの解説
シェルエクステンションって? アイテムIDリストはここがわかり易いかも
本来であれば、COM経由で呼び出しといいたいところですが、シェル拡張で利用されているインターフェイスは、いわゆるデュアルインターフェイスではないことの方が多いので、C++やC#などを使わずに処理をすることは不可能だと思います。
なので、その方向性での実装ではない方向で考えると、PowerShellでは、.NETのライブラリは比較的簡単に使えるので、
を使って、キー処理をエミュレートするというのはどうでしょうか?タイミングの問題に翻弄されやすいことや、デスクトップがインタラクティブでないと動作しないなど問題が無いわけでは無いですが、ある程度までなら実現可能だと思います。
コメント(4件)
適当なメソッドにdll名と引数を渡す感じではないでしょうか?
このキーで指定されてるDLLがExplorerからどういう風に(デフォルトでは)呼ばれるのか、とか、そういう事が知りたいんです。
もしかして見る場所間違ってるのかな…
(シェル拡張を呼び出したときにタスクマネージャで見ると別のexeファイルをヘルプに載せていない引数付きで呼び出していた…ので、その間の動作はさておいて直接隠し引数指定で実現した^^;)
こう開発環境のない素の、というかお仕事用のVista環境でどこまで掘り下げられるかな、というのを知りたかったんですが、解析/再利用するにしても開発環境がないと難しいようですね。
COMのインプロセスサーバとして実装されていればWSHからとりあえず使えるんだろうか、とか甘い考えしてました。
ファイルを複数/単数選択してメニューから項目を選ぶ、のであれば、「何が選ばれたか」と「どのファイル群が引数として与えられたか」だけだろうから、(シェル拡張全体の仕様として)手続きが決まっているもんだと思ってました。