「Windowsの右クリックメニューから複数ファイルをまとめて開く」の版間の差分
(Notion-MW) |
(Notion-MW) タグ: 手動差し戻し |
||
(同じ利用者による、間の6版が非表示) | |||
32行目: | 32行目: | ||
<syntaxhighlight lang="javascript">reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked" /v {8AB635F8-9A67-4698-AB99-784AD929F3B4} /f</syntaxhighlight> | <syntaxhighlight lang="javascript">reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked" /v {8AB635F8-9A67-4698-AB99-784AD929F3B4} /f</syntaxhighlight> | ||
* HKLMでも設定できる。また、「デバイス キャスト」を非表示にする場合はUUIDを<code>{7AD84985-87B4-4a16-BE58-8B72A5B390F7}</code>に変える。 | |||
Clipchampを表示したまま順番を治す方法は不明だが、自分でメニューを作ればできるかもしれない。Clipchampのメニューの実装に使われていると思われるIContextMenuについては後でも述べているのでそちらも参照。おそらくClipchampによるこのインターフェースの実装にバグがあるためにこのような挙動をしているのであろう。 | Clipchampを表示したまま順番を治す方法は不明だが、自分でメニューを作ればできるかもしれない。Clipchampのメニューの実装に使われていると思われるIContextMenuについては後でも述べているのでそちらも参照。おそらくClipchampによるこのインターフェースの実装にバグがあるためにこのような挙動をしているのであろう。 | ||
99行目: | 101行目: | ||
拡張子ごとの設定は<code>HKEY_CLASSES_ROOT\SystemFileAssociations</code>にある(編集時は<code>HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations</code>を使うのがいいだろう)(比較的新しいWindowsだと<code>HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations</code>キー自体が存在しないことがあるが、自分で作ればちゃんと動作する)。<code>command</code>キーは<code>HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations\.xxx\shell\yourcommandname\command</code>のような場所に作られることになる。 | 拡張子ごとの設定は<code>HKEY_CLASSES_ROOT\SystemFileAssociations</code>にある(編集時は<code>HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations</code>を使うのがいいだろう)(比較的新しいWindowsだと<code>HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations</code>キー自体が存在しないことがあるが、自分で作ればちゃんと動作する)。<code>command</code>キーは<code>HKEY_CURRENT_USER\Software\Classes\SystemFileAssociations\.xxx\shell\yourcommandname\command</code>のような場所に作られることになる。 | ||
また、全てのファイルを対象にするメニューは<code>HKEY_CLASSES_ROOT\SystemFileAssociations\*</code>、フォルダを右クリックしたときのメニューは<code>HKEY_CLASSES_ROOT\Directory</code>、ドライブ(C:\など)を右クリックしたときのメニューは<code>HKEY_CLASSES_ROOT\Drive</code> | また、全てのファイルを対象にするメニューは<code>HKEY_CLASSES_ROOT\SystemFileAssociations\*</code>、フォルダを右クリックしたときのメニューは<code>HKEY_CLASSES_ROOT\Directory</code>、ドライブ(C:\など)を右クリックしたときのメニューは<code>HKEY_CLASSES_ROOT\Drive</code>、フォルダ(・ドライブ)の背景(何もないところ)を右クリックしたときのメニューは<code>HKEY_CLASSES_ROOT\Directory\Background</code>以下でそれぞれ管理されている。また、<code>HKEY_CLASSES_ROOT\AllFilesystemObjects</code>というのもあり、これはディレクトリとファイルの両方に適用される(ドライブやフォルダ背景には影響なし)。 | ||
* 一覧は[https://learn.microsoft.com/ja-jp/windows/win32/shell/reg-shell-exts ここ]かな? | |||
<span id="複数種類に一括で追加"></span> | <span id="複数種類に一括で追加"></span> | ||
153行目: | 157行目: | ||
動作確認できたサンプルとしては、[https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/NonDefaultDropMenuVerb https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/NonDefaultDropMenuVerb]はIContextMenuを使って実装されており、dllをビルドしてCLSIDのInProcServer32でそれを指定するとちゃんと右クリックメニューが追加される。 | 動作確認できたサンプルとしては、[https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/NonDefaultDropMenuVerb https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/NonDefaultDropMenuVerb]はIContextMenuを使って実装されており、dllをビルドしてCLSIDのInProcServer32でそれを指定するとちゃんと右クリックメニューが追加される。 | ||
また、メニューを追加する関数であるQueryContextMenuが呼ばれる時点で、引数となっているファイルの情報がわかる(Initialize関数においてIDataObjectが渡されているのでそれをクラスのフィールドなどとして持っておけばよい)ので、特定の拡張子のファイルのみに対してメニューを表示することもできる。もちろんファイルの内容まで読み取ってからメニューを表示するといったこともできるが、そこまですると右クリックするだけで動作が重くなるといった副作用が大きくなるのでやめた方が無難だろう。ただ、スタートメニューへのピン留め<code>{a2a9545d-a0c2-42b4-9708-a0b2badd77c8}</code>やPDF XChange Editor(9.3や10.2.1で確認)のメニューは実際にそのような挙動をする(余談だが、InfoTipの表示もexplorerが操作しづらくなる原因なのでshowinfotipを0にしたほうがいい)(サムネイルなどはバックグラウンドで並列に走るので操作にはあまり影響がない)。またエラーが発生した場合はexplorer自体が強制終了することもあり、全体的に実装は慎重に行ったほうがよい。 | |||
==== コード例 ==== | ==== コード例 ==== | ||
159行目: | 163行目: | ||
拡張子が.tで始まるファイル(*.txt, *.ttfなど)のみにメニューを追加するサンプル(から抜粋)。 | 拡張子が.tで始まるファイル(*.txt, *.ttfなど)のみにメニューを追加するサンプル(から抜粋)。 | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="c++">IFACEMETHODIMP CNonDefaultDropMenuVerb::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT /*idCmdLast*/, UINT /*uFlags*/) | ||
{ | { | ||
FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; | FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; |
2024年4月15日 (月) 01:47時点における最新版
複数のファイルを別々にではなく一度にまとめて開きたいということがある。すなわち”app.exe file1” “app.exe file2”…をそれぞれ実行するのではなく、”app.exe file1 file2 …”を実行したい。例えば複数の音楽ファイルを連続再生したいときなどである。
ただ、エクスプローラ上で複数ファイルを選択して開く(右クリックで「開く」を選ぶorエンターキー)と、別々に実行されてしまう。では、右クリックメニューからこれを実行するにはどうすればいいか?というのがこの記事のテーマである。
ついでに、パス長さ制限を超えるファイルやフォルダにおける機能制限も解決する。
Windows10以前の右クリックメニューに戻す
まず、Windows11に移行すると右クリックメニューの項目数が大幅に削られており、とても使いにくいので、Windows10までの仕様に戻す必要がある。Windows 11 - 右クリックメニューを旧仕様に戻す方法 - PC設定のカルマなどに従って修正しよう。コマンド例は以下。これはユーザーごとの設定なので、管理者権限は不要である。
reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve
ファイルを開く操作ならダブルクリックでできるわけで、それでもあえてファイルを右クリックするユーザーの層を考えれば、使用頻度の高いメニューに絞る必要がないことは明らかである。右クリックメニューは動作が重いという問題点も指摘されてきたが、それなら項目数を削る以外の方法でパフォーマンスを改善するのが筋だろう。そもそも、Windows 11の右クリックメニューもそれほど快適な動作ではない。
なおバージョン22H2からはShift+右クリックで旧仕様のメニューが出るようになったようなので、それで十分という人は設定しなくてもよい。
ちなみにこれ以降も同じだが、HKEY_CURRENT_USER
以下はユーザー別設定なのでPC自体の再起動をしなくてもログアウト(サインアウト)→ログイン(サインイン)で全て反映されるはず。また今回の例に関しては、実際にはexplorerの再起動で十分である。
動画の右クリックメニューの順番がおかしい
動画ファイル(mp4やmkvなど)に関して、右クリックメニューの順番がおかしくなるという問題がある。具体的には、全ファイル共通のメニュー(「お気に入りに追加」など)とClipchampのメニューが普通の「開く」よりも上に表示されてしまう。また単一ファイル選択時だと(関連付けがすでに設定されているにもかかわらず)「プログラムから開く」が太字になってしまっている。
- 単一ファイル選択時
- 複数ファイル選択時
これはどうやらClipchampのメニューが悪いらしく、Add or Remove Edit with Clipchamp Context Menu in Windows 11 Tutorial | Windows 11 Forum(Clipchamp context menu removal | Windows 11 Forumに書いてあった)やWindowsの右クリックの「Clipchampで編集」を削除する方法 | ナポリタン寿司のPC日記に書いてある通り、Clipchampのメニューを非表示にすると治る。コマンド例は以下。
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked" /v {8AB635F8-9A67-4698-AB99-784AD929F3B4} /f
- HKLMでも設定できる。また、「デバイス キャスト」を非表示にする場合はUUIDを
{7AD84985-87B4-4a16-BE58-8B72A5B390F7}
に変える。
Clipchampを表示したまま順番を治す方法は不明だが、自分でメニューを作ればできるかもしれない。Clipchampのメニューの実装に使われていると思われるIContextMenuについては後でも述べているのでそちらも参照。おそらくClipchampによるこのインターフェースの実装にバグがあるためにこのような挙動をしているのであろう。
以下に修正後の正しい表示を載せておく。いずれの例でも関連づけはデフォルトのメディア プレイヤーが使われているものと仮定している(書いていて気付いたが、同一アプリにもかかわらず「プレーヤー」「プレイヤー」の表記ゆれがある)。
- 単一ファイル選択時
- 複数ファイル選択時
不完全な解決策
本題に入る。まずは、不完全ではあるがそこそこ簡単な解決策を紹介する。それは右クリックメニューにある「送る」を使う方法である。エクスプローラのアドレスバーにshell:sendto
と入力すると「送る」メニューの元になっているフォルダを開くことができる。ここにショートカットやファイルを追加しておけば、それが右クリックメニューに表示され、選択されたファイルをまとめてコマンド引数として渡してくれる。ショートカットを編集すればオプションも渡せる(あるいはバッチファイルを突っ込んでもよい)。
- 今後紹介する方法にも共通するが、ファイルの順番としては、メインとして選択されたもの(右クリックされたもの)から下方向に進んでいき、下端まで達したら一番上に戻ってそれから選択されたものの直前まで進む。例えばファイルが”0”から”9”までの10個あるフォルダで全選択をして”5”を右クリックして「送る」を使うと、5 6 7 8 9 0 1 2 3 4の順番で渡される。
しかしこの「送る」には弱点があり、[ref]で紹介したように、この「送る」メニューは260文字の長さ制限を超えるパスをもつファイルに対してはうまく機能しない。音楽ファイルなんかはアーティスト名も曲名も色々入って長大な名前になりがちで、実際に自分がこれで困ることになった。
もう一つ、「送る」メニューは拡張子に関連付けられないという問題がある。つまり、音声ファイルを一度に開くためだけのコマンドも、画像ファイルを一度に開くためだけのコマンドも、全てのファイルを対象とした「送る」メニューにまとめて入れざるを得ない。メニュー数が無駄に増えればそれだけ管理・操作もしづらくなる。ついでに言えば、「送る」を一旦クリックしてから中の項目を選ぶ手間もなかなかバカにできないところがある。
前提知識 - メニューを管理するレジストリ
右クリックメニュー関連の設定はレジストリを使って管理されている。この記事では上記「送る」の問題を解決する方法をこの後で2つ紹介するが、いずれもレジストリのcommand
という名前のキー(設定内容に応じて色々な所に設置されている)を編集する必要がある。それにあたって、前提知識としてレジストリの内容をある程度理解しておく必要があるだろう。
この話題に関しては比較的多くのサイトで解説があるが、Windowsのバージョンごとにかなり頻繁に仕様が変わっているようで、不十分なサイトも多い。最も優れているのはWindowsレジストリ解剖記で、かなり量は多いが、バージョンによる挙動の違いまで細かく示されている。
この記事に必要な操作を行うためにはcommand
の場所だけわかっていればいちおう十分だが、以下の内容くらいは覚えておいたほうがよい。
- HKEY_CURRENT_USERはHKCUと略され、ユーザーごとの設定を管理する。原則、変更はユーザー権限のみで可能。色々いじっても比較的安全。
- HKEY_LOCAL_MACHINEはHKLMと略され、システム全体(全ユーザー対象)の設定を管理する。変更には管理者権限が必要。設定は慎重に。
- HKEY_CLASSES_ROOTはHKCRと略され、関連付け専用のもので、HKCU\Software\ClassesとHKLM\Software\Classesの内容を合わせたものが表示される。関連づけの設定を確認するにはここを見るのがいいが、設定を変更する際にはここをいじるとHKLMのほうが変わってしまうことがあるのでHKCU\Software\Classesを変更したほうがよい。
以下では、設定方法を①ProgIDを使うもの②完全に独自のメニューを追加するもの と大きく二つに分けて記述する。
ProgIDを使うもの
特定の拡張子をもつファイルをダブルクリックやEnterで開くときに使われるプログラム、あるいは「既定のプログラム」あるいは「ファイルを開くプログラムの選択」のようなダイアログが表示されるときに表示されるプログラムは、このProgIDを用いて管理されている。この方法のメリットは、何より「ダブルクリックまたはEnterキー一発で起動できる」という操作性である。また、Windows11以前のメニューに戻さなくても使いやすい。
- 複数選択した際は「送る」と同様の順番で渡されるが、右クリックをしない場合は”メインとして選択されたファイル”がどれであるかが若干分かりづらい。基本的には最後に(左)クリックされたものになるはずで、よく見るとそれだけ少し太い(濃い)枠で表示されていることがわかる。なので音楽再生時など複数ファイルを上のものから順にすべて渡したいときは、「先に一番下にあるファイルをクリックしてから、一番上にあるファイルをShift+ダブルクリック(あるいはShift+クリックしたあとEnter)する」というのが有効である。
一方で、設定のしくみはやや煩雑である。具体的には、拡張子.xxxに対して
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xxx\UserChoice
というキーがあって、ここのProgId
の値をvalue
としたときのHKEY_CLASSES_ROOT\value\shell\open\command
の内容に従ってアプリケーションが起動される。
Windows10以降ではUserChoice
の中身を変更するためには普通の管理者権限やSYSTEMよりもさらに上位のTrustedInstaller権限が必要である。AdvancedRunなどを使ってTrustedInstaller権限で変更することは一応できたが、ハッシュ値の生成までちゃんとやっていないので自動で修正されてしまった(ここが解決できるかどうかは未調査)。従って、任意のコマンドを設定したい際は、まずUserChoice
の中身を変更するところだけエクスプローラーのダイアログを使い、その後でHKEY_CLASSES_ROOT\value
の中身を好きに変えるほうがよいだろう。
例えば新たなメニューを追加したい場合なら、以下のようにするとよい。
- 今まで使われていない(乱数値など)名前をもつダミーのexeファイル(実行ファイルとして正しく機能する必要はなく、空のファイルでもいいし、拡張子がexeでなくてもよい)を用意する(ここでは
dummy.exe
とする) - 関連付けたい拡張子のファイルの右クリックメニューから「プログラムを開く」を選択し、
dummy.exe
を選んで関連づける- これによりレジストリキー
HKEY_CURRENT_USER\Software\Classes\Applications\dummy.exe
が作られた上でUserChoice
のProgId
にはApplications\dummy.exe
が書き込まれる
- これによりレジストリキー
HKEY_CURRENT_USER\Software\Classes\Applications\dummy.exe\shell\open
の中身を自由に変更する。
完全に独自のメニューを実装する
これは、「PowerShellをここで開く」「VS Codeで開く」のように完全に独自の右クリックメニューを追加するものである。設定は意外と簡単である。また、フォルダに対する右クリックメニューや全ファイルに共通の右クリックメニューを追加するにはこの方法しかない。ただし、Windows10以前のメニューに戻さないと「その他のオプション」を押す手間がかかるので注意。
拡張子ごとの設定はHKEY_CLASSES_ROOT\SystemFileAssociations
にある(編集時はHKEY_CURRENT_USER\Software\Classes\SystemFileAssociations
を使うのがいいだろう)(比較的新しいWindowsだとHKEY_CURRENT_USER\Software\Classes\SystemFileAssociations
キー自体が存在しないことがあるが、自分で作ればちゃんと動作する)。command
キーはHKEY_CURRENT_USER\Software\Classes\SystemFileAssociations\.xxx\shell\yourcommandname\command
のような場所に作られることになる。
また、全てのファイルを対象にするメニューはHKEY_CLASSES_ROOT\SystemFileAssociations\*
、フォルダを右クリックしたときのメニューはHKEY_CLASSES_ROOT\Directory
、ドライブ(C:\など)を右クリックしたときのメニューはHKEY_CLASSES_ROOT\Drive
、フォルダ(・ドライブ)の背景(何もないところ)を右クリックしたときのメニューはHKEY_CLASSES_ROOT\Directory\Background
以下でそれぞれ管理されている。また、HKEY_CLASSES_ROOT\AllFilesystemObjects
というのもあり、これはディレクトリとファイルの両方に適用される(ドライブやフォルダ背景には影響なし)。
- 一覧はここかな?
複数種類に一括で追加?
HKEY_CLASSES_ROOT
にはPerceivedType
という値があり、これにvideo
などと指定されている場合はHKEY_CLASSES_ROOT\SystemFileAssociations\video
にある設定が反映される(これによって複数種類のファイルに共通するメニューが作れる)…というような情報もあったのだが、手元のWindows 11では全く反映されなかった。
そもそもPerceivedType
に複数種類が指定できるか不明だったりと色々と意義が怪しいので諦めて各拡張子ごとに追加するのがよいのではないかと思う。CLSIDの部分は共通化されているわけなのでそれほど非効率な二重管理ということにもならないだろう。(そもそもこの辺のWindowsの仕様はころころ変わるっぽいのであまり頑張ってもしょうがないかも?)
Directory\Backgroundの表示
エクスプローラーの左側のエリアにあるフォルダやドライブを右クリックしたときのメニューには、なぜかDirectory\Backgroundの内容も表示されてしまう。そのうえ、これらには正しくパラメータが渡されない(commandに直接書くときは%V
などが入っていると起動せず、COM使用(少なくともDelegateExecute)で受け取る方法も不明)。
ただ、DirectoryやDriveに同名のメニューが登録されている場合はそちらが優先される。しかし、これを利用してメニューを非表示にすることはできない。(DirectoryやDriveのほうにLegacyDisableを書いたら、そもそもそちらが無かったのと同じになるっぽい)
実際には、Directory\Backgroundに設定するメニューはDirectoryにもDriveにも等しくあったほうがいい場合が多いと思うので、そのように設定するとよいだろう。
その他設定項目
本筋とは関係ないので詳細は省くが、その他にもこれらのキーでは様々な設定ができる。
Icon
によるアイコンの設定&
によるキーの設定(「"編集(E)"のEみたいなやつ」)MUIVerb
(多分、言語設定ごとの表示文字列の切り替えとかができるようになるやつ)AppliesTo
、DefaultAppliesTo
による表示条件の設定- AQS書式というのが使用可能で、かなり柔軟な指定ができる(特定フォルダ内のみに表示するなど)。ただしあまり情報がなく、Windows レジストリ 解剖記でもそこまで詳細な解説はない。そこからもリンクされているMSの公式ドキュメントを読むのが早そう。あと、Backgroundメニューではそもそも背景フォルダに関する情報が全く渡されてこないようで(ただしどこかに書いてあったのではなく実験の結果)、使い物にならない。
HasLUAShield
による「管理者アイコン」の表示(Icon
との併用可)- これも実は上と同様にAQS書式で管理者アイコンの表示条件を設定できる
- このアイコン自体はSystem32のimageres.dllとかUserAccountControlSettings.exeとかに入っている
SubCommands
によるサブメニューの登録- 手元のWindows11で試した限り、
Directory
やDrive
ではうまくいくがDirectory\Background
だとサブメニューが出ない?
- 手元のWindows11で試した限り、
ExtendedSubCommandsKey
値による他のshell
キーへの参照によるサブメニューの登録- かなり強力で、循環参照も作れる。
- 例: Nonsubject: 8月 2011
- 一つのルートメニューが持てるサブメニュー上限は16個までという制限がある。https://stackoverflow.com/questions/48625223/is-there-a-maximum-right-click-context-menu-items-limit
- これは
Directory\Background
でも有効。
Extended
によってShift+右クリックしたときのみ表示させる- 設定例: Add or Remove 'Open in Windows Terminal as administrator' context menu | Tutorials
ContextMenuHandlers(IContextMenu)を用いた実装
先ほどのClipchampのメニュー項目などはここまで述べたようにレジストリ内に静的に設定されているものではなくCOM(Component Object Model)を用いて外部のDLLの関数から動的に追加されているものである(ただDLLの指定自体はレジストリ内のInProcServer32のようなキーにあるはず)。Clipchamp以外だと7-ZipやPDF XChange Editorなどのメニューはこれを用いて実装されている。レジストリのXXX\shellex\ContextMenuHandlers
といったキー以下に設定されているものはこれに分類される。
詳しくはIContextMenuなどのキーワードで検索すると出てくるCustomizing a Shortcut Menu Using Dynamic Verbs - Win32 apps | Microsoft Learnなどを読むとよさそうだが、実装はかなり難解である。Clipchampもこの部分で不適切な実装をしたために順番がおかしくなったのではないかと思われる。
動作確認できたサンプルとしては、https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/NonDefaultDropMenuVerbはIContextMenuを使って実装されており、dllをビルドしてCLSIDのInProcServer32でそれを指定するとちゃんと右クリックメニューが追加される。
また、メニューを追加する関数であるQueryContextMenuが呼ばれる時点で、引数となっているファイルの情報がわかる(Initialize関数においてIDataObjectが渡されているのでそれをクラスのフィールドなどとして持っておけばよい)ので、特定の拡張子のファイルのみに対してメニューを表示することもできる。もちろんファイルの内容まで読み取ってからメニューを表示するといったこともできるが、そこまですると右クリックするだけで動作が重くなるといった副作用が大きくなるのでやめた方が無難だろう。ただ、スタートメニューへのピン留め{a2a9545d-a0c2-42b4-9708-a0b2badd77c8}
やPDF XChange Editor(9.3や10.2.1で確認)のメニューは実際にそのような挙動をする(余談だが、InfoTipの表示もexplorerが操作しづらくなる原因なのでshowinfotipを0にしたほうがいい)(サムネイルなどはバックグラウンドで並列に走るので操作にはあまり影響がない)。またエラーが発生した場合はexplorer自体が強制終了することもあり、全体的に実装は慎重に行ったほうがよい。
コード例
拡張子が.tで始まるファイル(*.txt, *.ttfなど)のみにメニューを追加するサンプル(から抜粋)。
IFACEMETHODIMP CNonDefaultDropMenuVerb::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT /*idCmdLast*/, UINT /*uFlags*/)
{
FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM medium;
HRESULT hr = _pdtobj->GetData(&fmte, &medium);
if (SUCCEEDED(hr))
{
HDROP hdrop = (HDROP)medium.hGlobal;
WCHAR szFile[MAX_PATH];
DragQueryFile(hdrop, 0, szFile, ARRAYSIZE(szFile));
const WCHAR* ext = wcsrchr(szFile, L'.');
ReleaseStgMedium(&medium);
if (ext && *(ext+1) != L't') {
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); // Add nothing
}
}
else
{
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); // Add nothing
}
WCHAR szMenuItem[80];
LoadString(g_hinst, IDS_CHECKDROP, szMenuItem, ARRAYSIZE(szMenuItem));
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION, idCmdFirst + IDM_CHECKDROP, szMenuItem);
LoadString(g_hinst, IDS_CHECKNETRESOURCES, szMenuItem, ARRAYSIZE(szMenuItem));
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION, idCmdFirst + IDM_CHECKNETRESOURCES, szMenuItem);
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(2)); // indicate that we added 2 verbs.
}
- 特定の拡張子に対して追加できるのは以下で知った
- できるという情報
- できるという情報
registry - How to add context menu to one specific File - Stack Overflow
- 実際のコード
- 実際のコード
freeimagerip/Thumb.cc at master · nyfair/freeimagerip
IContextMenuの今後について
Windowsの右クリックメニューが重いと良く言われるのはこのような高い自由度をもつIContextMenuに起因するものであると思われる。Windows11ではIContextMenuよりIExplorerCommandがよいというのを見かけた(https://twitter.com/kenichiuda/status/1417266893581295651や、そこで引用されているhttps://twitter.com/madonomori/status/1417265763363168263)。https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/ExplorerCommandVerbも見てみる必要があるかも。
ただまあMicrosoftのことなのでIContextMenuもあと10年くらいは使えるだろう。
- Windows 11 でアプリをすぐれたものにするためにできることトップ 11 - Windows apps | Microsoft Learn を読んだら、Windows 11の(新しい)右クリックメニューに表示するためにはIExplorerCommandを使わなければならない(IContextMenuは不可)と明確に書いてあった。また、IExplorerCommandのサンプル(エクスプローラー コマンド動詞のサンプル - Win32 apps | Microsoft Learn)を使ってみたが、IExplorerCommandState(CommandStateHandler)のほうは動作しているように見えるものの肝心のIExplorerCommand(ExplorerCommandHandler)は全く反応しなかった(右クリックを一度でも行うとdllへの上書きアクセスが禁止されるので読み込まれてはいるようだが、コンストラクタや
QueryInterface
にデバッグ用のメッセージボックスを入れても表示されない)。
サブメニュー
IContextMenuは自由度が高いのでサブメニューなども実装できるはず(7-Zipなどもそうしていると思われる)。またこのような記事もあった: デスクトップのシェルメニューをサブメニューにする | Nonsubject
Windows11の右クリックメニューへの追加
これはどうやらますます面倒になりそうで、リリース済のWindowsストアアプリをインストールするか開発者モードを有効にしないと自分の好きなメニューを追加できない(多分)上、アプリ1つにつき1メニューしか追加できないらしい。
Windows 11の右クリックメニューに追加してくれるソフト
フォルダ背景を右クリックしたメニューに追加できない(https://github.com/ikas-mc/ContextMenuForWindows11/issues/44)(いや、使ってみたら普通にできてるっぽい?)、アプリ1つにつき1メニューしか追加できない(https://github.com/ikas-mc/ContextMenuForWindows11/issues/45)(これはWindows側がそういう仕様っぽいので厳しそう)
Microsoftの関連するサンプル
AppModelSamples/Samples/SparsePackages at master · microsoft/AppModelSamplesMeryへの追加要望
解決策①COMを利用する
まずは、COMを用いて実装するという方法がある。基本的にはこちらが綺麗な解決策である。
IExecuteCommandというインターフェースを具体的に実装しているMicrosoftのサンプルがあったので、それを参考に、「任意コマンドを引数にとり、取得したファイル・フォルダの情報をそのコマンドの標準入力にUTF-8で送信する」というプログラムを作成した。
COMを使っていることからわかるように、コマンドラインを介さず直接Explorerからデータが送られてくるので、他の方法に共通してみられるパス長さやファイル個数の制限が(事実上)皆無であり、非常に強力である。
使い方
詳しい使い方は上記レポジトリのREADMEも参照。
基本
引数となる(実際に起動したい)コマンドはレジストリキー
HKEY_CURRENT_USER\Software\Classes\CLSID\{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\LocalServer32
の既定の値で設定する。
その上で該当メニューに対応するcommand
キー(レジストリの各所にある)にDelegateExecute
という値を作成して{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
を書き込むことで起動できる(command
の既定値よりもDelegateExecute
が優先される)。
IDropTargetを用いた実装
IExecuteCommandではなくIDropTargetというインターフェースを用いてもほぼ同じことができそうであった。対応するサンプルはhttps://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/DropTargetVerbである。IExecuteCommandとの機能的な違いはあまりよくわからないが、https://learn.microsoft.com/en-us/windows/win32/shell/samples-droptargetverbを読む限り、DropTargetのほうが古くからあるようである。CLSIDの登録までは同じだが、command
のかわりにDropTarget
キーを使用し、DelegateExecute
値のかわりにCLSID
値を使う(ソースを読めばわかる)。
解決策②プロセス間通信で頑張る
「プロセス間通信を使って頑張る」という方法もある。要するに、コマンドとしては別々に渡されたとしても、既に立ち上がっているプロセスがあるときはそちらに投げて終了するようにすれば、一つのプロセスでまとめて開けるというわけである。
この部分を代わりにやってくれるものとしてはhttps://github.com/zenden2k/context-menu-launcherがある。これを少し改善したフォークとしてhttps://github.com/KnIfER/SingleInstance-Launcherがあり、さらに自分が64bit版のビルドを加えたものがhttps://github.com/ge9/SingleInstance-Launcherにある。
ただ、これはCOMを使う方法とは違っていくつかの欠点がある。
- ファイル個数の上限
- ProgIDを使って開く場合には、選択個数がデフォルトで15個までに設定されており、これはMultipleInvokePromptMinimum値(詳しくは他サイト)の変更により増やせるが確実ではない(上限に諸説あり)。
- 独自メニューの場合は、"MultiSelectModel"を"Player"にすることでファイル数上限が100個になるが、それより大きくはできない。
- 長さ制限(260文字)を超えるパスをもつフォルダでは失敗する(詳しくはWindowsのパス長さ制限に関して を参照)。
- プロセス間通信を使っているため、起動時にラグがあったりすると、ごく稀に渡されたファイルの順番が乱れることがある。
SingleInstance-Launcher - 基本的な使い方
上記GitHubリポジトリから実行ファイルをダウンロードしてどこか分かりやすい場所に置く。インストールなどは不要である。筆者がリリースした実行ファイルを信用できない・改変したいなどの場合はソースコードをどうぞ。言語はC++。ファイルの順番が乱れにくくなる改善など大歓迎。
README.mdにもあるが起動に用いる基本的な構文(レジストリに登録する値)は以下のようである(※ここでは64bit版を使うものとして説明しているが、32bit版でも全く同様)。
"C:\path\to\singleinstance_x64.exe" "%1" "C:\Program Files\path\to\app.exe" "$files" --si-timeout 1000 --option-to-app-1 --option-to-app-2
"C:\path\to\singleinstance_x64.exe"、"%1"、"C:\Program Files\path\to\app.exe"、"$files"の順番はおそらくこの通りである必要がある。その後にあるものはapp.exeにオプションとしてそのまま渡されるが、--si-timeout n
に限り、singleinstance_x64.exeが解釈し、app.exeに渡されない。このオプションはSingleInstance-Launcherに対して次のファイルが渡されてくるのを待つタイムアウト期間をミリ秒で指定するものである。短すぎると、右クリックメニューの処理が遅い場合に途中で打ち切られてしまう可能性がある。長すぎると、その期間待つまでapp.exeは呼ばれないため、タイムラグが長く不便に感じる可能性がある。特に動作が重いPCでなければ1000(1秒)くらいにしておけば十分だろう。もう少し短くてもいいかもしれない。
%1や$filesの両側にダブルクォーテーションがないと、空白を含むファイル名を正しく扱えない。GitHubのサンプルでは付いていないものもあるので注意。