Are you curious about cross-platform .NET applications?

Do you have a need or an interest in how to write .NET applications that will run on OS X? Do you need to deploy your application to a *nix box? If you’ve answered “Yes” to one of these last two questions or are curious about running .NET code on *nix, then read on.

With this article, I’ll introduce Mono along with some advice and tips on how to write cross-compatible code.

What Is Mono?

Mono is an open-source *nix version of the .NET development platform. I’ve taken this description of Mono from the Mono Web site:

“The goal of Mono is to allow .NET developers to create and deploy cross-platform versions of their applications.”

As you start off with Mono, one thing that might confuse you is the version numbers. Don’t make the mistake of assuming that the current Mono version number maps to or matches any particular .NET Framework version number. For example, Mono 2.2 (the current version at the time of writing), is somewhere between .NET 2.0 and .NET 3.5. Odds are that you won’t have too much trouble running .NET 3.5 code. However, if you are using some of the .NET 3.0 features (WPF, Windows Workflow, WCF) then you will begin to experience discomfort because these .NET 3.0 features either do not exist or are only partially implemented. On the other hand sometimes Mono comes out with .NET features before .NET: Mono 1.2.6 had support for some .NET 3.5 features such as lambdas before .NET 3.5 was released. Or, if you need to use the C# compiler as a service, that is available now from Mono.

What is currently implemented and what is going to be implemented is constantly changing. For specific details, consult the Mono roadmap at http://www.mono-project.com/Roadmap.

Mono has been allowing developers to write and deploy cross-platform .NET applications since June of 2004. One of the handy things about Mono is that it is binary compatible with .NET. This means that in many cases you can compile your applications with the .NET Framework, and then deploy them to any Mono-supported operating system and hardware combination. Likewise, you could compile your application using Mono, and then deploy to the .NET runtime.

Places You Can Use Mono

What exactly is meant by cross platform? Mono supports both 32-bit and 64-bit systems and a wide variety of operating systems. Mono currently supports the following operating systems:

Linux

Mac OS X (and the iPhone)

Sun Solaris

OpenBSD, FreeBSD, NetBSD

Microsoft Windows

Nintendo Wii

Google Android

Mono will run on the following hardware:

x86

x86-64

IA64

ARM

Alpha

MIPS

HPPA

Sun SPARC (32 bit)

s390, s390x (32 bit and 64 bit)

In addition to the architectures listed above, it is possible to make your own smaller Mono install for embedded systems, if you find it necessary. It is beyond the scope of this article to go into this topic in detail, but you can find more about this by visiting the Mono Web site at http://www.mono-project.com/Small_footprint.

Of these four classes of issues that MoMA will find for you, System.MissingMethodException is the most oppressive. These exceptions will actually prevent your application from loading and running.

Getting Started with Mono

How do you get started with Mono? To begin with, you need to install it.

First, point your Web browser to the download page for the Mono project (http://www.mono-project.com/Downloads). From there you will be presented with several choices detailed below:

  • Use the LiveCD. The LiveCD is a good choice if you just want to test the waters. At the time of this writing, the Mono LiveCD is running openSUSE 11.1 with Mono 2.2 and sample applications. The LiveCD will give you the best performance as it is running on your hardware. The drawback is that you will have to take some extra steps to persist your data-saving to a USB drive for example.
  • Use the VMware Image. This technique is arguably just as simple (or simpler) than using the LiveCD. Download the VMware virtual machine and open it up using VMware’s free VMware Player. Once the virtual machine boots up, you are basically running the same environment that you would have if you would have gone with the LiveCD approach.
  • Use the Installer for your Operating System. This is probably the simplest option. Just download the appropriate installer for your OS and make use of that.
  • Build from Source. Being that Mono is open source, it is possible to get the source code for Mono and build it yourself. I won’t go into this option in this article, as building Mono from source and installing isn’t necessarily for the faint of heart, particularly on Windows. However, if you have specialized needs or want to hack the Mono source, this is yet another option for you.

For the purpose of this article, and for most users, any of the first three options mentioned above is more than sufficient.

If you are unsure how to get a copy of Mono to experiment with, ask yourself this question first:

How familiar am I with UNIX?

If you are comfortable working in UNIX (specifically Linux), then you might just want to use the VMware image or the LiveCD. This will give you a completely isolated sandbox and you won’t have to worry about “cluttering” up your workstation with something that you are only experimenting with anyway.

If you prefer Windows or are not comfortable with UNIX, then you might just want to use the Mono MSI download to install Mono on your PC.

With the details of how to install Mono on your computer out of the way, let’s move on to a bit about some of the tools you can use to craft your Mono applications.

The Tools

Technically you don’t need anything special to start writing code-it is possible to just use a text editor of your choice and the command line tools for compiling your code. However, this isn’t necessarily the best user experience for a developer.

One of the nice things about Mono-if you’re already a .NET developer-is that you probably already have everything you need to start writing applications. You can develop your applications in .NET like you used to and then deploy them with Mono-with a little bit of extra care and attention.

IDEs

If you’re like most .NET developers, the thought of using a text editor to type up your code doesn’t exactly make you giddy with glee. You probably prefer some sort of full-fledged IDE that helps with your day to day keystrokes. Here are several choices available to you:

  • Visual Studio. If you’re already a .NET developer, you might be surprised to know that you can still use your existing .NET tools to develop your Mono applications. Remember that Mono is binary compatible with .NET, so you need to be mindful of the parts of the .NET Framework that aren’t implemented or compatible with Mono.
  • SharpDevelop. SharpDevelop (sometimes referred to as #Develop) is an open source IDE for .NET development on the Windows Platform. At its core, #Develop provides much of the same functionality as Visual Studio: IntelliSense, Forms designer, syntax highlighting, refactoring, code completion, etc. If you don’t have Visual Studio for whatever reason, you will probably find #Develop a good second choice.
  • MonoDevelop. MonoDevelop was originally started as a port of #Develop to the GTK# Framework, for use on non-Windows platforms. MonoDevelop can read and use the Visual Studio 2008 solution and project files without any special effort on your part. If you really want to, you can compile MonoDevelop on Windows-but this is probably a bit excessive for the average user. MonoDevelop provides a good, solid, C# IDE for those who are not on Windows. Figure 1 is a screenshot of MonoDevelop running on openSUSE 11.1, editing a Visual Studio 2008 solution file.
Figure 1: MonoDevelop 2 editing a VS2008 solution.

Porting Your Existing .NET Code

In general, you can develop on Windows and run in *nix. The fact that Mono will produce binary compatible assemblies for .NET (or consume the .NET compiled assemblies) does certainly lower the cost of compatibility. However, you need to be aware of some potential obstacles.

Undocumented “Tricks” or Hacks. Be careful to avoid undocumented hacks. Consider bug 425512 (https://bugzilla.novell.com/show_bug.cgi?id=425512) as an example.

Briefly, this “trick” allows you to read the private variables of the System.Exception to retrieve the stack trace information of an Exception:

FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString",BindingFlags.Instance | BindingFlags.NonPublic );

This is a hack that allows you to capture the stack trace information on an inner exception. This code breaks in Mono, since the private field storing the stack trace is called “remote_stack_trace” and not “_remoteStackTraceString”.

Directory Separators. Remember that Windows uses \ to separate directories, while *nix uses a /. Using System.IO.Path.DirectorySeparatorChar and System.IO.Path.Combine can help with avoiding some of the pathing/file system issues.

Path Separators. If you’re dealing with environment variables, remember that Windows uses a semicolon “;” while *nix uses a colon “:”. Use the System.IO.Path.PathSeparator to help with these issues. Also, remember that drive letter references such as C:\ will also not work on *nix, and that absolute path names could cause you grief.

Mono is very liberally licensed to allow it to be used in a wide variety of scenarios.

Case sensitivity. Something else to remember is the fact that Windows is not case sensitive with directory and file names, but *nix is.

It seems onerous to have to go through an existing code base, find these issues, and deal with them. Luckily Mono has a portability layer that will help take care of this problem for you.

By setting an environment variable called MONO_IOMAP, you will turn on this portability layer, which will save you from the drudgery of hunting down and refactoring these changes to your code.

MONO_IOMAP has three possible settings:

case: allows case insensitive file system access

drive: strips drive name from paths.

all: same as all case and drive.

The following snippet shows how to set MONO_IOMAP, and then run your application at the command line:

mono@linux: ~> export MONO_IOMAP=all
mono@linux: ~> mono myapp.exe

This compatibility layer does not come for free. MONO_IOMAP will have some performance implications on your application, as now Mono has to do some extra work for you.

COM. Mono will support COM Interop on Windows. If you’re not running Windows, then you might want to experiment with a 3rd party product such as Mozilla’s XPCOM (http://www.mozilla.org/projects/xpcom/) or MainWin (http://www.mainsoft.com/products/mainwin.aspx). These products will help you use COM in your Mono application. For more details on COM Interop in Mono, it is best to consult the Mono Web site at http://www.mono-project.com/COM_Interop.

P/Invokes, aka Platform Invocations. P/Invokes, to refresh your memory, are function calls made into native Windows libraries. If your application makes use of P/Invokes, you must ensure that there is a similar function available in *nix or refactor your code. The web site at http://www.pinvoke.net might help you find an alternate way, using managed code, to work around your P/Invoke issues.

Another tool that you might find useful to help you with porting your application is the Mono Migration Analyzer, or MoMA for short. MoMA is a managed application that will run with .NET or Windows. MoMA will scan your assemblies and generate a report with potential portability issues that it finds.

Figure 2 is an example of potential issues that may be encountered, while Figure 3provides more information about what the issues might be.

Figure 2: MoMA results for StructureMap 2.5.2.
Figure 3: Details MoMA results from ASP.NET MVC.

Note that these are only potential issues. In spite of these issues it is possible that your application will still run.

MoMA will help you identify four types of issues:

  • Missing Methods. These are methods in the .NET Framework that are not implemented in Mono. The application will compile in .NET, but will not compile under Mono. Your application will still run under Mono-until it tries to use the missing method and then a System.MissingMethodException will be thrown.
  • MonoTodo. These are methods that are not fully implemented to some extent. Sometimes the method is missing just one small piece of functionality, and sometimes it is not implemented at all.
  • NotImplementedException. These are basically methods that are stubbed in, and will throw a System.NotImplementedException as soon as they are called.
  • P/Invokes. Mono can handle P/Invokes if the unmanaged library that is being called is implemented for your operating system. If it is not, then you will have to find another way to obtain the P/Invoke functionality on your target platforms.

Of these four classes of issues that MoMA will find for you, System.MissingMethodException is the most oppressive. These exceptions will actually prevent your application from loading and running. Your application can still load and run with the other three issues, with a run-time exception being the worst thing that might happen. In some cases, you might not even notice issues that MoMA told you about.

Some registry calls, for example are marked MonoTodo. If you are reading and writing to registry keys for your own application there should be no problems. However if you are trying to read and write registry keys set by another application then they may not work as you thought they would.

One of the handy things about Mono is that it is binary compatible with .NET.

After running MoMA, you have the option of submitting the results to the Mono team for their use. The Mono team collects and uses the data that you submit via MoMA to help determine what features of the .NET Framework they should implement in future releases. If there is a particular feature of .NET that you would like to see implemented, make sure you submit the MoMA results and let the Mono team know. Of course, being open source, you could also just implement it yourself and submit a patch.

Databases. If you need to interact with a database, Mono will already have an ADO.NET data provider. Which database do you want to use for your application: SQL Server, Oracle, MySQL, PostgreSQL, DB2, Firebird, SQLite, or some database for which there is an ODBC driver? Mono will support them all.

Deploying Your Application

At some point you will want to deploy your application so that it can be useful to you.

Figure 4: WinForms App running on Linux via Mono.
Figure 5: The WinForms DateTimePicker control.
Figure 6: Another WinForm in Linux via Mono.

Web Applications

One of the biggest reasons people use Mono is to run ASP.NET applications on *nix. At the time of writing, Mono supports all of ASP.NET 2.0 (with the exception of WebParts) and ASP.NET AJAX.

The two most popular ways to run your ASP.NET applications are XSP and mod_mono.

XSP is the simplest way to get started and is helpful while developing your ASP.NET application. XSP is good for quickly getting started-it is analogous to the Visual Studio Web Development server that comes with Visual Studio 2005/2008.

There are actually two versions of XSP-there is xsp and xsp2. The difference between the two is that xsp is primarily for ASP.NET applications developed for .NET 1.x, while xsp2 is for Web applications developed for .NET 2.0 or higher. In all likelihood, you will want to use xsp2.

Running xsp2 on your local box is as simple as:

linux@linux:~> cd ~/HelloWorld
linux@linux:~/HelloWorld> xsp2
xsp2
Listening on address: 0.0.0.0
Root directory: /home/HelloWorld
Listening on port: 8080 (non-secure)
Hit Return to stop the server

You should be aware that by default, xsp and xsp2 run in release mode. Consequently, you will be missing debugging information when errors occur. If you’d like to run xsp/xsp2 in debug mode, you must set the MONO_OPTIONS environment variable:

linux@linux:~/HelloWorld>MONO_OPTIONS=--debug xsp2
xsp2
Listening on address: 0.0.0.0
Root directory: /home/HelloWorld
Listening on port: 8080 (non-secure)
Hit Return to stop the server

Xsp and xsp2 are handy for local development and testing, but for use in a production environment one should probably use Apache and mod_mono.

Most distributions of Linux come with Apache and mod_mono, so it is probably best to use the packages that come with your particular distribution for installation.

Earlier versions of mod_mono required that the Apache configuration files be manually edited for each application. This wasn’t a lot of work or effort, but it was tedious.

To simplify the deployment of an ASP.NET application, mod_mono now has an auto-configuration (AutoConfiguration) capability which is designed to provide zero configuration for your applications. AutoConfiguration will allow any account on a machine to deploy ASP.NET applications without going through the steps of manually editing the Apache config files. You don’t have to do anything to enable AutoConfiguration in mod_mono. Once Apache loads mod_mono, the auth-hosting functionality will also be present.

Don’t make the mistake of assuming that the current Mono version number maps to or matches any particular .NET Framework version number.

If you are using AutoConfiguration, you will probably want to do one quick edit to your mod_mono.conf file:

MonoServerPath "/usr/bin/mod-mono-server2"

If you do not do this, then your application will use the 1.0 runtimes, which will cause problems with applications that target the newer versions of .NET (or Mono).

Desktop Applications

Next, let’s take a look at desktop applications. With .NET you have two choices for writing applications:

WinForms

Windows Presentation Foundation (WPF)

Currently Mono does not support WPF, and there are no plans to implement WPF. However, there are several other options available for developing desktop applications. Some popular options are:

WinForms needs no introduction. The Mono project has implemented a compatible version of WinForms to make it easier to port your existing applications to *nix. You can still design your forms using Visual Studio and use them in Mono. If you do not have Visual Studio available, #Develop also has a GUI designer for WinForms.

Another option for desktop applications is GTK#. GTK# is a .NET binding for GTK+. GTK+ is, in turn, a toolkit for creating cross-platform GUIs. GTK+ itself is written in C. One of the more famous users of GTK+ is the GNOME platform. MonoDevelop does have a designer for GTK# called Stetic, which functions very much like the Form Designer in Visual Studio and is very useful in creating your GTK# GUI.

Finally Cocoa# is a binding that allows developers to provide a native Mac OS X look to their applications by calling the Cocoa APIs with their C# code.

If you’re trying to decide if you should use WinForms or GTK# for your application, ask yourself one question:

Who are the primary users of my application?

If you are porting an existing application, or the primary users of your application will be running Windows, you want to stick with WinForms. If the primary users of your application will be running *nix and/or Mac go with GTK#. If the users of your application will only be Mac users, then use Cocoa#.

Of course, you don’t have to limit yourself to just one framework. Some applications implement a front end using all three frameworks so as to have the best look and feel for the operating system in use. For an example of this, check out Tangerine at http://snorp.net/tangerine/.

I Am not a Lawyer

If you’re concerned about any legal implications surrounding the use of Mono, it’s best to receive professional legal advice from a lawyer in your jurisdiction. However, generally speaking, you can use Mono to write commercial and proprietary software. Mono is very liberally licensed to allow it to be used in a wide variety of scenarios.

The runtime libraries are licensed under the GNU Library GPL (LGPL) 2.0, which basically states that it is okay to dynamically link the binary run-time libraries without having to release the source code. As well, it is possible to contact Novell and obtain a proprietary license.

The class libraries fall under the MIT X11 license. This license states that you may do whatever you want with the software (sell/modify/use) without any restrictions, save the retention of the MIT X11 license.

As for the C# compiler, that is dual licensed under the MIT X11 and the GPL.

Finally, all the tools (i.e., MonoDevelop) that come with Mono are covered by the GPL.

Next Steps

Hopefully by this point, you have a better feel for what Mono is and what you can do with it. Feel free to check out the Mono mailing lists for more information or if you need help. You will find that they are, in general, friendly and prompt in providing assistance or information.

Download, install, and try it out. You will most likely be pleasantly surprised with how quickly you can get started with Mono.