Using Mono to avoid depending on the .NET Framework on Windows

At GDC I remember seeing Brendon Chung’s talk about Atom Zombie Smasher, where he mentioned how it runs on Mono, even on Windows. Besides making the game’s behavior more consistent between Windows, Linux and Mac, it also allowed him to remove the need to install the .NET framework before running the game, since he can just bundle Mono with his game.

This is attractive to me for a few reasons:

  • It means all Windows players could play my game just by unzipping it — no separate step to install the .NET framework, no need for an installer to do it for them
  • It means I don’t have to target .NET 2.0 to try to keep Vista/Win7 users from needing to install the .NET framework — I can use nice 4.0 features
  • If I can get my game running on Mono, I can probably run on Linux and Mac as well

So I started looking into Mono. It turns out they have a utility called mkbundle that bundles your app, and all of the assemblies it depends upon, into a single executable, and the only thing it needs to run is a mono DLL you can distribute along with it. The .NET assemblies provided by Mono are MIT/X11 licensed, but the runtime is LGPL, so while you can embed Mono’s System.dll into your executable, you have to either dynamically link to the runtime DLL or otherwise provide your program to users in such a way that they could use a different Mono runtime if they wanted.

Unfortunately mkbundle is kind of broken on Windows right now, a lot of which is due to Cygwin drift since the tool was written. Luckily, it’s not so broken that you can’t fix it yourself. I mostly followed this StackOverflow answer — but there are a few changes I made to make it a lot simpler!

The procedure

  1. First, make sure your app works under Mono on Windows. If it doesn’t, none of this will be any use to you.
  2. Install Mono for Windows from here. Click the Windows button in the top row (the stable release) and choose “Mono for Windows, Gtk#, and XSP.” I used version 2.10.8, I can’t guarantee that anything else will work exactly as described. You can install to the normal directory.
  3. Install Cygwin (UNIX-like environment for Windows.) Make sure you have selected the packages gcc-mingw, mingw-zlib1, mingw-zlib-devel, and pkg-config.
  4. Launch a Cygwin Terminal using the shortcut created by the Cygwin installer.
  5. Navigate to the directory your game assemblies are (e.g. your bin/Release folder.) Note that in cygwin, a path like C:\some\path is expressed as /cygdrive/c/some/path — note the slash direction and the way drive letters work! Backslashes are escape characters in bash so if you just type \cygdrive\c\Foo it thinks you meant cygdrivecFoo.
  6. Create an environment variable $MONO pointing to the Mono installation. On 64-bit Windows use this:

    On 32-bit:


    The point here is to use the DOS short filename compatibility feature to point to the right Program Files (or Program Files (x86)) directory without creating a path with spaces in it, which mkbundle doesn’t like.

  7. Add Mono’s bin directory to your $PATH so you can run mkbundle:
    export PATH=$PATH:$MONO/bin
  8. Add Mono’s lib/pkgconfig directory to your $PKG_CONFIG_PATH environment variable. Usually this starts out empty so just do:
    export PKG_CONFIG_PATH=$MONO/lib/pkgconfig
  9. Now here is the magic part that makes this far less laborious — specify a value for $CC that invokes the mingw32 version of GCC:
    export CC="i686-pc-mingw32-gcc -U _WIN32"

    See, Cygwin used to bundle a version of GCC that supported the -mno-cygwin flag to produce executables that didn’t need cygwin1.dll. The GCC 4.x it includes does not support it any longer. That is why you have to install the gcc-mingw package. Since some people may also have normal gcc installed in Cygwin, I want to unambiguously refer to the mingw32 version of GCC.

    The -U _WIN32 basically removes the predefined _WIN32 preprocessor definition before GCC compiles the C code that mkbundle generates. The Win32 code path in that generated code is broken right now (it uses an undefined function g_utf16_to_utf8 that looks like it comes from glib.) Luckily it works if you turn off that code path — the only thing you will miss is supporting Unicode command line arguments. If this is a problem you can use the more complicated procedure from this StackOverflow post to intercept the C code before it is compiled & fix it yourself. That post doesn’t describe how to fix the UTF thing, but it lets you edit the generated C code before recompiling it.

  10. Now you can finally run mkbundle. Replace the assembly names as necessary:
    mkbundle YourGame.exe OpenTK.dll --deps -o Output.exe
  11. Copy mono-2.0.dll into the current directory since the resulting EXE you just created depends on it:
    cp $MONO/bin/mono-2.0.dll .
  12. Now hopefully your app will run!

    You just have to distribute the output EXE file alongside mono-2.0.dll and it should just magically work on any Windows PC.

Making a reusable script

I have combined all of these steps into a shell script here: Once you have this as a script, you can recreate the bundle in one step whenever you want. Note that you will have to customize the script a little for it to work for your exact case.

How big is it?

You may be wondering how big your Output.exe and mono-2.0.dll are, seeing as it’s pulling in a Mono runtime and a bunch of System assemblies and so on.

Well, in my test app, which just uses OpenTK to do some OpenGL drawing, the file sizes are as follows:

Output EXE: 13.4 MB
mono-2.0.dll: 2.6 MB
Both of the above, zipped at highest compression: 5.6 MB

5.6 MB is not bad at all compared to the size of textures and music you are likely to include in a game!

Making it even smaller

If you want the EXE file to be smaller even after it is unzipped, you can pass the -z flag to mkbundle and it will compress the embedded assemblies. (That is what the mingw-zlib* Cygwin packages are for.) This will make the EXE file smaller, although it may make startup times a little slower. That took my example EXE from 13.4 MB to 4.6 MB. Of course, now it won’t compress as much when you zip it.

There are also “linker” tools, like monolinker, that can strip assemblies so only referenced code is used. For example, if I only used OpenGL and not the OpenAL functionality of OpenTK, it could theoretically remove the OpenAL stuff from the OpenTK assembly automatically. In practice, it’s not so simple. I tried using monolinker and could not get it to work — it just made an OpenTK assembly that crashed when you tried to use it. I suspect monolinker got a little overzealous and removed something that was necessary. If I could get this to work it would reduce the executable size even further.

But, honestly, I think the executable is small enough already. Any further compression would be nice, but is not necessary.

Extra DLLs required sometimes?

The TODO file in the mkbundle source suggests that these DLLs may also need to be distributed with your program:

  • libglib*.dll
  • libgmodule*.dll
  • libgthread*.dll
  • iconv.dll
  • intl.dll
  • zlib1.dll

My program does not seem to require them, but if you use more framework functionality they may be required. I can see Atom Zombie Smasher includes some of them (specifically libglib and libgthread.) These libraries appear to all be either LGPL, X11 or zlib licensed. You may want to double check the exact terms of the licenses yourself, but it seems like it will just be fine to copy them out of the Mono bin directory and include them with your app.

Next steps

The mkbundle tool works out of the box on Ubuntu for me, and I am going to investigate the options for making a native-looking OS X application eventually as well.

If I have time I will try to submit a patch to Mono so mkbundle “just works” on Windows (provided a Cygwin install.)

Concept art, etc.

The forest at night.

A distinguished gentleiguana.

Some other progress: a friend of mine is willing to do music for Platformer. I also implemented support for background music in the game via XACT, and I’m going to add support for normal sound FX as well soon.

Instantly distracted

So I haven’t worked on Platformer (directly) in a couple days. Spent the past several days working on a C# networking library (something like the basics of Twisted for handling TCP and UDP sockets in an event-based way, so I don’t have to think about threading.)  The plan is ultimately to make a fancy-pants networked debug console for Platformer or other future games.

It’s a somewhat vague idea, but the general concept is that the game could expose a tree of objects with associated properties and commands. The console would be a separate process that connects and lets you browse the tree, view and edit properties, execute commands, and present the game’s log. This would be a more comprehensive version of the debug menu that’s present in Platformer right now, and would allow me to have a richer UI like sliders, color pickers, etc.

Wow, that sounds completely vague and tangential. Way to get distracted, self.

Actually coding a little

Been doing some minor tweaks to get back into Platformer:

  • Coded the game over screen using the image from the previous post, including a nice rotozoom/mode 7 looking effect.
  • Added autorepeat support to the input manager.
  • Ported some of the easing functions from TweenLite (which are ultimately based on Robert Penner’s Actionscript easing functions) to C#. Most of them aren’t very complicated, but they’re useful for quickly adding polish.

I don’t think I’ve mentioned it before, but something else I did a few months ago was write a simple 2D scene graph in Platformer. It’s only used in menu screens so far, but it’s very helpful to be able to arrange sprites & text labels as objects instead of having to draw them in immediate mode. You generally don’t even have to write a Render() function for a screen any more, just a constructor & an Update() function. It’s fairly minimalist, but I’ve ported most of the code that does drawing (except for the game itself, because that’s more complex and is more performance sensitive) over to the new scene graph system, and it seems to be a win as far as ease-of-use.

This was all inspired by some Actionscript 2D game libraries that are popular right now, like Flixel and Flashpunk.