【ITニュース解説】Porting a .NET desktop app to macOS and Linux, and the bugs a VM didn't show me
2026年10月10日に「Dev.to」が公開したITニュース「Porting a .NET desktop app to macOS and Linux, and the bugs a VM didn't show me」について初心者にもわかりやすく解説しています。
ITニュース概要
.NETデスクトップアプリをWindowsからmacOS/Linuxへ移植する際、仮想環境では見つからない多くのOS固有バグに直面した。描画やUI、多言語対応、ファイル操作、コード署名など多岐にわたり、それらの原因を特定し解決した。クロスプラットフォーム開発の具体的な苦労と解決策を示している。
ITニュース解説
Fileeというファイル変換アプリは、元々Windows専用で開発されていたが、macOSとLinuxといった異なるOSでも動作するように移植する挑戦が行われた。このアプリは、C#というプログラミング言語と、.NETという開発基盤、そしてAvaloniaという、Windows、macOS、Linuxなどで動くユーザーインターフェース(UI)を作るための技術を使って作られている。Avaloniaがクロスプラットフォーム対応であるため、移植作業は簡単なパッケージング作業のように思われたが、実際にはOSごとの様々な違いに起因する多くの問題に直面することになった。
まず、開発者がMacの実機を持っていなかったため、macOSのテストには仮想マシン(VMware Fusionなどで動く仮想的なMac環境)と、GitHub Actionsという、プログラムのテストやビルドを自動で行うクラウドサービスが利用された。しかし、仮想マシン上ではアプリが正常に起動せず、「RenderTimerが起動できない」というエラーが発生した。これは、VMwareの仮想Macゲスト環境では3Dグラフィックアクセラレーションがデフォルトで有効になっておらず、Avaloniaが画面を描画するためのタイマーを動かせなかったためだ。仮想マシンの設定で「3Dグラフィックアクセラレーションを有効にする」ことでこの問題は解決した。これは、開発環境として仮想マシンを使う場合でも、実機に近い設定や性能が重要であることを示している。
次に、GitHub Actionsの環境でアプリがクラッシュする問題が発見された。これは、アプリが起動直後に「値がヌル(何もない状態)であってはいけない」というエラーで停止するものだった。原因は、macOSのタスクトレイ(画面右上などに表示される小さなアイコン群)に表示するアイコンに対して、その説明文である「ツールチップ」が設定される前に、アイコン自体がシステムに登録されていたことだった。Windowsではツールチップがヌルでも問題ないが、macOSのネイティブコード(OSが直接理解するプログラム部分)はヌルを受け付けないためクラッシュしたのだ。ツールチップをアイコン登録前に設定するように修正することでこの問題は解決した。このようなOSごとの挙動の違いは、クロスプラットフォーム開発において頻繁に遭遇する課題であり、綿密なテストの重要性を浮き彫りにした。
macOSでの言語設定も、Windowsとは異なる挙動を見せた。SSH経由で起動すると韓国語で表示され、Finder(macOSのファイル管理ソフト)から起動すると英語で表示されるという問題だ。これは、.NETがシステム言語を判別するために使う情報が、Finderからの起動時には不足していたためだった。macOSでは、ユーザーの言語設定をCoreFoundationというフレームワークから取得する必要があり、特別な関数を呼び出して対応した。さらに、アプリが対応する言語をmacOSのシステム設定ファイル(Info.plist)に記載することで、システムパネルなども正しい言語で表示されるようになった。
Fileeの主要な機能であるドラッグジェスチャーも、macOSで問題を引き起こした。このジェスチャーはファイルマネージャー上でしか動作しないように設計されているが、macOSではカーソルの下にあるウィンドウを問い合わせると、常に「Dock」(macOSの画面下部にあるアイコンバー)が返ってきた。これは、Dockが画面全体を覆う透明なウィンドウを常に持っているためで、この透明なDockウィンドウがジェスチャーを遮ってしまっていたのだ。解決策として、ウィンドウの「レイヤー」という概念を導入し、Dockより上位のレイヤーにあるウィンドウを無視するように変更された。
Finderの右クリックメニューに「Fileeで変換」という項目を追加する際にも課題があった。当初はAutomatorというmacOSの自動化ツールとシェルスクリプト(コマンドを実行するプログラム)を使って実現しようとしたが、これがうまく動作しなかった。最終的には、Fileeアプリ自身がこのサービスを提供するように変更された。具体的には、アプリのInfo.plistファイルにサービスを宣言し、アプリの起動時にC#コードからObjective-Cのランタイム機能(macOSのネイティブ機能を利用するための技術)を使って、ファイル変換の要求に応答するメソッドを登録した。これにより、Finderから選択されたファイルが直接実行中のFileeアプリに渡されるようになり、無駄なプロセス起動が不要になった。
アプリを配布するためには、macOSのセキュリティ要件として「署名」が必要となる。codesignという署名ツールを使ってアプリを署名しようとしたところ、.NETアプリの構造(実行ファイルと、その隣に配置されるDLLファイル群)が原因でエラーが発生した。codesignは、DLLファイルもコードの一部とみなし、それぞれが署名されていないと判断したのだ。この問題は、まず個々のDLLファイルを署名し、次に実行ファイル、そして最後にアプリバンドル全体を署名するという手順を踏むことで解決した。また、改行コードの違い(WindowsのCRLFとmacOS/LinuxのLF)が原因で設定ファイルが読み込めないという、細かな環境設定の問題も発生した。
ファイル監視機能にもOS間の違いが影響した。Fileeは特定のフォルダ内のファイルを監視し、ファイルが完全に書き込まれた後に変換処理を行う。Windowsでは、書き込み中のファイルは排他的に開けないため、書き込み完了を簡単に判断できる。しかし、macOSとLinuxではそのような排ファイルロックの仕組みがないため、書き込み途中のファイルを変換してしまう可能性があった。これに対し、Fileeはファイルが一定期間変更されなくなるまで待つ時間を長くしたり、macOS特有の、Finderがファイルをコピー中に割り当てる特殊な作成日時(1984年1月24日)をチェックすることで、書き込み中のファイルを判別する工夫が施された。
Linuxへの移植は比較的スムーズだったものの、ドラッグジェスチャーはX11という古いGUIシステムに依存していることが判明した。現在主流になりつつあるWaylandという新しいGUIシステムでは、セキュリティ上の理由から、あるアプリが別のアプリの入力を直接監視することができないため、ドラッグジェスチャーは利用できなかった。この問題に対する根本的な解決策はまだ見つかっておらず、Wayland環境では代替手段として、ファイルをドロップする専用ウィンドウや右クリックメニューの利用が推奨されている。
このように、Fileeのクロスプラットフォーム移植は、単なるコードの移動ではなく、OSごとの設計思想、API(アプリケーションプログラミングインターフェース)の挙動、ファイルシステムの特性、セキュリティモデルの違いなど、多岐にわたる課題への対応が求められる複雑な作業だった。仮想環境でのテストだけでは見つからない実機固有の問題も多く、実際に各OSで動作させることの重要性が改めて示された。macOSとLinux版はまだプレビュー段階であり、さらなるフィードバックを通じて改善が進められている。