In which I explain why GTK# applications built in Monodevelop on Linux don’t run on Windows, and explain how to fix this…
As I’ve mentioned elsewhere, I’ve been working a lot recently with C# via Monodevelop to create applications that will work cross-platform (specifically Linux, Windows and Mac).
As you probably know, C# and the other .NET languages don’t compile down to machine code but to CIL (Common Intermediate Language) bytecode which is then interpreted by a JIT (just-in-time) compiler when the application runs. This means I can compile a “hello world” console application on Linux, take the resulting hello.exe and run it directly on Windows.
That’s great for command-line applications, but what if you want to do the same with a GUI application?
Firstly, Monodevelop does support WinForms – but it lacks any kind of GUI designer for this. The built-in designer (called Stetic) outputs GTK#, which is a better option for cross-platform apps anyway – it just takes a bit of re-adjustment if you’re used to building WinForms apps (for example the control property equivalent to “Enabled” is called “Sensitive” – not immediately apparent!).
So if I build an equivalent “hello world” GTK# application consisting of, say, one window with one button that just closes the app, that resulting guihello.exe (or whatever I call it) should run on Windows, right?
Um, no, firstly the obvious point – if you want to run GTK apps on Windows you need the GTK runtimes. Install those on Windows and you should be good to go, right? No, there’s one other gotcha. (Before that – a quick aside – if you’ve experimented with mkbundle to create static EXEs which package up all dependencies [to avoid needing runtimes], note that it doesn’t cover GTK# – you’ll still need to ensure that’s installed on the target machine).
So, what’s the second problem? OK, I try to run guihello.exe on Windows by double clicking on it. Hourglass for a few seconds, then nothing. Hmmm.
Looking in the Windows Event Viewer, I see the following error:
Application: guihello.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.IO.FileNotFoundException Stack: at MainWindow.Build() at MainWindow..ctor() at guihello.MainClass.Main(System.String)
That’s a System.IO.FileNotFoundException exception being thrown before we even get a chance to catch anything. It turns out the problem lies with a reference which is added automatically to Monodevelop’s GTK#-based projects: Mono.Posix – which, if your target machine just has the normal Microsoft .NET runtimes installed, won’t exist.
So, remove that reference. After you do that you’ll find that there will be lines in the auto-generated Build() method for the form which now fail – for example:
this.Title = global::Mono.Unix.Catalog.GetString ("MainWindow");
The GetString() call is to allow for internationalisation / translations. For this test I just changed the lines to use hard-coded strings such as:
this.Title = "Hello World";
Rebuild, copy the file over to Windows, et voilà, it now runs.
If you want to prevent Monodevelop from keeping putting back a reference to Mono.Posix and making strings translatable, whenever you enter a string into a widget’s property in the designer, click the “…” button and uncheck “Translatable” in the resultant dialog. Of course this then gives you the problem that you’ve got hard-coded strings, but that’s something for another post some time.