コンテキストメニューが開いたのを検知する方法
右クリックで出てくるアレの出現を検知したかったのです。 UIAssistant でコンテキストメニューの要素を扱えたら便利じゃないかと思って調べてみた結果がこれです。
対象
- Windows XP 以降
TL;DR
対象が非WPFアプリケーションの場合、以下のどちらかで検知できます。
- Win32API
SetWinEventHook
関数(EVENT_SYSTEM_MENUPOPUPSTART
を利用) - UI Automation
AddAutomationEventHandler
メソッド(UIA_MenuOpenedEventId
を利用)
対象がWPFアプリケーションの場合、以下でのみ検知できます。
- UI Automation
AddAutomationEventHandler
メソッド(UIA_MenuOpenedEventId
を利用)
なので、どの場合でも検知できるようにするには、UI Automation の AddAutomationEventHandler
メソッドを使うとよいです。
動機
「コンテキストメニューの UI を差し替えるなり、拡張できたら便利ではないだろうかと思った」などと供述している模様。
すなわち、
- コンテキストメニューが開かれるのを検知する
- 検知したら、コンテキストメニュー内の要素を取得する
- その要素の中から、入力した文字列によって検索、選択できるような UI を付加する
こんなことができれば便利かもしれないと考えました。UI を付加するのであれば、従来通りにも使えて、既存の使い勝手をなるべく阻害しない形になります。
SetWinEventHook
下記のサイト経由で、コンテキストメニューが開いたのを検知できると知り、試してみることに。
SetWinEventHook
の引数に EVENT_SYSTEM_MENUPOPUPSTART
を指定すると、コンテキストメニューが開かれたときに引数で指定した関数がコールバックされます。その際、コンテキストメニューのウィンドウハンドルを受けとれます。
というわけで ContextMenuObserverWin32
を作成してみました。(ソースコード@Gist)
以下のようなコマンドで、ビルドできます。(csc.exe にパスが通っているかの確認と、.NETFramework のバージョンについては環境ごとに変更する必要があります)
csc /t:winexe /lib:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1" /r:UIAutomationClient.dll /r:UIAutomationTypes.dll ContextMenuObserverWin32.cs
非WPF(Win32, Windows Forms)の場合
検知して、メニュー項目を取得できました。
WPF の場合
こちらは、検知できませんでした。
WPF とそれ以外では、コンテキストメニューの作られ方が異なり、そのため検知不可になっているのだと思われます。(下図参照)
Win32 アプリケーションのコンテキストメニュー
WPF アプリケーションのコンテキストメニュー
WPF も検知できる方法を探す → UI Automation を試す
以下のように、WPF だろうとなんだろうと利用できると公式に記載されていました。よさげです。
The UI Automation requirements apply to all menu controls, whether Microsoft Windows Presentation Foundation (WPF), Microsoft Win32, or Windows Forms.
...
Required UI Automation Events
Menu controls must raise the UIA_MenuOpenedEventId event when they appear on the screen.
UI Automation Support for the Menu Control Type
試してみた結果
ContextMenuObserverUIA
を作成してみました。(ソースコード@Gist)
SetWinEventHook
のときと同様に、以下のようなコマンドで、ビルドできます。
csc /t:winexe /lib:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1" /r:UIAutomationClient.dll /r:UIAutomationTypes.dll ContextMenuObserverUIA.cs
WPF でも問題なく検知できて、以下のようにコンテキストメニューの内容を取得できました。
Win32 アプリケーション
WPF アプリケーション
さいごに(所感)
たぶん、この投稿の需要はほとんどないと思います。PC をがっつり使う人って、もうそんなにいないだろうし。 アプリケーションによっては、コンテキストメニュー内に項目が多くあるので、検索できれば使い勝手向上に役立ちそうとは思うのです。まぁ、実際に試してみないと便利かどうかは評価できないわけですが……。