PCを適当に操作するツール作った

数独ソルバー×Azure Cognitive Servicesの方が伏線回収できていませんが、今日は寄り道ネタです。

きっかけ

どこそこのIT企業に勤めている古い馴染みがここ最近ずっとこテレワークしているらしく、たまに採取されるPCの操作ログみたいなので日頃の行いの是非を判断されるという何とも残念無念な形態が取られているとのことで、

「未操作が続くと理由を問い詰められて厄介なので、それを回避するためのツールを作ってくれ」

そんな一言が私のモノづくりソウルを燃え滾らせてしまいました。単純明快な構図ですこと。

まぁ一端のIT企業にいるんならそれくらい自分で作れよとか思ったり、そもそもの話がずっとPCに張り付いていればそんなことにはまずならんやろと思わなくもなかったのですが、誰しも小休憩とか散歩とかタバコとか気を紛らすタイミングは必要でしょうからね。知らんけど。

設計

というか概略図的な。作り終わって記事にするこの段になってたった今作ったものであることをここに懺悔します。

全体アーキテクチャ

View~ViewModelがUIスレッドなのは周知の通りかと。

その先のModelレイヤはおおよそどの処理も共通して「実行→スリープ→実行→…」の無限ループになるだろうなと思ったのでそれぞれサブスレッドに切るようにしています。

肝心の「人によって操作されているか?」の判断には、

  • マウスカーソルの座標が変更された
  • アクティブウィンドウが変更された
  • 最後のキー入力が更新された

辺りの情報が満遍なく必要です。それぞれの操作(マウス操作ならカーソル移動、プロセスハンドルならアクティブウィンドウ変更)を実行すべきかどうかについて、別スレッドで動くその他の情報が必要になってくるわけです。

別スレッドで得た状態を他のスレッドでも知るために一番右側の「スレッド状態管理クラス」というシングルトンのインスタンスを設けました。ここにスレッドセーフな辞書オブジェクトがあり、各スレッドで「人が操作しているか?」の最新情報が保持されています。全てのスレッド状態がfalse(一定時間操作されていない)となった場合に初めて自動での変更処理が発火します。

作ったもの

例によりてGitHubなり。名前がクソダサいのは見逃してください。

苦労話

使い慣れたWPFだしPrismだしロジックとかは別に悩まなかったのですが、2点だけハマりました。

1)Random.Nextが返す値が全スレッド一緒な件

それぞれのループ処理でウェイト時間はできれば異なって欲しいのですが、

int awaitSec = new Random().Next(10, 60);

とかでやると、よーいドンした全部のスレッドで一緒の値が返ってきてしまいました。

これは恐らくC#の仕様でRandomのシードのデフォルトにシステム時刻だったかを使ってる事情で、一緒のタイミングでnewしたオブジェクトは一緒のランダム値になることに起因するようです。

これについては以下を参考に、スレッドごとに別のシード値を返す(スレッドローカルな)Randomを提供するクラスを作成することで解決しました。

2)最新バージョンのPrismでInvokeCommandActionできなくなってた件

最新の.NET FrameworkではPrism 7.xをサポートしません、みたいな警告文が表示されてて不穏な感じだったのでつい先月リリースされてた8.0にバージョンアップしたのですが、ここが暗黒物質ダークマターでした。何がって、System.Windows.Interactivity.dllが同梱されなくなったとは思わないじゃないですか…

まぁ過ぎ去ったことにくよくよしていても仕方ないので代替手段なんかを色々手探っていたわけでありますが、やりたかったのはWindow.Closingイベントを拾う(閉じるボタン押したら代わりに通知領域に飛ばしたかった)ってだけで、何とかVMに持ってこようとしましたが、以下のQAを見つけて我に返りました。そうだ、コードビハインドに書こう、って。京都行くみたいなノリで。

Having an empty code-behind is however definitely just an ideal, which is usually not easy and not always necessary to achieve. Sometimes you need to use code-behind for purely view-related manipulation like changing layout for different window sizes, controlling animations, etc. For such actions, code-behind is a much better fit than trying to force this somehow into the VM.

c# – WPF MVVM calling ViewModel methods from code behind – Stack Overflow

我々は、ViewModelがDataContextと呼ばれる所以に、より一層自覚的にならなければいけませんね。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です