Scott Hanselman

.NET everywhere apparently also means Windows 3.11 and DOS

January 17, 2020 Comment on this post [24] Posted in DotNetCore
Sponsored By

I often talk about how .NET Core is open source and runs "everywhere." MonoGame, Unity, Apple Watches, Raspberry Pi, and Microcontrollers (as well as a dozen Linuxes, Windows, etc) is a lot of places.

Michal Strehovský wants C# to run EVERYWHERE and I love him for it.

C# running on Windows 3.11

He recently got some C# code running in two "impossible" places that are now added to our definition of everywhere. While these are fun experiments (don't do this in production) it does underscore the flexibility of both Michals' technical abilities and the underlying platform.

Running C# on Windows 3.11

In this 7 tweet thread Michael talks about how he got C# running in Windows 3.11. The app is very simple, just calling MessageBoxA which has been in Windows since Day 1. He's using DllImport/PInvoke to call MessageBox and receive its result.

I'm showing this Windows 3.11 app first because it's cool, but he started where his DOS experiment left off. He's compiling C# native code, and once that's done you can break all kinds of rules.

In this example he's running Win16...not Win32. However (I was alive and coding and used this on a project!) in 1992 there was a bridge technology called Win32s that was a subset of APIs that were in Windows NT and were backported to Windows 3.11 in the form of Win32s. Given some limitations, you could write 32 bit code and thunk from Win16 to Win32.

Michal learned that the object files that CoreTR's AOT (ahead of time) compiler in 2020 can be linked with the 1994 linker from Visual C++ 2.0. The result is native code that links up with Win32s that runs in 16-bit (ish) Windows 3.11. Magical. Kudos Michal.

Simple Hello World C# app

Running C# in 8kb on DOS

I've blogged about self-contained .NET Core 3.x executables before and I'm a huge fan. I got my app down to 28 megs. It's small by some measurements, given that it includes the .NET runtime and a lot of accoutrements. Certainly one shouldn't judge a VM/runtime by its hello world size, but Michal wanted to see how small he could go - with 8000 bytes as the goal!

He's using text-mode which I think is great. He also removes the need for the garbage collector by using a common technique - no allocations allowed. That means you can't use new anywhere. No reference types.

He uses things like "fixed char[]" fields to declare fixed arrays, remembering they must live on the stack and the stack is small.

Of course, when you dotnet publish something self-contained, you'll initially get a 65 meg ish EXE that includes the app, the runtime, and the standard libraries.

dotnet publish -r win-x64 -c Release

He can use ILLinker and PublishedTrimmed to use .NET Core 3.x's Tree Trimming, but that gets it down to 25 megs.

He tries using Mono and mkbundle and that gets him down to 18.2 megs but then he hits a bug. And he's still got a runtime.

So the only runtime that isn't a runtime is CoreRT which includes no virtual machine, just functions to support you.

dotnet publish -r win-x64 -c Release /p:Mode=CoreRT

And this gets him to 4.7 megs, but still too big. Some tweaks go to about 3 megs. He can pull out reflection entirely and get to 1.2 megs! It'll fit on a floppy now!

dotnet publish -r win-x64 -c Release /p:Mode=CoreRT-ReflectionFree

This one megabyte size seems to be a hardish limit with just the .NET SDK.

Here's where Michal goes off the rails. He makes a stub reimplementation of the  System base types! Then recompiles with some magic switches to get an IL only version of the EXE

csc.exe /debug /O /noconfig /nostdlib /runtimemetadataversion:v4.0.30319 MiniBCL.cs Game\FrameBuffer.cs Game\Random.cs Game\Game.cs Game\Snake.cs Pal\Thread.Windows.cs Pal\Environment.Windows.cs Pal\Console.Windows.cs /out:zerosnake.ilexe /langversion:latest /unsafe

Then he feeds that to CoreIT to get the native code

ilc.exe zerosnake.ilexe -o zerosnake.obj --systemmodule zerosnake --Os -g

yada yada yada and he's now here

"Now we have zerosnake.obj — a standard object file that is no different from object files produced by other native compilers such as C or C++. The last step is linking it."

A few more tweaks at he's at 27kb! He then pulls off a few linker switches to disable and strip various things - using the same techniques that native developers use and the result is 8176 bytes. Epic.

link.exe /debug:full /subsystem:console zerosnake.obj /entry:__managed__Main kernel32.lib ucrt.lib /merge:.modules=.rdata /merge:.pdata=.rdata /incremental:no /DYNAMICBASE:NO /filealign:16 /align:16

a

What's the coolest and craziest place you've ever run .NET code? Go follow Michal on Twitter and give him some applause.


Sponsor: Like C#? We do too! That’s why we've developed a fast, smart, cross-platform .NET IDE which gives you even more coding power. Clever code analysis, rich code completion, instant search and navigation, an advanced debugger... With JetBrains Rider, everything you need is at your fingertips. Code C# at the speed of thought on Linux, Mac, or Windows. Try JetBrains Rider today!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
January 21, 2020 11:21
Does ".NET everywhere" also mean Visual Studio Tools for Office (VSTO) add-ins ;)?
January 21, 2020 11:34
This is why I love .Net core!!
January 21, 2020 12:09
Around 8KB reminds me of the 90s demoscene
January 21, 2020 13:31
don't be too excited.

in Windows 3.11 and DOS only rudimentary program code could be run (for now) as the .NET runtime had been stripped down to almost zero.

Nevertheless Michal did a great job to show how much overhead is included in a .NET application out of the box and what could be achieved if knowledge and some time is investigated to tune the working set of the application.
Hopefully we'll see adding support to the .NET tool chain to perform those kind of .NET application tuning.

Merging Mono repo into .NET 5 runtime this weekend is also a good starting point as Mono does include several improved tools compared against existing .NET Framework / Core 3.1 - eg. linker etc and therefore will sooner or later be available for all .NET 5 platforms.

January 21, 2020 14:05
For anyone else getting failed by google: it's neither "CoreIT" or "CoreTR" - the spelling is "CoreRT".
January 21, 2020 18:46
Great article. Just one question: why do you still have a Google+ button in your profile card?
January 21, 2020 21:06
It's a nice experiment, but IMHO calling it ".NET Everywhere" is a little exagerating. What's running here is not .NET but some very specific kind of C# code snippet that was compiled and stripped down to native code which, as a result, has nothing to do with .NET anymore. A "real" .NET assembly wouldn't run on Windows 3.11 or DOS, because there is no CLR, GC etc. and it's not portable. Just a good old object file you could have compiled from a C/C++ source straight away.

I still like it. I mean, he could have written a tiny CLR that just maps the assembly into memory, looks for the CLR header, finds the entry point from metadata, and then starts interpreting the IL opcodes. I don't know how hard it is to implement P/Invoke this way (might be easier than a managed method call) and forget about the heap, but still. This would even run .NET on MIPS or Alpha Windows NT. :P
FS
January 21, 2020 22:00
Oh, now This is the kind of dedicated pointless engineering insanity I love.
January 22, 2020 13:48
I know that matsujirushi managed to get C# compiled for the Azure Sphere. Some cunning techniques there.

http://matsujirushi.hatenablog.jp/entry/2018/12/29/173521
January 22, 2020 20:06
I got .NET running on my turning machine. (It can do ANYTHING!)
January 22, 2020 20:08
"turing" (Can't edit typos. Grrr...)
January 22, 2020 22:14
I am not worthy.

I haven't done anything at this level since "Inter segment self-relative fixup error" back in the 80s

I bow down before him.
January 23, 2020 0:56
I didn't expect to see a .NET runtime capable of this. Pretty cool. Would be cool if a portable version of this runtime was maintained.

RELATED: I've been working on a project called CS2X that will be able to run at native C performance apps written in a C# subset on Win 3.11, DOS, etc, etc and support an agnostic rendering layer for new, old and embedded platforms. Native CPU/GPU performance on each target in short.

https://github.com/reignstudios/CS2X
https://github.com/reignstudios/Orbital-Framework
January 23, 2020 1:48
Oh, now This is the kind of dedicated pointless engineering insanity I love.
January 23, 2020 14:43
This is absolutely insane. Love it
January 24, 2020 4:37
self-employed
January 24, 2020 9:38
Once ported Mono to the now defunct BlackBerry 10 platform and wrote a small slideshow app in C# so I could use a BlackBerry (with HDMI out and Bluetooth remote) to talk about this very project at a BlackBerry conference.
Rob
January 24, 2020 15:33
This prompted me to resurrect an old project I started for my Lumia 920 (Windows 8.1) - a simple "short cut" app so I could have a static tile on the start screen, pointing at a URL, useful as I no longer have MSN money, and in February we're losing MSN news and weather.

VS 2017 & 2019 no longer support the Phone SDK.

So it actually .Net not quite everywhere...
January 25, 2020 19:52
I just read 1 of your old articles about software quality and nobody caring:
https://www.hanselman.com/blog/EverythingsBrokenAndNobodysUpset.aspx

I'm concerned about Visual Studio...been using it since 6.0. There have always been bugs, but VS 2017 was absolutely horrible; I've never seen a Microsoft program crash so often. 2019 has been better, however every point release update (16.4, 16.4.1, etc.) bring fixes, but introduce more bugs. It's 1 step forward and 2 steps back. It's hard to believe something could be worse than that, but there is: the way bug reports are handled on the developercommunity site; answers like: "we don't have time to look into this now, so we're closing it". I don't see why any genuine/serious report should be closed because of "low priority", either say "we're never doing this", or keep it open. Many of the canned responses by MS come off as rude. It seems like there are too many new features put into a minor point release, and they clearly are not tested enough.

You were ahead of your time on your blog post about this. You should share it with whoever is running VS. Most people I know miss how candid Brian Harry was sometimes when he blogged about the state of VS, and we wish someone at MS would just come out and state what's going on.
January 28, 2020 21:24
Where can I find updates for my Windows 3.11 Machine?
January 29, 2020 8:24
Wow! What an awesome article!!! I’ve learnt a lot of things, if anyone wants to learn more then please visit https://sisayed360.blogspot.com/
January 29, 2020 8:25
Wow! What an awesome article!!! I’ve learnt a lot of things, if anyone wants to learn more then please visit https://sisayed360.blogspot.com/
January 30, 2020 15:03
in Windows 3.11 and DOS only rudimentary program code could be run (for now) as the .NET runtime had been stripped down to almost zero. By the way thanks for the info, can I share it on Geek Squad tech Support site.
November 05, 2020 21:59
Cool! You have a great article. We'll visit your site often. Thanks! <a href="https://watersoftenersolutions.com/fleck-5600-sxt/">fleck 5600sxt</a>

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.