やじうまの杜
.NET Core/C#なら“どこでも”動く ~そう、「Windows 3.11」でもね!
Microsoftのなかの人が“Twitter”でやり方を解説
2020年1月22日 14:28
“やじうまの杜”では、ニュース・レビューにこだわらない幅広い話題をお伝えします。
「.NET Core」はオープンソースのソフトウェアフレームワークで、従来の「.NET Framework」との違いはマルチプラットフォームに対応する点です。Windowsだけでなく、MacやLinuxはもちろん、ゲームエンジン「Unity」でも使われていますし、今人気のIoTボード“Raspberry Pi”でも動作します。つまり、“どこでも”動くわけです。――でも、ほんとうに“どこでも”動くのでしょうか? たとえば、「Windows 3.11」でも?
それに本当に挑戦してしまったのが、Microsoftで.NETランタイムの開発に携わっているMichal Strehovský氏。“Twitter”でその手の内を明かしてくれました。
1/7 Did you ever need to run a piece of C# code on Windows 3.11? Me neither, but I did it anyway. A thread.pic.twitter.com/ZW0p29d9Ef
— Michal Strehovský (@MStrehovsky)2020年1月9日
今回、「Windows 3.11」で動かしたのは以下のC#コード。ダイアログを表示するAPIで簡単なメッセージを表示するプログラムです。C#プログラマーにはおなじみのDllImport/PInvokeを用い、“MessageBoxA()”関数を呼び出しています。
2/7 The key ingredient it setting your expectations low. This is the program I got running:pic.twitter.com/VLU8McM3Y3
— Michal Strehovský (@MStrehovsky)2020年1月9日
さて、C#のコードを古いOSで動作させる際、まず課題となるのが依存関係です。一般的なC#コードは、多くのライブラリに依存しています。また、「.NET」はさまざまなプログラミング言語やプラットフォームで動作するよう、中間コードを生成して仮想マシンで実行する仕組みになっています。こうしたものを排除し、単体でネイティブ動作する実行ファイルを作成しなければならないでしょう。
これには、氏が以前に取り組んだ“C#で8KB以下のゲームを開発する”という取り組みが役に立ったそうです。ソースコードから参照を排除する(スタックを利用する)、リンカーをいじって利用していないアセンブリを徹底的に削除する、JITではなく事前コンパイルされた「CoreRT」を使う、リフレクションを無効化する、ランタイムライブラリへの依存関係をすべて回避するためにWindows APIを再実装するといった9ステップもの最適化���施して、65MBのゲームをわずか8KBにまで削減しています。この方法を応用すれば、自己完結型の(とってもサイズの小さな)C#バイナリが作れるというわけ。
For my next trick, I'm going to build a 65 MB self-contained game in C# and shrink it to 8 kB (still 100% self-contained):https://t.co/ykhOjSoa3upic.twitter.com/tblEAM3HsO
— Michal Strehovský (@MStrehovsky)2020年1月3日
ここまででも十分わけのわからないことになっていますが、これでもまだ「Windows 3.11」では動作しません。なぜならばバイナリは32bitであるにもかかわらず、OSは16bitだからです。
これを解決するために持ち出されたのが、「Win32s」と呼ばれるランタイム環境。1992年10月にリリースされたというこの骨董品は、サンク(thunk)と呼ばれる仕組みで32bitコードを16bitコードに変換し、実行することができます。つまり、当時課題であった16bitと32bitの橋渡しをするための技術ですが……まさか28年も経ってここで役に立つとは! もはや「.NET Core」とは呼べないかもしれませんが、ちゃんとC#コードが動いています。
現在のC#コードが30年近く前の環境でも動作させられるのもすごいですが、Windowsという仕組みが断絶せず、今まで連綿と続いていることにも改めて驚かされます。