Alt + Tab > Mery クリックでメニューバーがアクティブになる件

  1. >> Kuro さん

    Alt + Tab でメニューバーがアクティブになる件、手持ちの情報を共有しようと思ったらかなーり長くなってしまったのでフォーラムに投稿することにしました^^;

    Delphi でどうすればいいかといったことは一切分からない上、果たして参考になるかも分かりませんが、AutoHotKey で得た知見を共有しておきたいと思います。

    ■Windows での Alt のキーイベントの受け取り方について

    1. それぞれのキーイベントを、Windows 内では押し下げ (Down) とその戻り (Up) で取得している。(なので、AutoHotKey では Up だけに動作を割り当てる等も可能) これは修飾キーである Alt も同様
    2. 通常のキーは Down 時に動作するが、Alt や Win キーは修飾キーであり Down 時に何らかの動作をしてしまっては都合が悪いため、Down 時には動作しない
    3. Alt や Win キーは、組み合わせキーが押されなかった場合のみ、Up 時の挙動をする (Alt ならメニューバーへのフォーカス、Win ならスタートメニュー)。組み合わせキーが押された場合には、Up 時の挙動をしない

    ■Mery での挙動の考察

    上記を踏まえ Mery の挙動を見てみると、Alt Down と Alt Up 両方を受け取ったときメニューバーにフォーカスが移っているように見えました。(意図的に Alt Down だけ、Alt Up だけを送り込むようにしてみると、メニューバーにフォーカスは移らない)

    以下は既知の再現手順ですが 2 で Mery が Alt Down を受け取った状態になり、その後 3 で Alt Up を受け取ってしまうのでメニューバーにフォーカスが移るのだと推測しています。

    1. Mery をアクティブにする (メニューバーにはフォーカスが当たっていない状態)
    2. Alt + Tab で他のウィンドウをアクティブにする
    3. Alt + Tab でアプリケーション選択画面を表示し、クリックで Mery を選択する

    ■AutoHotKey での Alt Up, Win Up 時動作の回避の仕組み

    AutoHotKey では修飾キーを使ったキー入力をフックする特性から Alt や Win の Up 時挙動を意図的に抑制しなければなりません。

    例) Alt + A キーに動作を割り当て
    Alt & A::Send {Z}

    上記は Alt + A キーを押した際に単一 Z キー押下の動作をさせる記述ですが、単一 Z の入力を OS 側に送り込むには、まず Alt キーの Down 状態を解放する必要があります。

    この解放時に単純に Alt Up をしてしまうと、OS 上では「組み合わせキーの無い、Alt 単一の Down→Up」と認識され、メニューバーへフォーカスが当たってしまいます。

    これを回避するために、AutoHotKey では上記3の「組み合わせキーが押された場合には、Up 時の挙動をしない」という仕組みを利用しています。

    具体的には本来の動作に Ctrl キーを差し込むことで回避しています。以下に、上記例の「Alt & A::Send {Z}」が実行された際に OS 側に送られるキーシーケンスを示します。

    * Alt Down
    * Ctrl Down
    * Ctrl Up
    * Alt Up # Alt キー押下状態の解除
    * Z Down
    * Z Up
    * Alt Down # Alt キー押下状態の復帰

    Z 直前に Alt が解放されているので、結果として画面上には単一の Z キー入力がされます。

    また2、3番目に突如 Ctrl が現れましたが、これが肝です。この Ctrl は AutoHotKey が自動で送っています。

    Alt が Down 状態のときに Ctrl を Down することで OS 側では Alt + Ctrl が押されたものと認識されます。その結果、Alt Up 時に Alt 単体 Up 時の挙動であるメニューバーへのフォーカス動作が取り消されます。

    また、この Ctrl を自動で送り込む動作ですが、Ctrl だと都合が悪いケースがあります。その場合、「vk07」という、物理的には存在しないけれど OS 上では認識できるキーを Ctrl の代わりに送る、というのが AutoHotKey ではベストプラクティスとされています。

    ちなみにその設定は以下のような #MenuMaskKey という記述で行います。
    http://ahkwiki.net/-MenuMaskKey

    これらの動作を流用して、例えば Mery がアクティブになった際に Alt が押下状態だった場合、vk07 のような無害なキーコードを OS が認識できる形で送り込むことができれば回避できるかもです。

    もしくは、Alt + Tab を押下した際はアクティブウィンドウが一時的にアプリケーション選択ウィンドウに移るので、非アクティブになった際、あるいは再度 Mery がアクティブになった際に、既に Alt Down を Mery が受け取ってしまっている状態を何らかの方法でリセットできれば、Mery では Alt + Tab > クリック後の Alt Up だけを受け取ることになるのでメニューバーはアクティブにならない気がします。

     |  yuko  |  返信
  2. >> yuko さん
    情報ありがとうございます。

    私のほうは Delphi の内部のソースを追って、ツールバーが Alt キーをフックしている部分までは発見できまして、それを強制的に無効化することには成功したのですが…

    > 2 で Mery が Alt Down を受け取った状態になり、その後 3 で Alt Up を受け取ってしまうのでメニューバーにフォーカスが移るのだと推測しています。
    > 非アクティブになった際、あるいは再度 Mery がアクティブになった際に、既に Alt Down を Mery が受け取ってしまっている状態を何らかの方法でリセット

    おっしゃるとおり、2 の段階ですでにツールバーが Alt Down をキャッチして Delphi の内部でフラグが立っちゃってましたが、Delphi の内部なのでサポートに投げて対策してもらうしかないかなーと思っておりました ^^;

    > また2、3番目に突如 Ctrl が現れましたが、これが肝です。この Ctrl は AutoHotKey が自動で送っています。

    確かに、Alt を押して、そのまま Ctrl を押して離して、そのあと Alt を離すと、Alt の動作がキャンセルされてますね。こんなテクニックがあったなんて。

    > これらの動作を流用して、例えば Mery がアクティブになった際に Alt が押下状態だった場合、vk07 のような無害なキーコードを OS が認識できる形で送り込むことができれば回避できるかもです。

    いただいた情報をもとに AutoHotKey のソースを読みまして、OS に vk07 を送り込む仕組みを作ってみたところ、うまくいきました!

    副作用とかはちょっと心配ですが、軽くテストした感じだとうまく動いていますので、もうちょっと試して問題なさそうなら次のバージョンで実装してみようかなと思います。

     |  Kuro  |  返信
  3. > いただいた情報をもとに AutoHotKey のソースを読みまして、OS に vk07 を送り込む仕組みを作ってみたところ、うまくいきました!

    おお、やったー!

    …が、少し残念なお知らせが。

    https://twitter.com/bsakatu/status/800275389813657601?s=09
    上記のツイートの情報があり、確かめてみたところ、確かに vk07 で Windows 10 のゲームバーが起動してしまうようです。
    ちなみにこのゲームバーは、Windows 10 の設定画面から「ゲーム > ゲームバー」の設定を ON にしておくと有効化される機能です。有効時、通常なら Win + G キーがその起動ショートカットになるのですが、こっそり vk07 も起動ショートカットとして機能しているようです。

    ただし、これは修飾キー無しの vk07 キーで動作するようで、今回の対策のように「Alt + vk07」を送り込む分には発動しないようです。
    ただ、万全を期すために vk07 以外のキーを使った方がいい気がします。

    個人的には、できるなら vk00 か vkFF がいいかな?と思っています。
    そう考える理由となった情報も以下に載せておきますね。

    Virtual-Key Codes - Windows applications | Microsoft Docs
    https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes
    ↑の日本語訳
    http://kts.sakaiweb.com/virtualkeycodes.html

    AutoHotKey 公式 (英語) の MenuMaskKey のリファレンス
    https://www.autohotkey.com/docs/commands/_MenuMaskKey.htm

    MenuMaskKey の説明によると「Alt Up 時の挙動を抑制するためのキーとして、上記のキーリストから Undefined になっているものを選択すべし」と説明されており、 Undefined のキーの中でも vk07 が最も若版だったことから AutoHotKey で定番化しただけのような気がしています。

    また、同 MenuMaskKey の解説によると vkFF もマッピング無しのキーとして解説されています。これは上記の Microsoft 発行の Virtual-Key Codes 内では完全に記載されておらず、Undefined よりもより強い意図で「マッピング無し」とされている気がします。(根拠はありませんが…)

    また vkFF 同様に、vk00 もマッピング無しのキーのようで、vk07 や vkFF と同様に Alt Up の挙動を抑制しつつ、他に何の挙動もしないキーでした。

    vk07 等の Undefined とされているキーが今回のゲームバーのケースのように今後使われる可能性があることを考えると、vk00 や vkFF を使った方がこういったケースへの心配は少ないように思いました。

    ちなみに、vkFF は確かに OS 上で何も機能しないキーコードなのですが、一部のキーボード等で送り込まれることがあるようです。例えば私の ThinkPad のキーボード左下にある Fn キーを押すと、vkFF の Down が発行されます。(もちろんそれ自体は OS 上でなんの動作もしないので無害なのですが)

    極稀ですがこういった vkFF が発行されるケースもあることを踏まえ、vk00 が最も無害なのではないか?と私は考察しています。

    とまぁ、長くなりましたが、私としては vk07 以外の無害なキーを選定した方が良さそうという結論です。

     |  yuko  |  返信
スポンサーリンク