見出し画像

Windows10を自前のタイマーでスリープさせようとしたが、USBコントローラー接続判別プログラムができあがった話

 以前の記事の続きである。

 概要としては、Windows10 が、自動的にスリープモードへ移行せず、いろいろ試行錯誤しても改善しなかったので、自前でタイマーを作って、時間が来たらスリープコマンドを叩くようなスクリプトを用意しようと考えた話である。

 ――こう書くと、たった数行に収まるような内容のない記事を、ただただダラダラと書いただけだった気がしている(笑)。

 ということで、今回は、スクリプトを用いて自前でスリープをさせる仕組みを作ろうという話なのだが、どうしてスクリプトなのかというと、まあ、単に、タイマーを回して、とあるコマンドを叩くぐらいであれば、実行ファイル(exe ファイル)まで作らなくても、より手軽なスクリプトで充分だろうと思ったからである。

 

○スクリプト言語に AutoHotkey を選んだワケ

 スクリプトといえば、今や、Python や Ruby などが手軽で有名なのかもしれない。しかし、私は、SE を離れ、もはや 20年。未だ、これらの言語に触れる機会がないまま、現在に至ってしまっている。

 手軽といえば、やはり大抵のパソコンに入っている Excel の VBA(いわゆるマクロ)もあるが、ただ、これで今回の自前スリープを実装するとなると、基本的には、常に Excel を立ち上げっぱなしにする必要があり、それはそれでなんかカッコ悪い……。

 JavaScript というか、JScript でやろうと思ったが、どうせなら……ちょうど最近触れることになった、AutoHotkey でチャレンジしてみようか? と思ったのは、次の理由からだ。

・元々は、キーボードの割り当てを変更するソフトだったようだが、今では、スクリプト言語としての地位も確立している
・その名のとおり、キーボード関連の実装に強いのではないかと感じ、結果、プログラムの実装が楽になるのでは? と思った(後述)

 AutoHotkey については、私自身も、キーボードの割り当てを変更し、日々恩恵を受けているところだが、この辺の詳細は、次の記事をご覧いただきたい。


○プログラムの仕様と実装

 さて、タイマーでスリープさせるスクリプトの仕様だが、ざっくりいうと、次のようになるだろう。

(1)スクリプト起動時にタイマーを走らせ、セットされた時間が経過したらスリープするコマンドを叩く
(2)マウスが動かされたり、何かキーボードが押されたらそのタイマーをリセットする。

 そして、(2)を、もっと詳細に噛み砕くと、

(2)①「マウスが動かされた」とは、例えば1秒おきくらいにマウスの位置を保存し、それが、現在のマウスの位置と異なっていた場合とする
(2)②「何かキーボードが押された」ことを確認するために、常に、キーの入力状態を監視する

ということになるだろうか。前述のとおり、AutoHotkey を選んだ決め手の一つが、この、(2)②であるキー監視関係の強みがあるのではないか、と思ったからである。

 とりあえず、(1)については、時間が経過したらメモ帳が立ち上がる程度ものは数行でできあがった。また、(2)①についても、マウスを動かした際に、タイマーがリセットされることを確認した。

 さて、残った仕様の(2)②について、キー状態をどのように取得しようか……と AutoHotkey のドキュメントを確認していたときである。なんと、すごく便利な機能……というか「変数」を見つけてしまった。

 AutoHotkey には、環境変数というのがあり、その中には「システムがアイドル時間になってからの経過時間」(A_TimeIdle)というものがある。これを利用すれば、自前でキーボードなど監視する必要もなく、単に、この A_TimeIdle が「指定した時間」を超えた場合にスリープコマンドを叩けばよいのではないか? と思ったのである。

 これで、(2)②がシンプルに解決できるだけでなく、(2)①の泥臭い処理も不要になり、よりシンプルなプログラムになる。あとは、「メモ帳が起動する部分」を「スリープコマンドを叩く」ように差し替えるだけでよい。これもうまくいった。

 全てはうまくいった。あっという間に完成した。

 

○スリープ地獄?

 ……が、ここでひとつの事件が発生した。

 このスクリプトによるスリープさせたあと、そのスリープ復帰後のログイン画面で、パスワードを入力しようとすると、突然! また、スリープに入ったのだ。なぜ? 再度、適当にキーボードを叩き、スリープから復帰させ、パスワードを入力しようとするが……やはり、すぐに「プツン」とスリープに入る。

 何が原因か分からないが、とりあえず、デスクトップ画面まで到達しないと何もできない。次は、急いでパスワードを入力するが……やはり、パスワードの入力が終わる前にスリープに入る。

 このスリープ地獄を何回繰り返しただろうか……こうなると、もうどうしようもない。こんな短時間では、このスクリプトを止めることもできない。というか、何もできない。ただひたすら、スリープから復帰しては、スリープ……を繰り返すだけなのだ。

 今まで、スリープに入ってくれなくて、どうにかしてスリープに入ってもらいたいと思っていたが、逆に、こんなに何回もスリープばかり入るのもまた困りものである。何事もほどほどがよい……。

 結局、どうしようもないので、強制的に電源落とすこととなった……。

 

○連続スリープの原因とは?

 再起動後、再度プログラムとにらめっこすることになる。このような仕様であるため、デバッグ自体が非常に困難なのだが、動きを細かく見ていくと、どうも、次の可能性が考えられた。

・A_TimeIdle がログイン画面ではリセットされていない可能性があり、スリープから復帰しても、またすぐにスリープコマンドが叩かれた可能性がある。

・スリープコマンドが叩かれてから実際にスリープしてしまうまでの数秒間の間にも、1秒おきにずっとスリープコマンドが叩かれ続け、そのコマンドが累積していった可能性がある。

 または、これら2つの合わせ技だろうか?

 テストではうまくいっても、本番でうまく行かないというのはよく聞く話だが、それを目の当たりにした瞬間だった。結局、どんなにテスト環境を用意したとしても、それはテスト環境でしかないのだ……。

 

○さらなる問題発生

 ということで、連続スリープを防ぐために処理を一部修正し、復帰後は、システムが一旦アイドル状態ではなくなるまでは、このスリープタイマー処理を一時停止するようにした。

 これにより、スリープ復帰直後に、再度スリープすることはなくなったのだが、逆に、仮に予期しない勝手な復帰があった際に、その後は、一度、マウスを動かすか何かしない限り、このタイマーによるスリープに入ってくれない仕様にはなった。とはいえ、これは、そもそも勝手に復帰する方が問題なのであって、そんなイレギュラーなことにまで対応させる仕様にする必要もないと思い、割り切った。

 ということで、これでゲームコントローラーを刺したままでもスリープに入ってくれることになるだろう。

 ただ、これも、一応、実際にやってみないと分からないかと思い、ゲームコントローラーを刺したままこのスクリプトを実行させるが、時間が経過してもスリープしない。詳しく調べると、なんと、ゲームコントローラーが刺さっている間は、A_TimeIdle が増えないことが分かった。

 つまり、この用意された A_TimeIdle を使う以上は、結局は、標準のスリープの挙動と同様に、ゲームコントローラーを刺していたら、いくら待ってもスリープに入らないのだ。
 
 やはり、この A_TimeIdle を使うのを諦め、泥臭いプログラムを書いて、ゲームコントローラーの刺しっぱなしでも対応させるべきなのか。それとも、ゲームコントローラー刺したままのスリープは諦めるべきなのか……。

 

○単純でないスリープの仕様

 A_TimeIdle を使わずに、全て自前でスリープタイマーを管理するのであれば、どのようなことに注意する必要があるのか。ざっと気づいただけでも、例えば、次の最中にスリープに入ったら困ると考えられた。

(特に困るもの)
・インターネットからファイルをダウンロードしている最中
・ネットワークの別の端末から共有ファイルにアクセスが継続している最中(他の端末から動画閲覧中など)
・光学メディアに記録している最中

(やや困るもの)
・Windows やウィルス対策ソフトのアップデート中
・ソフトウェアのインストール中
・ファイルのコピー中(ファイルバックアップ中など)
・ウィルスチェック中

 これら全ての状況を把握した上で、自前で泥臭いプログラムを書くのは、今の私の技術では到底無理だし、それ以前に、無意味な車輪の再発明はやるべきではない。

 結局、この自前スリープにどこまでの機能を求めるかにもよるだろうが、今の私のように、家庭内サーバーとしての使い方も兼ねているのであれば、単にマウスやキーボードだけを監視するだけでは不十分だったのだ。

 そして、やはり、単に「時間が経過したらスリープに入る」という、一見すると非常に単純なことでも、実は、目に見えない裏側でやっていることというのは想像以上に多く、自前で用意するというのは、そんなに簡単な話でもないということなのだ。

 

○最終的にできあがったスクリプトとは……?

 結局、この私が作ったスクリプトは、今後、補助的な使い方はできるかもしれないが、メイン利用は厳しいものとなった。

 加えて、このパソコンを利用していく中で、起動後、一度でも別の端末からリモート接続したあとは、再度、このパソコンにログインし直しても、指定時間経過後に正常にスリープに入ることが分かったので、せっかく作ったこのスクリプトも、使う必要はなくなった。

 ただ、USBコントローラーが刺さっている間は、それでもやはり、スリープに入らないことには変わりない……。

 一応、このスクリプトでは、結果的に、USBコントローラーが刺さっている可能性が判別できるため(A_TimeIdle が増えないため)……もし、刺しっぱなしの場合は、ツールチップで、その抜き忘れを促すよう注意を出すことにした。

 なんともしょうもない結末だ(笑)。

 ただ、久々にプログラムの難しさと面白さを体感できたのはよかった。

【関連記事】

この記事が気に入ったらサポートをしてみませんか?