世界を動かす技術を、日本語で。

Windowsネイティブアプリ開発は混乱しています

概要

  • 著者は 長年Windowsユーザー として開発経験を重ねてきた経緯を持つ
  • Display Blackout というユーティリティを開発し、現代Windowsアプリ開発の難しさを体感
  • UIフレームワークや配布方式の 複雑な進化 と混乱を指摘
  • P/Invoke など旧APIとの相互運用が依然として不可避である現状
  • Microsoftの開発エコシステムの課題 と、その影響について考察

Windowsアプリ開発の現状と混乱

  • 著者は 幼少期からWindowsとVisual C++ に親しみ、.NETの登場も体験
  • 初就職先も.NET系 だったが、プロとしてネイティブWindowsアプリは未経験
  • 趣味では Web開発を選択 しがちで、Windowsアプリは思い出補正が大きい
  • Display Blackout はゲーム時に左右モニターを黒くするユーティリティ
    • 既存のAutoHotkeyやMicrosoft Storeアプリの類似機能も存在
    • 著者は よりモダンなUIと学習目的 で独自実装を選択
  • 必要要件:
    • ディスプレイ情報の取得
    • 枠無し・非アクティブ黒ウィンドウの配置
    • グローバルショートカットの取得
    • スタートアップ起動オプション
    • 永続設定の保存
    • トレイアイコンとメニュー表示

Windows UIフレームワークの歴史

  • Win32 API(C言語) が出発点で、今も現役
  • MFC(C++) によるオブジェクト指向ラッパーの登場
  • .NETとC# による新時代の幕開け
    • Windows Forms :Win32のラッパー
    • WPF (.NET 3.0):XAMLによる宣言的UI、GPU描画
  • Windows 8/10のWinRT・UWP
    • サンドボックス型・クロスデバイス対応
    • 一部OS機能はUWP/WinRT専用
    • Windows App SDK/WinUI 3 で再統合を目指すも混乱継続
  • UI進化の流れ:
    • Win32 → MFC → WinForms → WPF → WinRT XAML → UWP XAML → WinUI 3

開発手法の選択肢と課題

  • WinUI 3 + Windows App SDK が推奨だが、実装方法が三択
    • C++ :軽量だがメモリ安全性に課題
    • C#/XAML(フレームワーク依存) :.NETの最新バージョンがOSに標準搭載されておらず、配布時にユーザーへ追加インストールを強要
    • .NET AOT :ランタイムごとバイナリ化、9MiB超の肥大化
  • Rust対応 も頓挫
  • 配布形式 も混乱
    • MSIX 推奨だが、コード署名証明書が高額($200~$300/年)
    • 未署名配布 はユーザー体験が劣悪
    • Microsoft Store も独自価値がないとリジェクト
  • .NETの配布やMSIXの依存関係管理 が十分に整備されていない

旧APIとの相互運用とその限界

  • 新APIだけでは機能が不足
    • 例:ディスプレイ監視やグローバルホットキー取得はP/Invoke必須
    • トレイアイコンやコンテキストメニュー も標準化されていない
  • UI自動リサイズ機能 などもフレームワーク進化の過程で消失
  • P/Invoke技術自体も過渡期
    • CsWin32 など新しいラッパーツールも未成熟
    • C#言語仕様の限界も露呈
  • 最新フレームワーク採用の意義が薄れる現状

Microsoft開発エコシステムの課題

  • 頻繁なフレームワーク刷新 による技術的負債の蓄積
  • サンドボックス化や機能制限 による開発者体験の悪化
  • C#とWindows APIの親和性不足
  • Electron等Web技術への流出 の背景
  • 根本的な改善策の不足 と「中途半端」な現状

このように、著者は モダンWindowsアプリ開発の現実 を実体験を通じて詳細に分析し、 Microsoftのエコシステム全体の課題 を浮き彫りにしています。

Hackerたちの意見

俺が見る限り、ほとんどの開発者もそうだと思う。Hacker Newsのコメント欄では、ネイティブアプリの死を嘆く声が多いけど、Windowsアプリプラットフォームの混乱を考えると、俺はウェブスタックを選ぶよ。ElectronやTauriを使って、OS統合のために必要なWin32 APIに繋げるしね。ユーザーとしては、パフォーマンスの面でネイティブアプリが好きだけど、この記事が示すように、ネイティブアプリの開発は明らかに混乱してる。でも、ユーザーとしてはその問題は感じないかな。ただ、アプリがどんどんひどくなってるのは感じる。新しいOutlookやTeamsの完全な混乱っぷりとかね。

マイクロソフト自身がElectronを使ってアプリを開発してるのに、他の開発者に何を期待できるんだろう?

Electronの話だけど、自分の小さなツールにはTypeScript+BunとElectrobunを使ってるよ。https://github.com/blackboardsh/electrobun

新しいOutlookとTeamsが混乱してるのは皮肉だね。だって、あれはネイティブアプリじゃなくてウェブアプリだから。ウェブアプリは、ちゃんとしたネイティブの見た目や感触をエミュレートするのが難しくて、フォーカスやキーストロークナビゲーションに関して変な問題がよく起こるんだ。Javaアプリのダメな問題はそのままで、Javaじゃない以外の改善もなく、しかも遅くてメモリを多く使うしね!

俺は組み込みプログラマーで、たまに組み込みデバイスとインターフェースするためにWindowsプログラムを書く必要があるんだけど、純粋なWin32とC++でネイティブGUIプログラムを書くのは楽勝だよ。最近、XP時代に最後に更新された古いプログラムに新機能を追加しなきゃいけなかったんだけど、2つのポイントがある。1. プログラムはVista、7、10、11で動くように更新する必要がなかった。ずっと動き続けてたんだ。2. プロジェクトをVisual Studio 2022に読み込んだら、VC6から変換されて問題なくコンパイルできたし、新機能を追加して新しい.exeを顧客に送ったら、そのまま動いた。他のプラットフォームにこんな互換性の成功ストーリーがある?

Winforms?まだ彼らがベストオプションだなんて笑っちゃう。置き換えようとする努力が無駄すぎる。

大きなレガシーWin32/C++コードベースでの一番の課題は、32ビットから64ビットに完全に移行することだね。複雑なGUIコントロールや構造体に関するノウハウやドキュメントが失われてるか、ほんとにバラバラになってる。それ以外は、そこを乗り越えれば、ほんとに全部うまくいくよ。

どう見えるの?ウィジェットはどんな感じ?

俺にとって、この「何も変える必要がない」っていうのは安定性を示唆してるけど、週ごとに何かが変わるのに慣れてる若い開発者たちがいて、1週間以上古いものは「メンテされてない」って思ってバグだらけだと考えてるんだよね。

俺は、世界で唯一「エレガント」や「よく構造化された」Cocoaコードベースに触れるくらいなら、醜いWin32のジャンクを書く方がいいと思ってる気がする。Win32ではボタンが欲しいときは関数を呼んでハンドルを渡すだけだけど、Appleの世界ではまず7つのインターフェースをサブクラス化して、読みづらいSmalltalk風の構文で書かなきゃいけなくて、ドキュメントにアクセスしないことを祈るしかない。そしてもちろん、彼らは常に何かを変えるから、後方互換性を壊すのがAppleの趣味なんだよね。

APIによって文字列を定義する方法が12通りもあるんだって。

それ、あんまりうまくいかないこともあるよね。運が良かったんじゃない?高DPIを加えると、すぐに厳しくなるし。あと、共通コントロールには変な入力の問題があるよ(Editコントロールでctrl+backspaceを試してみて)。2026年に何かをちゃんと動かすためには、そういう小さな問題を丁寧に修正する必要があるよ。

でも、この記事が言いたいのはそこなんだよ。Cレベルでは完全に機能するシステムがあるけど、その上のレベル(C++レベルでも)では、機能サポートがめちゃくちゃなんだ。

「狂気は個人には稀だが、集団、政党、民族、時代にはルールである」 —F. ニーチェ(皮肉を込めて)

1999年からMFC Win32プログラミングはやってないけど、確かあのプログラムはmain()関数を実行しないんだよね。アプリ用のWin32クラスをインスタンス化する感じだったと思う。もう詳細は全然覚えてないな。

著者はいくつか良いポイントを挙げてるね。最新の.NETランタイムがWindows Updateを通じてWindows 11デバイスに引き下ろされないのはなんで?デプロイメントのためのもっと良い道がないのはなんで?これは、彼らが製品全体で良いユーザー体験を提供する試みを完全に放棄している一例だね。

最新の.NETを統合しない理由はいくつかあると思う。まず、.NET 5でセキュリティモデルが変わったこと。次に、Monoや.NET Coreを言語の基盤に組み込んだことで、Windowsネイティブ開発、特にWin32 APIに関するサポートができなくなったこと。 .NET 10を見て.NET 5と比較すると、Win32 APIを再統合しようとしているのがわかるけど、今は新しいMicrosoftの名前空間に入ってる。変更の量が大きすぎて、元の.NETフレームワークの代替品として使うのは無理があるかも。サイドバイサイドのインストールもできたかもしれないけど、.NETフレームワークの急速な開発があって、OSのアップデートに結びつけるのが難しかったんじゃないかな。年に一回か二年に一回のアップデートサイクルから解放して、毎回ダウンロードしてインストールすることで開発を迅速に進めたかったんだと思う。

Windows Updateは昔のやり方で、ひどいよね。アップデートで古いアプリが壊れるか、すべてのバージョンをダウンロードすることになる(現実的じゃない)。新しいアプリをインストールするためにWindows Updateを使いたい人なんている?ただの悪いアイデアだよ。今はDLLをパッケージにして、そのまま動くからね。

.NETには2つのバージョンがある。一つは「レガシー」で、安定していてOSにバンドルされてる。もう一つは「Core」で、サポートは3年だけど100%互換じゃない。最新の.NETランタイムがバンドルされてない理由は、上記の通り、安定版がバンドルされてるからなんだ。

推測だけど、バージョンは完全に後方互換性がないから、最新バージョンだけを出すわけにはいかないんだ。全てを出さないといけない。Windowsに出荷されるバージョンの後に、ほぼ10個の.NETバージョンがリリースされてるし、新しいバージョンは毎年出てる。著者は、.NETにはユーザーにランタイムをインストールさせない配布オプションがあるとも言ってる。ビルドにフルランタイムをパッケージすることもできて、ファイルの束として、自己解凍実行ファイルとして、またはスタンドアロンのAOTコンパイルされたネイティブ実行ファイルとして提供できるんだ。著者はAOTコンパイルされた実行ファイルが9MiBで、それは受け入れられないと言ってた。他のオプションはもっと大きくなるだろうね。個人的には、9MiBは大したことないと思う。特に著者がElectronを選ぶ方がいいって言ってるのに、それは最悪でも大きい(バンドルされたChromium)し、最高でも非効率的(システムWebView)だからね。

Windowsの開発に触れたのは久しぶりだな。もしもう一度やるなら、できるだけWindows UIにはReact Nativeを使って、ユーザー空間のコードには低レベルのWin32-React Nativeモジュールブリッジを使うかな。最後にWindowsの開発をやったのは約15年前で、WTLってライブラリを使ってた(ここでも何件かコメントに出てると思う)。Windows 8-10が推してた新しいものは、後方互換性が必要だったから使えなかったんだ。MFCよりはずっと軽い感じだったけど、ATLや生のWin32 APIを使うほど面倒でもなかった。皮肉なことに、MacでWin32アプリを開発して、Railsアプリへのクラウドブリッジを作ってたんだ(QuickbooksのCOM APIに話しかけるのが地獄だった、XMLとXML定義で)。Mac上のVMwareを使ってQuickbooks Windowsに接続してた。Win32の開発にイライラしてたから、Win32アプリのUIを作るのにChrome Embedded Frameworkライブラリを使って、WTLに苦しむことなくブラウザベースのビューでUIを作ったんだ。C/C++の開発を.NETコードに切り替えたくなる誘惑はあったけど、ユーザーに大きな.NETランタイムをダウンロードさせるのは避けたかったんだ。これがLevion、Quickbooks WindowsからCloud Railsアプリを作ってたときの話だよ…。

ちょっと言わせてもらうと、C++(または他の「オブジェクト指向」言語)を使っていて、自分でMFCみたいなラッパーを書く覚悟があるなら、普通のWin32 APIも全然アリだよ。これ、Windows GUIの仕事を始めたばかりの人には向いてないけど、少し経験があれば、2〜3週間の基礎作りでUIの細かいところまで完全にコントロールできるようになるから、好きなように拡張したり修正したりできるよ。マイクロソフトが得意なのは、深い後方互換性を確保することだから、Win32 APIに基づいているものは安定してる。今動いてるなら、後でも動くよ。10年以上の開発の更新の例がここにあるよ - https://bvckup2.com/wip

スクリーンショットを見る限り、Windows 11スタイルのUIは作れないよね?つまり、https://ntdotdev.wordpress.com/2023/01/01/state-of-the-windo...で探求されている問題に寄与してるってことだよね。

なんでC++ BuilderやDelphiを使わないの?

あんまり言いたくないけど、MFCスタイルのラッパーに対してまともなAPIを考えられるなら、AIが decentな実装を書いてくれるはずだよ。

このルートを進むのが難しいのは、ダークモードのサポートだね。Win32のUSERや共通コントロールはダークモードをサポートしてないし、システム内のハードコーディングされた明るい色や背景のせいで、むしろ敵対的なんだ。システムの色はダーク/ライトの設定に関係なく明るいし、ハイライトはハードコーディングされたライトブルーだし、無効なコントロールはハードコーディングされた色を使ってる。コントロールの色を変更するためのウィンドウメッセージの半分は、テーマが有効になってると無視されちゃう。メニューはオーナードローが必要だから、扱うのが特に難しい。さらに、ダークモードをサポートしているシステムUIが少しだけあって、それが原因でプログラムがダークモードと不一致に見えちゃう。メッセージボックスやファイルダイアログはダークモードに切り替わるけど、Vistaスタイルのカスタマイズを使ってると、システムリンクがダークモードの背景に対して読みづらい青色になっちゃうのが問題だね。

著者の言う通り、本当にめちゃくちゃだよ。スケールでいくつかのWindowsアプリを作って出荷した経験から学んだ教訓は基本的にこうだよ:(1) Win32を学んで、可能ならその古いAPIを使え。めちゃくちゃ安定してるし、結局必要になることが多いから。そんなに怖くないよ。(2) マイクロソフトが所有しているUIツールキットは使わない方がいい、絶対に痛い目に遭うから。何でもいいから他のを選んだ方がマシ。理想は、Win32の調整を上に重ねるのを妨げないツールキットを選ぶこと。そうしないと、ツールキットの開発者が考えてなかったケースにぶつかって、修正できなくなるから。最終的にはカスタムのWindowProcが必要になるよ。Win32のウィンドウライフサイクルやハンドルにアクセスできる必要があるんだ。

"(2) マイクロソフトが所有しているUIツールキットは使わない方がいい、絶対に痛い目に遭う" これは過去約20年間に彼らが作った技術全てに100%当てはまるけど、WPFとWinformsは非常に安定していて、実際の問題はないよ。過去20年間のほとんどのことは、基本的にWPFの不完全なリミックスだったから、すごく変だよね。もし彼らがWPFを使い続けて、それを拡張していれば、C#に相当するUIツールキットとして、今のWindows開発の金標準になってたし、オープンソースや標準化してれば、一般的なUI開発にもなってたかもしれない。

でも、2026年にC++のようなメモリ安全でない言語でグリーンフィールドアプリケーションを書くのは犯罪だ。私は反対だな。GUIレイヤーは安全性が重要なコンポーネントからは遠いし、C++はGUIやビデオゲーム、産業アプリケーションなど、あらゆるものに対して実績のある選択肢だよ。C++が飛行機や原子炉を制御するのに十分安全なら、GUIのような些細なものにも十分安全だよ。この記事はQtのようなフレームワークについても言及してないけど、2026年にGUIアプリを書く最良の方法だと思う。Qtはネイティブ(C++)で、組み込みのメモリ安全機能があって(でもGCはない)、クロスプラットフォームだよ。

でも、快適だし、管理された実行環境と比べると実際には面倒くさいよね :-) もちろん、組み込みシステムは別の話だけど…。

でも、Qtをネイティブアプリ開発と考えることはできないよ。だって、どのアプリもQtランタイムが必要だから。ネイティブっていうのは、システムライブラリだけを使うことだよね。

なんで記事がQtに言及してるの?QtはLinuxディストリビューションの一部ではネイティブだけど、Windowsではないよ。

もう一度言うけど、WinRT、UAP、UWPに対して書かれた既存のWindows 8/10アプリがない限り、WinUI 3.0やWinAppSDKに関することには触れない方がいいよ。マーケティングからも離れた方がいい。例外として、COMではなくWinRTに依存するWin32のいくつかのAPI、例えば新しいMIDI 2.0やWindows MLがあるけど。Microsoftのツールだけを使うなら、Win32、MFC(C++でWinUI 3.0より状態がいいよ)、WinForms、WPFを使い続けて。そうじゃないなら、Qt、VCL、Firemonkey、Avalonia、Uno、ImGUI…を使えばいい。BUILD 2024では、WinUI 3.0がひどかったからWPFの状況を見直すことを余儀なくされたし、今も改善されてるかは分からないけど、オープンソース化のプロセスにあるみたい。$4兆の価値がある会社が直せない混乱をコミュニティが引き継げるかどうか見てみるって感じだね。本当に、MicrosoftのWindowsチームの社員じゃない限り、WinUIからは離れた方がいいよ。[0] - 違いについて何度でも説明できるけど、気が向いたらね。

WinJSにちょっと言及したいな。Windows 8のアプリを作って、短期間だけWindows Storeに公開したことがあるんだ。その後、WinJSのUI部分がオープンソース化されて、公式にWindowsアプリを作るための方法じゃなくて、ただのウェブフレームワークになったと思う。これが私のWindows Storeへの挑戦の終わりだったよ。 https://github.com/winjs/winjs

そういう視点で見ると、金曜日のブログ投稿[0]が「コアWindows体験をWinUI3フレームワークに移行する」と発表したのは、そういった体験の質を向上させるための措置として問題だよね。[0] https://blogs.windows.com/windows-insider/2026/03/20/our-com...

Flutterアプリをexeインストーラーでリリースしようとしたら、Googleドライブがウイルスだって言ってきたけど、Windows 10/11では普通にインストールできたよ。今はmsixの同じことをやってるんだけど、証明書を探したら年200ドル近くするものしか見つからなくて、FIPS要件のせいで最新の100ドルのYubiKeyに入れないといけないんだ。CAがもはやプライベート/パブリックキーのファイルを簡単に取得させてくれないなんて知らなかったよ。唯一の配布方法はハードウェアベースのFIPSキーだし、アマチュア無線のために作ったオープンソースプロジェクト1つだけだから、コード署名は完全に諦めた。

*20年以上前からそうだよね。GnomeのエディションはGnome Builderを搭載してるし、Flatpakが配布問題を解決してくれた。今のLinuxは、Windowsを使ってたほとんどの人が覚えてるよりもずっと良くなってるよ。