2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Linux/Web畑の自分が、AIと一緒にRustでWindowsネイティブアプリを作って配布するまで

2
Posted at

はじめに

普段、私はLinuxサーバの上でWebサービスやメールサービスを作っているエンジニアです。よく書く言語はPerl・Erlang・JavaScriptあたり。WindowsのGUIプログラミングは未経験で、Rustに至っては一行も書いたことがありませんでした。

そんな私が、ここ2か月ほどで Windowsネイティブの画像ビューワ「mImageViewer(mIV)」 を作って一般公開し、ありがたいことに窓の杜さんの記事でも紹介してもらえるところまで来てしまいました。しかも正直に言うと、ソースコードは一切書いていませんし、読んでもいません。 AIと相談しながら設計を考え、実装はすべてAIに実行させる——そんな形でも、ちゃんとアプリケーションを作ることができました。

きっかけは、長年愛用してきた ViX という画像ビューワです。フォルダごとに整理した、大量の個人的な画像コレクションを眺めるのに、軽くて快適なViXは手放せない存在でした。

ただ、このViXには困った事情があります。32bit時代のソフトで、もう何年もメンテナンスが止まっているのです。実際、2018年には DLL読み込みに関する脆弱性(JVN#56764650、CVSS v3で7.8) が報告されていて、JVNからは「利用の中止を検討するように」と呼びかけられている状態でした。画像ファイルと同じフォルダに細工されたDLLが置いてあると、最悪の場合、任意のコードを実行されかねない、というものです。開発者とは連絡が取れず、対策版も出ていません。本当は、使い続けるのはまずい。

…とはいえ、自分が前から持っているファイルを眺めるぶんには大丈夫だろう、とだましだまし使っていました。でも心のどこかでは、ちゃんとした後継がほしいとずっと思っていたのです。

もちろん、代替ソフトは探しました。が、どれも決め手に欠けました。一番の理由は、ViXにある「フォルダツリーを深さ優先でたどって"次のフォルダ"へ移動する」操作が、ほかのビューワにほとんど無かったことです。同じ階層の兄弟フォルダを行き来できるものは多いのですが、深い階層をどんどん潜りながら次々にフォルダを移っていく、あの操作感が再現できない。大量のフォルダを延々と見ていく私にとって、これが無いのは致命的でした。

「無いなら自分で作るしかないか。でもWindowsのアプリなんて作ったことないしなあ」と思いつつ、背中を押したのはAIでした。最近、趣味でも仕事でも Claude Code(AIのコーディング支援ツール)を使うようになっていて、「今のAIのレベルなら、しっかりしたWindowsアプリでもいけるのでは? ちょっと試してみよう」と思い立った——これが本当のきっかけです。

結論から言うと、なんとかなってしまいました。この記事は、その2か月の記録です。

最初は画像だけのはずが、気づけば"なんでもビューワ"に

最初の目的は、フォルダごとに整理した画像を、ViXの代わりに快適に眺めることだけでした。ところが、いざ作り始めると「ついでにこれも見たい」が止まらなくなります。

  • 自炊PDFも見たい。 自炊には ScanSnap を使っているのですが、スキャンするとゴミや汚れが縦線のように写り込むことがあります。これをAIで自然に消したくて、消しゴムツール(選んだ範囲を、AIが周囲となじむように補完してくれる機能)を付けました。もちろん、書籍PDFを画像と同じ感覚でめくりたい、という欲もありました。
  • AI生成画像の整理をもっと快適に。 大量に生成した画像の管理には、これまでEagle(for Windows)を使っていました。良いソフトなのですが、私の使い方ではいくつか不満が溜まっていて——たとえば、AIイラストのプロンプト(生成時の設定)を取り込むのに拡張機能を毎回手動で起動する必要がある、テーマごとに300〜500枚生成して「ボツ/採用候補/採用」を2段階で振り分ける運用をしていると、枚数が多いときに"まだ評価していない画像"が一覧から漏れて出てこないことがある(レイアウトを選び直すと直る)、そもそもEagleにはDBの上限(25万アイテム)があって、アップデート時にそれを超えてカタログを作り直すはめになったことも。「自分のビューワなら、このへん全部どうにかできるのでは?」と思い始めました。
  • 動画も見たい。 ついでに動画まで。これも既存ツールに小さな不満があって——音量がバラバラなのにノーマライズ機能がない、一度見たシーンをまた探すのが大変、など。

…という具合に「あったらいいな」が浮かぶたびに機能を足していった結果、画像ビューワのつもりが、PDFも動画も扱う"なんでもビューワ"に育っていきました。

(実は、この「思いつくたびに足せた」こと自体が、この記事で一番書きたかったことに繋がります。その話は最後に。)

なぜ Rust × Windowsネイティブ になったか

私の本業はLinux + Webなので、本来ならブラウザで動くWebアプリにするのが一番慣れた畑です。でも今回は、最初からWindowsネイティブアプリ一択でした。

理由は単純で、手元のHDD/SSDに溜め込んだ大量の画像や、自炊でスキャンしたデータを見るには、ローカルで完結するWindowsアプリが一番快適だからです。何万枚もの画像をクラウド経由で扱うのは現実的ではないですし、ローカルのファイルをそのままヌルヌル表示できる、というのが絶対条件でした。

加えて、メインPCには RTX 4090 を積んでいます。AI画像生成のときに使っていたアップスケールをmIVにも組み込めば、4Kディスプレイで昔の解像度が低めの画像も綺麗に見られるのでは——という欲も湧いてきました。

問題は言語です。私はネイティブアプリを書いたことがありません。そこでAIに「C++とRust、どっちで書くのがいい?」と素直に聞いてみたところ、「Rustが得意です」 という答え。じゃあRustで、と即決しました。自分に経験がない以上、AIが一番力を出せる言語に乗るのが合理的だろう、と。

Rustを選んだ理由はそれだけで、深い思想があったわけではありません。ただ、Rustはメモリ安全性が言語レベルで保証されていて、CやC++でありがちなバッファオーバーフローのような"メモリ系"の不具合が出にくい、というのは地味にありがたい点でした。私の用途では、ネットから拾ってきた画像を開くことも多いので、「壊れた画像や細工された画像を読んでも、変なことが起きるリスクがC++より少しは減るかも」 くらいの、ゆるい安心感です。

とはいえ過信は禁物で、これはあくまで画像デコードのような"メモリ系"の話。たとえばViXで報告された脆弱性は「同じフォルダにあるDLLをうっかり読み込んでしまう」という別系統の問題で、言語をRustにしても自動では防げるものではありません。言語を変えれば全部安全、というほど甘くはない、というのも正直なところです。そして何より、メモリが安全でもロジックのバグは普通に出るわけで——そこは後述の「実機確認の壁」で散々味わうことになります。

2つのAIを使い分ける

基本の進め方は地味で、「AIに実装を頼む → 動かしてチェックする」の繰り返しです。うまくいけばそれで終わり。問題は、何度やってもうまくいかないときです。

最初は Claude Code(Anthropic)だけで進めていました。ところが、コードベースが3万行を超えたあたり——開発を始めて1週間ちょっと、4月の半ばごろ——から、一筋縄ではいかない場面が増えてきて、セカンドオピニオンとして Codex(OpenAI)も併用するようになりました。今ではこの2つを使い分けるのが定番です。

ちなみに料金は、Claude Codeがもともと月200ドルのプラン、Codexは月100ドルのプラン(5月は使用量が2倍になるキャンペーンをやっていてお得でした)。キャンペーンが終わった今はどちらも月200ドルのプランで……まあ趣味への投資とはいえ、財布にはなかなか痛かったです。

今の定番フロー

  1. 両方のAIに、同じ「こういう機能を作りたい」を渡して、それぞれに設計を考えさせ、ドキュメントに書かせる。 いきなり実装させず、まず設計書を2案出させるイメージです。
  2. お互いに相手の設計ドキュメントを読ませて、意見をマージする。 別々のAIなので着眼点が違い、片方が見落とした点をもう片方が拾います。おもしろいのが、こちらが明示的に渡さなくても、Claudeがファイルを見張っているのか、Codexの書いた設計ドキュメントを勝手に見つけて先に取り込んでくることがあること。気づいたら2人で勝手に相談している、みたいな状態になります。
  3. 実装担当とレビュー担当のどちらを任せるべきかも、AI自身に聞く。 意見が割れることもありますが、一致することもあって、一致したらその布陣で作業させます。

それでも詰まったら

  • 実装担当とレビュー担当を入れ替える。 これが地味に効きます。実際、動画まわりでは——

    • Claude Codeが実装 → Codexがレビュー:5回以上の手戻りもザラ
    • Codexが実装 → Claude Codeがレビュー:ほぼ一発、多くても1往復

    と、担当を入れ替えるだけで手戻りの回数が劇的に変わったことがありました。一方で「じゃあ動画は全部Codex」かというとそうでもなく、Codexが何度も詰まったバグをClaude Codeが一発で直したことも。どちらが上というより、得意・不得意が場面ごとに違うのだと思います。

  • AIに状況を説明させて、打開策そのものを出させる。 「これで詰まっている。原因の仮説を2〜3個」「ログを仕込んで分析するならどう設計する?」「再現用の最小コードを書ける?」——直し方ではなく"進め方"を相談すると、抜け出せることが多いです。

どれくらいAI同士でレビューを回していたかというと、v1.0.0 までの約1,500コミットのうち、メッセージに「Codex」と書いてあるものが 約415件、AIレビューで指摘された点を直したコミットが 約337件。実装の片手間というより、レビューと修正が開発の主成分でした。

コードを読まない私にとって、この"2つのAIに設計させて、突き合わせて、競わせる"やり方こそが、行き詰まりを抜ける一番のツールになっています。

それでも苦戦した ― 時間と「動作確認」の壁

コードを書くのはAIなので、実装そのものの苦労はほとんどありません。では何が大変だったかというと、動作確認です。これに尽きます。

私の作業の大半は、こんなループでした。

  1. AIに実装してもらう
  2. ビルドして、実機(自分のWindows機)でアプリを実際に使ってみる
  3. 「なんか動きがおかしいぞ」と気づく
  4. その症状をAIにフィードバックする
  5. 1に戻る

この 2〜4のループがとにかく回数が多く、時間を食いました。 AIはコードを書くのは速いのですが、最終的に「実際に動かして目で見る」のは人間の仕事なので、ここがボトルネックになります。

地味に困ったのが、AI自身にGUIを操作させるのが難しいことです。一応、画面を操作させること自体はできるのですが、時間がかかりすぎて実用になりませんでした。結局、ボタンを押して挙動を確かめる役は、ほぼ私が手でやることに。

動画 ― フレームドロップとちらつき

全体でいちばん時間を吸われたのが、この動画でした。どれくらい沼だったかというと、v1.0.0 までのコミットのうち、動画関連がざっと 550件以上。全体の3分の1以上が動画がらみという有様です。

  • そもそもRustのGUIフレームワークでそのまま動画を描画すると、どうしてもフレームドロップ(カクつき)が避けられませんでした。GPUで描画する経路を組んでみたり、それを撤回したり、再生エンジンそのものを作り直したり……と紆余曲折した末、最終的に動画の表示だけはフレームワークに任せずネイティブに描画する作りへ移行しています。かなり大きな作り直しでした。
  • 画面の"ちらつき"のように、言葉で説明しづらい症状もありました。AIに口頭で伝えてもなかなか伝わらないので、OBSで画面を動画キャプチャして、その動画をClaude/Codexに見せて分析させるという手を使いました。「この瞬間にこうちらつく」を映像で渡すと、原因の特定がぐっと早くなります。

もうひとつの沼 ― VST3プラグインとウィンドウハンドル

動画の"音まわり"で、さらに深い沼にハマったのが VST3プラグイン対応です。音声にエフェクト(音量の調整やイコライザなど)をかける仕組みで、趣味のDTMで使っているプラグインを動画再生にも活かしたくて入れました。

ここでこだわったのがサンドボックス化です。私はDTMで Bitwig Studio を使っているのですが、これはプラグインを別プロセス(サンドボックス)で動かす仕組みになっていて、とにかく安定しています。以前 Cubase を使っていた頃は、ひとつのプラグインがクラッシュするとアプリ全体が巻き込まれて落ちることがありました。mIVではそれを避けたくて、最初から「プラグインは別プロセスで動かす」と仕様に決めました。

ところが、これが想像以上に大変でした。プラグインのGUIウィンドウを表示するには、Windowsのウィンドウハンドル(HWND)というものをやり取りする必要があるのですが、別プロセスだとこれが素直に共有できません。結果、プラグインの画面が真っ白のままハングする、表示できても右クリックが効かない、ウィンドウサイズやDPIがずれる……といった問題が延々と出てきました。実際、VST3まわりだけでv1.0までに約190コミット、うちウィンドウまわりの格闘だけでも60件以上を費やしています。

そもそも私はWindowsプログラミング未経験で、HWNDが何なのかすら知りませんでした。なので、「HWNDって何?」「なぜ別プロセスだと画面が出ないの?」をAIに一から教わりながらトラブルシュートするという、字面だけ見ると不思議な開発スタイルに。専門外の領域を、AIに家庭教師についてもらいながら少しずつ攻略していった感覚です。

動画とVSTのまわりは、機能の複雑さも相まって、専門外の私が一番心を折られかけた領域でした。

性能を測るのも一苦労 ― TensorRT対応

もうひとつ、毛色の違う苦戦がAI高速化まわりです。AIアップスケールを少しでも速くしたくて、NVIDIA GPU向けの高速化(TensorRT)にも対応しました。これも別プロセスに隔離しています。理由はいくつかあって——AI推論エンジン(ONNX Runtime)が1プロセスにつき1回しか初期化できず、普段使うDirectML版とNVIDIA向けのTensorRT版を同じプロセスに同居させられない(読み込むDLL自体が別物)こと、そしてVSTと同じで、もし不安定でも本体を巻き込まないようにしたかったこと。結局ここでも"クラッシュを隔離する別プロセス"という構成に落ち着きました。

そしてここでも、動作確認ならぬ"性能確認"が地味に大変でした。速くなったかどうかは、実際に測ってみないと分かりません。AIは「たぶん速くなります」と言ってはくれるものの、鵜呑みにはできない。そこで、ベンチマーク用の小さなコードを書かせて実測する/タイルサイズなどの条件を変えながら何度も測るといった作業を、かなり細かく指示しながら進めました(計測まわりのコミットだけで80件以上)。「ここをこう変えて、静かな環境でもう一度測って」と、AIにほぼ手取り足取りの計測です。

性能まわりは、AIに丸投げでは答えが出ない典型でした。"何を・どう測るか"は人間が決めてやらないといけない——というのを実感した部分です。

AIでも、諦めた機能はある

ここまで「AIならどんどん作れる」風に書いてきましたが、もちろん失敗もあります。たとえば、

  • スキャンの縦線を自動で検出して消す機能:手動の消しゴムはうまくいったのに、検出の自動化は試行錯誤しても十分な品質に届かず断念。
  • 裁断してスキャンした本を見開き表示するとき、裁断で欠けた中央部分をAIで補完する機能:これも品質が安定せず、一度は入れたものの結局は削除しました。

AIに任せれば実装の"量"はこなせますが、狙った品質に届くかどうかは別問題です。何でも思い通りになるわけではない、というのも正直なところでした。

できあがった mIV

そんなこんなで完成したのが mImageViewer(mIV)です。最初は「ViXの代わりに画像を見るだけ」のつもりが、気づけばこんなアプリになっていました。

サムネイルグリッド表示

主な機能をざっと挙げると、

  • GPUで描画する、高速なサムネイル一覧とフルスクリーン表示。 大量の画像でもサクサク動くことを最優先にしました。
  • ZIP / PDF をフォルダのように開ける。 自炊した書籍PDFも、画像と同じ操作でめくれます(見開き・右→左読みにも対応)。
  • AIによる4倍アップスケールやノイズ除去、消しゴム(非破壊修復)。 手持ちのGPUを活かせます。
  • 動画のインライン再生。 倍速(音程はそのまま)・音量ブースト・ブックマーク・チャプターなど。
  • RAW / HEIC / AVIF / JPEG XL など幅広いフォーマット、さらに往年の Susieプラグイン(32bit) まで。
  • そしてもちろん、フォルダツリーを深さ優先でたどる移動。ViXを手放せなかった、あの操作です。

フルスクリーン表示・EXIF

AIプロンプトのメタデータパネル

AIアップスケール

第2章で書いた"既存ツールへの不満"も、自分のアプリなので好きなだけ解決できました。答え合わせ的に並べると、

  • Eagleの「プロンプト取り込みに拡張機能の手動起動が要る」 → mIVは、Stable DiffusionやComfyUIなどで生成した画像のプロンプトをPNGから自動で読み取り、フルスクリーンの右パネルにそのまま表示します。さらに読み取るだけでなく、プロンプトの全文検索もできるようにしました。検索エンジンの作り方も——言語をRustにしたときと同じで——AIに「どう作るのがいい?」と相談して、Rust製の全文検索エンジン Tantivy を採用しています。
  • Eagleの「大量だと"未評価"の画像が一覧から漏れる」「25万アイテムの上限」 → mIVは表示範囲だけをメモリに載せる作りで、上限のような明確な制限を設けていません。レーティング(★1〜5)やタグで振り分けても表示が破綻しないようにしています。
  • 「動画の音量がバラバラ」 → ラウドネスを測って揃える(正規化)仕組みを入れました。音が小さい動画はブーストもできます。
  • 「見たいシーンをまた探すのが大変」 → 動画全体のサムネイルを一定間隔でタイル状に並べて表示し(Sキー)、見たい場面をクリックすると一発でそこへシークできるようにしました。チャプターやシーンのブックマークにも対応しています。

動画タイルモード(シーン探し)

ソースコードは MIT ライセンスで公開しています。「コードを読まずに作った」と言いつつ全部見られるのも、なんだか妙な気分ですが……(笑)。

配布はすべて無料です。

ありがたいことに、リリース当初には窓の杜さんの新着ソフト紹介でも取り上げていただきました(記事)。記事に私のサムネイルを使ってもらえたのが、地味にうれしかったです。

未経験から2か月やってみて思ったこと

Windowsもネイティブ開発もRustも未経験のまま始めて、約2か月。気づけば20万行を超えるアプリが手元にあって、しかもそのコードを私はほとんど読んでいません。少し前なら考えられなかったことだと思います。

やってみて一番強く感じたのは、「こんな機能あったらいいな」を思いついた瞬間に、それを実装に回せる楽しさです。

個人開発でいちばん腰が重いのは、たいてい「思いついても、実装する時間と気力がない」ところだと思います。でもAIに任せると、その実装コストがぐっと下がる。だから第2章で書いたように、画像 → PDF → AI画像整理 → 動画 と、欲しい機能がどんどん雪だるま式に増えていきました。 思いついて、頼んで、確認して、入る。このサイクルが速いと、開発がただただ楽しいのです。

実際、その雪だるまは今も転がり続けていて——最近は、AI画像を投稿する前の"加工"のワークフローを充実させたくて、こんな機能を作っています。

  • 手動のマスクや、AIによる被写体の切り抜きで領域を選び、その範囲に約100種類のフィルターを掛けられる機能
  • 漫画の吹き出しや、ゲームのメッセージウィンドウ、スタンプを画像に追加できる機能

たとえばフィルター100種類なんて、自分でコードを書いていたら絶対に手を出さない物量です。でもAIなら、「良さそうなフィルターを挙げて」とリストアップさせて、1個ずつ実装させていけば、案外なんとかなってしまう。実際この加工まわりだけで、コードはもう2万行を超えました。"物量で殴る"を個人で選べてしまうのが、AI開発のいちばん面白いところかもしれません。

AI画像を作って投稿するのも趣味のひとつなので、この一連の流れを、できるだけmIVの中で完結させたいと思っています。

もちろんいいことばかりではなくて、動作確認の地道さだけはAIに肩代わりしてもらえません。むしろ作る量が増えるほど、確認する量も増えます。それでも、「自分のかゆいところに、自分で手が届くツールを、専門外の領域でも作れてしまう」体験は、想像以上のものでした。

このあと別の記事で、AIに任せて作るときに詰まったり失敗したりしたのをどう乗り越えたか、そしてインストール不要の単体exeでどう配ったかも書いてみようと思います。

専門外でも、AIと一緒なら、実用品はちゃんと作れる。そんな手応えが得られた2か月でした。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?