B(ug)log

開発とかぼやきとかLINEスタンプとか http://line.me/S/sticker/1245783 職探し中

コンテキストメニューが開いたのを検知する方法

右クリックで出てくるアレの出現を検知したかったのです。 UIAssistant でコンテキストメニューの要素を扱えたら便利じゃないかと思って調べてみた結果がこれです。

対象

TL;DR

対象が非WPFアプリケーションの場合、以下のどちらかで検知できます。

  • Win32API SetWinEventHook関数(EVENT_SYSTEM_MENUPOPUPSTARTを利用)
  • UI Automation AddAutomationEventHandlerメソッド(UIA_MenuOpenedEventIdを利用)

対象がWPFアプリケーションの場合、以下でのみ検知できます。

  • UI Automation AddAutomationEventHandlerメソッド(UIA_MenuOpenedEventIdを利用)

なので、どの場合でも検知できるようにするには、UI Automation の AddAutomationEventHandler メソッドを使うとよいです。

動機

コンテキストメニューの UI を差し替えるなり、拡張できたら便利ではないだろうかと思った」などと供述している模様。

すなわち、

  1. コンテキストメニューが開かれるのを検知する
  2. 検知したら、コンテキストメニュー内の要素を取得する
  3. その要素の中から、入力した文字列によって検索、選択できるような UI を付加する

こんなことができれば便利かもしれないと考えました。UI を付加するのであれば、従来通りにも使えて、既存の使い勝手をなるべく阻害しない形になります。

SetWinEventHook

下記のサイト経由で、コンテキストメニューが開いたのを検知できると知り、試してみることに。

Windowsのイベントを拾う - hoge

SetWinEventHook の引数に EVENT_SYSTEM_MENUPOPUPSTART を指定すると、コンテキストメニューが開かれたときに引数で指定した関数がコールバックされます。その際、コンテキストメニューのウィンドウハンドルを受けとれます。

Event Constants (Windows)

というわけで 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)の場合

検知して、メニュー項目を取得できました。

f:id:u338steven:20160823113730p:plain

WPF の場合

こちらは、検知できませんでした。

f:id:u338steven:20160823113803p:plain

WPF とそれ以外では、コンテキストメニューの作られ方が異なり、そのため検知不可になっているのだと思われます。(下図参照)

Win32 アプリケーションのコンテキストメニュー

f:id:u338steven:20160823113814p:plain

WPF アプリケーションのコンテキストメニュー

f:id:u338steven:20160823113823p:plain

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 アプリケーション

f:id:u338steven:20160823113837p:plain

WPF アプリケーション

f:id:u338steven:20160823113846p:plain

さいごに(所感)

たぶん、この投稿の需要はほとんどないと思います。PC をがっつり使う人って、もうそんなにいないだろうし。 アプリケーションによっては、コンテキストメニュー内に項目が多くあるので、検索できれば使い勝手向上に役立ちそうとは思うのです。まぁ、実際に試してみないと便利かどうかは評価できないわけですが……。