きな粉もち.net

.NET関連仕事に携わっています。OSSのソースを読んで気がついたことを中心に呟いたりブログに投稿したりしています。最近はUiPathを使ったRPAも研究中。気軽にフォローやツッコミよろしくおねがいします! Gitはここを使っています https://github.com/kinakomotitti

UWP × GlobalHooks × 使えない理由を検証してみた

この記事の目的

この記事では、
UWPでGlobal Hookが機能するかについての検証結果をまとめること
を目的としています。

本題

★きっかけ

WPFでGlobalHookを使ったツールを作成しました。

作ったツールは、以下の機能を有しています。
1)GlobalHookを使って、ユーザー操作(マウスクリックやキーダウンなど)イベントを取得する。
2)イベントをきっかけにスクリーンショットを取得する。
3)エクセルファイルに2)で取得したスクショを時系列順に貼り付ける。

主に、何かのアプリのテストを手動で実行し、そのエビデンスを作成するときや、
何かの作業(特にGUIで操作するもの)を行うとき、その操作手順を記録するとき
に利用することを目的としたツール*1となっています。


このツールの配布の方法を考えたとき、GitHubからダウンロードするより、
UWPのほうが便利かと思い、WPF→UWPへつくりかえようと考えました。

UWPへのつくりかえにあたり、大きな問題となったのは、
スクリーンショットの取得
・エクセルファイルの出力
GlobalHookを使ったユーザー操作イベントの取得
です。

ということで、3つ目のGlobalHookが機能するかについて調べ始めました。

調べ始めてすぐに、StackOverFlowにて、GlobalHookがUWPで使えないという回答を見つけました。
stackoverflow.com


(自分が正しく議論が理解できていたら)
UWPではGlobalHookの選択肢はないということになります。

使えないってどういうことか・・・
コンパイルが通らないレベル?
実行時にエラーになるレベル?

という疑問があったので、どんなふうに”使えない”のか実際にサンプルを作って試してみることにしました。


★開発環境情報

Windows 10 1803
Visual Studio 2017 15.7.2
Grobal Hook Classはここのクラスを使いました。
github.com


★実装

UWPの画面自体はとてもシンプルで、
テキストブロックが1つだけある画面を作成しました。
テキストブロックの名前は"Sample"です。
f:id:kinakomotitti:20180526145633p:plain

画面が準備できたので、コードビハインドの実装をしていきます。

Hook時の操作を実装するメソッドを以下のように定義します。
以下の例では、マウスの左クリックイベントを拾うように定義します。

        void MouseHook_LeftButtonDown(GrobalHooks.MouseHook.MSLLHOOKSTRUCT mouseStruct) 
       {
            //TEXT BLOCKに実行時間を表示させる
            this.Sample.Text = DateTime.Now.ToString(”yyyyMMdd-HHmmss.ffffff”);
        }


次に、Hookのイベントを登録する処理を定義します。

        public void InitializeHooks()
        {
            // register events
            mouseHook.LeftButtonDown += new GrobalHooks.MouseHook.MouseHookCallback(MouseHook_LeftButtonDown);

            //開始
            mouseHook.Install();
            keyboardHook.Install();
        }


InitializeHooksメソッドを画面クラスの初期化時に呼び出します。

        public MainPage()
        {
            this.InitializeComponent();
            this.InitializeHooks();
        }
★動作確認!

実行したところのイメージは以下のようになります。
※初期表示時は”AAA”と表示しています。
 クリックすると、フックしたイベントが実行され、時刻を更新します。
 表示形式は、yyyyMMdd-HHmmss.ffffffです。

f:id:kinakomotitti:20180526145957g:plain



とりあえず・・・・


動いた(´▽`)
なんやかんやでUWPでもGlobalHookは動作することがわかりました!


★問題

しかし、そう簡単にはいかないようです(´・ω・`)


フックしたイベントは、UWPのアプリウィンドウの領域だけでしか発生しませんでした。。。
つまり、ウィンドウの領域外の部分での操作はフックできません。。。


これでは、ツールの目的を達成することができません。。。


上記の動作が本当だとしたら、考えられる対応としては、
UWPアプリを全画面表示させ、背景を透明にする
ことでしょうか( ゚Д゚)

そこまでいったら、グローバルフックではなく、画面のクリックイベントを拾えばよいですねw

まとめ

GlobalHookはUWPでも動作することが確認できた。
ただし、UWPのウインドウの中の操作に限る。←これが問題。。。
これがStackOverFlowでいわれていた”使えない”の理由なのだと感じました。

UWPだけでなく、フルスクリーンに対しての操作をフックしたい場合は
WPFWindows Formsを利用する必要がありそうです(=_=)

*1:つまらないものですが、プレリリース版は以下のURLで公開しています。 github.com