Alt + Tab > Mery クリックでメニューバーがアクティブになる件
-
>> 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 | 返信 -
>> 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 | 返信 -
> いただいた情報をもとに 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.htmlAutoHotKey 公式 (英語) の MenuMaskKey のリファレンス
https://www.autohotkey.com/docs/commands/_MenuMaskKey.htmMenuMaskKey の説明によると「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 | 返信 -
ご返信ありがとうございます。
> 上記のツイートの情報があり、確かめてみたところ、確かに vk07 で Windows 10 のゲームバーが起動してしまうようです。
そんな落とし穴が…。
> 極稀ですがこういった vkFF が発行されるケースもあることを踏まえ、vk00 が最も無害なのではないか?と私は考察しています。
情報ありがとうございます。
vk00 で実験してみたところ Windows 10 だと vk07 と同じように Alt がキャンセルされているのですが、Windows 7 だとどうも vk00 は Alt をキャンセルしてくれないようです。(Delphi の仕様なのか環境の問題なのかはわかりませんが…)
vk07 と vkFF は、Windows 7 でも 10 でも大丈夫っぽいですが ThinkPad の Fn キーで送信されてしまうという点についてはちょっと不安が残るところですね。
隠しオプションで送信するキーコードを変更できる仕組みか、そもそもその機能自体をオフにする仕組みを用意するというのも考えましたが、そこまでする必要があるかどうかは悩ましいところです… ^^;
| Kuro | 返信 -
> 隠しオプションで送信するキーコードを変更できる仕組みか、そもそもその機能自体をオフにする仕組みを用意するというのも考えましたが、そこまでする必要があるかどうかは悩ましいところです… ^^;
そうなんですよね、私も最適解をグルグル考えてるうちに、「そもそもそんなに困る場面ってあるんか?」という考えがよぎったり…
だけど、バグ (これは厳密にはバグじゃないですけど) を見つけたら対処したいと思うのは開発者の性 (さが) ですね…^^;その後しばらく調べてみたのですが、vkFF はやはり Fn などの目に見える動作の無いメタキーでしか送られることはなさそうです。
ただ、稀にその vkFF に AutoHotKey で動作を割り当てている方はいるようですね。例えば ThinkPad の Fn キーを押下した時に全角半角キーの動作をさせる…など、やろうと思えばできます。
ただそういったケースはそもそも AutoHotKey を利用している場合に限られますし、AutoHotKey 側でいかようにも回避できます。(Mery 上でだけ vkFF のフックをやめたり、vkXX での指定をやめて scXXX 形式(*)を使うなど)
(*) scXXX はスキャンコードの指定方法 (http://www.philipstorr.id.au/pcbook/book3/scancode.htm)
vkFF が一般的にメタキーとして利用されていること、 AutoHotKey ユーザーとの衝突はレアケースだと思うことから、もし実装するなら vkFF でいい気がします。
また、もしものときの対処としての隠しオプションは、この機能の On/Off くらいでいいのではないかと。キーコードだと設定方法とか説明するの面倒ですし、そもそもキーコードを変えたいようなディープなユーザーならそもそも AutoHotKey で対処できそうなので…
| yuko | 返信 -
調査のご協力ありがとうございます。
> だけど、バグ (これは厳密にはバグじゃないですけど) を見つけたら対処したいと思うのは開発者の性 (さが) ですね…^^;
それはありますね。対処できないならできないでも良いとは思うのですが、次に同じ質問が来た時にきちんと理由を添えて回答できるようにはしておきたいとは思いますね ^^> ただ、稀にその vkFF に AutoHotKey で動作を割り当てている方はいるようですね。
うーん、そうなるとやはり Mery 側で強制的に vkFF を OS に送り込んでしまうと誤作動のもとになりかねないですね。> vkFF が一般的にメタキーとして利用されていること、 AutoHotKey ユーザーとの衝突はレアケースだと思うことから、もし実装するなら vkFF でいい気がします。
そうですね、実装するなら vkFF の方向で検討してみようと思います。が、そういったシーンを考慮すると、やはり隠しオプションで ON・OFF はできたほうが良さそうですね。
この件についての問い合わせはまだ 1 件だけなので、なるべく他のユーザさんや他のソフトに影響を及ぼさないよう、デフォルトはその機能はオフで、どうしても Mery 側で対応が必要な場合のみ隠し機能を案内する形のほうが良いかもしれないですね。
| Kuro | 返信 -
> この件についての問い合わせはまだ 1 件だけなので、なるべく他のユーザさんや他のソフトに影響を及ぼさないよう、デフォルトはその機能はオフで、どうしても Mery 側で対応が必要な場合のみ隠し機能を案内する形のほうが良いかもしれないですね。
そうですね、それがいいと思います。デフォルト ON だとどうしても影響の可能性を捨てきれないですしね。
| yuko | 返信 -
v2.8.0 リリースお疲れさまです。
隠しオプション「AltTabFix=1」が AutoHotKey に干渉するかは気になるところかと思いますので、検証結果のご報告です。(Kuro さんの方でも既に試されているかもですが…^^;)
以下の AutoHotKey ソースコードで試しました。
*vkFF::Send {a}
このコードでは、修飾キーの押下状態に関わらず vkFF をフックして A キーを送ります。
結果、「Alt + Tab > クリックで Mery 選択」の操作で上記のコードでは A キーが送られました。もしかしたら仮想的に送られたキーコードでは AutoHotKey は反応しないかも?とほのかに期待しましたがしっかりとフックされたため、上記のような設定を使っているユーザがもし居た場合には (9割9分居ないと思いますが)、やはり影響がありそうです。
一方、以下のコードでは送られませんでした。
vkFF::Send {a}
上記は、修飾キー無しの単体 vkFF をフックするものです。「Alt + Tab > クリックで Mery 選択」では Alt キーが押下状態で vkFF が送られているために、フックされることはありませんでした。
想定通りの動作で、素晴らしいです!
| yuko | 返信 -
> v2.8.0 リリースお疲れさまです。
ありがとうございます。コメント欄がなくなっちゃったので、こういったコメントがもらえなくなるのは寂しいものですね ^^;> 隠しオプション「AltTabFix=1」が AutoHotKey に干渉するかは気になるところかと思いますので、検証結果のご報告です。(Kuro さんの方でも既に試されているかもですが…^^;)
ご協力ありがとうございます。いやぁ、試してないのです。そもそも AutoHotKey が何をするためのソフトなのかも分かっていないレベルですから… ^^;
> もしかしたら仮想的に送られたキーコードでは AutoHotKey は反応しないかも?とほのかに期待しましたがしっかりとフックされたため、上記のような設定を使っているユーザがもし居た場合には (9割9分居ないと思いますが)、やはり影響がありそうです。
ですよね…。AutoHotKey がやっているキーコード送信の仕組みをパク…参考にさせていただいたので、当然といえば当然かもしれません。
他にもいくつかキーコードを送信する API を試してみたのですが、Alt キーに勝てたのは AutoHotKey 方式だけでした。
> 想定通りの動作で、素晴らしいです!
ありがとうございます ^^ yuko さんのお力添えのおかげです。まさかこんな方法があるなんて想像もしていなかったので、本当に助かりました。
| Kuro | 返信