はじめに
みなさまは Mac のメニューバーでネコを飼う RunCat というアプリをご存知でしょうか?CPU 負荷に合わせて走る速度の変わるネコをメニューバー上に表示するだけというしょうもないアプリですが、現在では世界累計 45,000 ダウンロードを突破し、多くのみなさまに可愛がってもらえる定番アプリとなりつつあります。はじめは悪戯心で作成したジョークアプリが、思いもよらず高評価をいただけており大変嬉しいです。
一方で、Windows 版がほしいとの声もちらほら聞こえるようになり、Windows 版の模倣アプリも作られつつあることを知りました。そこで、ちゃんと本家から Windows 版も出したいなぁと思い立ったが吉日ということで、格安で ThinkPad を仕入れて Windows アプリ開発に初挑戦してみました。
成果物
RunCat for Windows
Windows のタスクバー上に常駐し、CPU 負荷に応じてネコが走るだけの最小限機能を搭載したアプリを開発しました。GitHub 上にソースコードを公開しており、アプリそのもの(RunCat.exe)もここ経由でダウンロード可能にしています。→Kyome22/RunCat_for_windows(Releases のページに exe があります。)
2025 年から Microsoft Store に RunCat 365 という名前でリリースしています。
引き続きOSSとして開発を続けています。
初めての Windows マシン
- ThinkPad E495(14-inch ノート)
- OS: Windows 10 Home 64
- メモリ: 16GB
- プロセッサ: Ryzen 5 3500U 2.1G 4C MB
- SSD: 256GB
- キーボード: US 配列カスタマイズ
- 価格: ¥ 77,550
メインマシンとして使うつもりはなく、小さい Windows 向けアプリを開発できる程度に Visual Studio が動けば OK という気持ちでスペック決めをしました。このくらいのスペックのマシンが 8 万円弱で手に入ったのは上々ではないでしょうか。(調べてみたら MacBook Air だとメモリ 8GB、SSD が 256 でも 10 万円オーバーなんですね。)
ただ、仕方がないことなのですが、メインマシンと同じ US 配列ではあるものの、キー配置がかなり違っていて入力ミスが多いです。とりわけ Control キーと S, Z, X, C, V キーあたりの配置がかなり厄介者で Mac で慣れた手つきでショートカットを入力すると、一つずれたコマンドを叩いてしまうんですよね。慣れるまで我慢...
初めての Visual Studio 開発
今回は Visual Studio 2019 (コミュニティ)を使いました。
Windows 向けのアプリ開発プロジェクトの種類
- Windows フォームアプリケーション
- WPF: Windows Presentation Foundation アプリ
- UWP: Universal Windows Platform
このような感じで何種類かあり、よく耳にする.NET Core や.NET Framework などフレームワークにより微妙に種類が違ったりするようですが、よくわからなかったので、タスクバーのアイコンについて検索した時に出てきた NotifyIcon というキーワードを頼りに、タスクバーだけのアプリが作れるプロジェクト形式ということで Windows フォームアプリケーションを選択しました。
- How can I make a .NET Windows Forms application that only runs in the System Tray?
- Creating a Tasktray Application
Visual Studio でのアプリ開発について、基本的に Xcode でのアプリ開発と流れは変わらないので、あまり難しくは感じませんでした。強いて言えば画像や色などのリソース管理について、Xcode の Assets Catalog と Visual Studio の Properties-Resources は使い勝手が結構異なるので、とっつきづらかったですね。ただ、Properties で管理することでResources.リソース名のようにリソースをコード上で扱うことができるようになるので、ここは便利ですね。(Android 開発でのR.リソースキー名に似ている)
開発ハマりポイント
最初に挑戦するアプリとしては RunCat はかなり特殊なところをついているので、案の定いくつかハマってしまいました。
なんか参考文献が古い!
c# NotifyIcon sizeとかc# cpu usageとかc# detect dark themeとかのキーワードで調べていくんですが、大体行き着く文献が 2011 年や 2007 年とかかなり古い記事ばかりなんですよね。おそらく Windows フォームアプリケーションが古い形式なのがいけないのでしょうが、信憑性がよくわからないし、今(Windows 10)でも通じる技術なのかもよくわからない感じで裏を取るのが結構大変でした。
ico ってなんや
Windows アプリではアイコンを扱う時は ico という拡張子のものを使うんですが、こいつが曲者で、一つのファイルで複数のサイズの画像を取り扱えるやつなんですね。Web だとファビコンの拡張子が ico なんですが、これを自作するのが mac だと厄介で、Preview.app で書き出す時に option キーを押しながら形式を選択しようとするとMicrosoft アイコンというのが選択できて ico で出力できるのですが、複数サイズに対応できるわけではありません。また、PhotoShop を使ってもプラグインを入れなければ ico は取り扱えません。あまり編集/書き出し環境が整っていない形式がなぜ普及しているのかよくわかりません。
どう頑張っても 16×16px の制限を突破できない
上にあげた成果物の GIF アニメーションをみていただければ分かる通り、タスクバー上のネコの解像度はかなり悪いです。それもそのはず、たったの 16×16px しか与えられていないのですから、ネコの輪郭がかなり潰れてしまいました。一応環境設定で UI を 125%に拡大するオプションをオンにすれば、32×32px のリソースが採用されるようですが、それでもかなり狭いです。まず、正方形でなければならないというのがかなり苦しい。NotifyIcon に表示できる画像は ico なので、自ずと正方形になるのですが、横幅をもう少しくれれば...ぐぬぬという気持ちです。macOS 版の方は正方形の縛りがなく、Retina ディスプレイならば 56×36px、それ以下の解像度のディスプレイでも 28×18px が使えるのでかなりましですね。
ダークテーマかどうかの取得方法が煩雑
昨今のダークモードの流行りを取り入れたのかはわかりませんが、最近の Windows ではダークテーマというのを環境設定で選択できるようです。しかし、昔からあった仕様ではなく、Windows フォームアプリケーションの方はフレームワークがあまりアップデートされていないため、現在のテーマがダークテーマかどうかを取得する簡単な方法はないようです。どっかの設定に書き込まれている情報を直接読み取って、現在どちらのテーマに属しているのか判断するというのが苦肉の策としてあるようです。
- Win32 アプリケーションで Windows 10 のライト/ダークモードを検出する方法
- C# Windows10 のダークモードいいっすね!アプリケーションのダークモード設定を取得してみましょ!ヽ(^。^)ノ
- ユーザー設定の変更をイベントで受け取る
Win32 と UWP の違いが一見わからん
いろいろ実装を試しながら文献を調査していると、Microsoft.Win32由来のクラスやメソッドを使っている実装例と、UWP由来のクラスやメソッドを使っている実装例が、文献によって入り乱れていることに気付きました。「よっしゃ、これなら簡単に実装できそう」と思ったら UWP 由来で今回は使えない...というのが何回もあって萎えました...
EventHandler を直呼びするのどうやるの?
RunCat は 5 秒に一回 CPU 負荷を取得してネコの走る速さを更新するのですが、そこで Timer を使っています。しかし、Swift のTimer.fire()のように Timer に指定した処理を即時発火するメソッドが用意されていないので、直接処理をコールしたくなりました。そこで、Timer に登録する EventHandler 用のメソッドは、引数に(object sender, EventArgs e)を持つので引数が用意できず直接叩けないじゃんという状態になったのですが、メソッド内でsenderもeも使っていないならば、適当に引数詰めてコールすれば良いようです。
private void ObserveCPUTick(object sender, EventArgs e)
{
// 処理
}
// 直接叩く
ObserveCPUTick(null, EventArgs.Empty);
所感
まぁいろいろ書きましたが、現状のものは Visual Studio を初めて起動してからだいたい3日くらいで実装できたので、mac 版のときの躓きに比べたらかなり楽な勝負でしたね(Store でのリリースもないし)。特に、CPU 負荷取得するのが簡単すぎて逆に驚きました。たった数行でこういうシステム情報が取ってこれるところが Windows アプリの良さかもしれません。
最小限機能のみの状態で、まだまだ改善の余地はありますが、とりあえず成果物を出すことができて満足ですね。今後はオープンソースの強みを活かして、みなさまの Issue やプルリクを待ってみようと思います。
