Windows Explorerのシェル拡張についての質問です。

やりたい事は、選択→右クリック→項目指定 で呼び出される動作をコマンドラインからエミュレートしたい(呼び出されるアプリに渡す引数を指定してバッチ処理できるようにしたい)ということです。
レジストリを探索して問題のアプリを呼び出してるっぽいGetClassObjectをExportしているDLLファイルは特定できたのですが、このDLLをどう扱えば所望の動作になるのかわかりません。
動作環境はOffice/WSH/Powershellのみ使える環境とします。(VC等は使えない環境)
・Explorer上で選択されたファイルを右クリックして項目を選択したときに内部的にどういう動作が行われるのか詳しく解説しているサイトはないでしょうか。(shell拡張を作成するページには「こうすればこうなる」というケースしか書かれていないので、ファイル名を引数に取って~を呼び出す場合は~」という情報が見つけられません)

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2011/04/05 22:43:45
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答2件)

id:ekrea No.1

回答回数3ベストアンサー獲得回数2

ポイント61pt

エクスプローラで実行されている処理を(ざっくりですが)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++コード

SHFileOperationによるファイル操作を元に戻す

こっちはもうちょっと簡単な方法でやってますね。


その他参考になりそうなところ

Emulating CoCreateInstance() - CodeProject CoCreateInstanceの内部でやっていること。

進め!中級プログラマー シェル拡張に使われるクラスの解説

シェルエクステンションって? アイテムIDリストはここがわかり易いかも

id:espresso3389 No.2

回答回数35ベストアンサー獲得回数6

ポイント35pt

本来であれば、COM経由で呼び出しといいたいところですが、シェル拡張で利用されているインターフェイスは、いわゆるデュアルインターフェイスではないことの方が多いので、C++やC#などを使わずに処理をすることは不可能だと思います。

なので、その方向性での実装ではない方向で考えると、PowerShellでは、.NETのライブラリは比較的簡単に使えるので、

System.Windows.Forms.SendKeys

を使って、キー処理をエミュレートするというのはどうでしょうか?タイミングの問題に翻弄されやすいことや、デスクトップがインタラクティブでないと動作しないなど問題が無いわけでは無いですが、ある程度までなら実現可能だと思います。

  • id:Cherenkov
    http://d.hatena.ne.jp/language_and_engineering/20100804/p1
    適当なメソッドにdll名と引数を渡す感じではないでしょうか?
  • id:clock9
    しくおゲー日記 2011/04/04 23:42:59
    うーん。ExcelのCALL関数の記事はためになったんですが、objdumpで見ると、InProcServer32キーで指定されてるDLLがDllCanUnloadNow,GetClassObject,DllRegisterServer,DllUnRegisterServerの4つしかexportしてないんですよね
    このキーで指定されてるDLLがExplorerからどういう風に(デフォルトでは)呼ばれるのか、とか、そういう事が知りたいんです。
    もしかして見る場所間違ってるのかな…
  • id:espresso3389
    あ、WSHでも普通にSendKeys使えますね。WSHの方が簡単かも。
  • id:clock9
    しくおゲー日記 2011/04/05 22:50:54
    この方法でやろうと思っていたことが別の方法で実現できてしまったので終了とさせていただきます。
    (シェル拡張を呼び出したときにタスクマネージャで見ると別のexeファイルをヘルプに載せていない引数付きで呼び出していた…ので、その間の動作はさておいて直接隠し引数指定で実現した^^;)
    こう開発環境のない素の、というかお仕事用のVista環境でどこまで掘り下げられるかな、というのを知りたかったんですが、解析/再利用するにしても開発環境がないと難しいようですね。
    COMのインプロセスサーバとして実装されていればWSHからとりあえず使えるんだろうか、とか甘い考えしてました。
    ファイルを複数/単数選択してメニューから項目を選ぶ、のであれば、「何が選ばれたか」と「どのファイル群が引数として与えられたか」だけだろうから、(シェル拡張全体の仕様として)手続きが決まっているもんだと思ってました。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません