Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MONO: an alternative for the .NET framework

0.00/5 (No votes)
28 Aug 2013 1  
This article presents possibilities for development of .NET applications running on operating systems other than Windows, using the MONO platform. Advantages and challenges will be presented. Also presented are some common issues encountered while developing applications using the .NET technology.

This article is written by Arkadiusz Merta and was originally published in the February 2005 issue of the Software 2.0 magazine. You can find more articles at the SDJ website.

Introduction

The .NET technology opened a new world of possibilities for highly distributed applications. .NET Remoting and Web services enable them to communicate all over the world. Language independency, seamless integration with databases, Web interfaces as easy to build as regular Windows applications – a new world of possibilities is ready to be discovered by developers.

This article presents possibilities for development of .NET applications running on operating systems other than Windows, using the MONO platform. Advantages and challenges will be presented. The paper also presents some common issues encountered while developing applications using the .NET technology. The primary idea is to show whether the same C# code samples can be compiled under .NET and MONO (using the Microsoft C# compiler – csc, or the MONO compiler – mcs), executed under either .NET or MONO, and run under either Windows or Linux.

The Seed

In the .NET Framework, one of the primary ideas was to isolate the application (assembly) from the operating system. This is achieved by compiling .NET source code into an executable that contains Microsoft Intermediate Language (MSIL), which does not depend upon the operating system or the target processor. During execution, MSIL is translated into native code for the particular processor, and linked with the appropriate operating system libraries, and executed as a ‘regular’ application. So, in this context, the .NET Framework builds a functional abstraction layer for .NET applications. For example, Windows Forms, a standard graphical interface for .NET applications, is translated by the .NET Framework into a series of calls to the .NET Framework’s Common Library (CL). This is very different to the approach previously used in Visual C++ Microsoft Foundation Classes (MFC), which actually executed direct calls to the Win32 API. .NET assemblies are executed against the .NET Framework, while Win32 applications are calling the operating system directly. This feature promises operating system independency. The trick is that the .NET Framework is a single vendor solution; although some ports to mobile devices are available (SmartPhone) – they are really bound to Microsoft's offerings.

The biggest competitor of .NET – the Java Virtual Machine – has one feature that gives it an advantage: true system independency. Java was developed much earlier than Microsoft .NET, and from the very beginning, it was meant to be an Open Source product. No wonder that the community accepted it and developed ports for the Java Virtual Machine for many different platforms. When .NET was released, Java was already quite popular, ported to many operating systems, with a rich set of free development tools already available (e.g., IBM Sun ONE Studio and Eclipse). The .NET Framework is given away for free, but the .NET development tools are quite expensive – Visual Studio .NET is sold for more than $2000.

To be able to compete with Sun’s Java, Microsoft (together with Hewlett-Packard and Intel) published C# and the Common Language Infrastructure (CLI) specifications and submitted them to the international standardisation organization, ECMA. As a result, ECMA-334 (C#) and ECMA-335 (CLI) standards were ratified late December 2001. This way, the .NET Framework was now on track to become really portable.

The Birth

The MONO project started in 2001, but the first stable release, 1.0, was issued in Q2 2004. The work was initiated by Ximiana (and based upon the research project ROTOR), which was further bought by Novell.

The aim was quite clear: to move the .NET Framework from the one vendor solution into a wide and portable standard for a number of platforms and operating systems. Versions for x86, SPARC, and PowerPC platforms are available. Unix, Linux, FreeBSD and Windows are supported. C# (stable) and eventually Basic (unstable) can be used for development. System, Web, Remoting, security, Web services, and XML packages are ready. Some data components are ready (Oracle), some are unfortunately unstable (including Microsoft SQL client; the full list of packages can be found in the section MONO components).

This article refers to MONO 1.0.1.

Mono for Windows

In this article, the terms ".NET Framework" refer to the Microsoft product, and "MONO Framework" refers to the MONO implementation of the CLI.

Please install Windows MONO to: c:\mono\Mono-1.0 which will save you a lot of effort while compiling packages (there are some bugs within the packager configuration). Furthermore, the PATH variable must be updated with the path to the MONO bin directory (here: c:\mono\Mono-1.0\bin).

Installing MONO under Windows with .NET Framework actually means that two execution engines will work side-by-side. Please be aware that simply double-clicking an assembly (or running it from the command line) will execute it using the .NET Framework, which is the default for the system. In order to execute an assembly under MONO, a command must be used:

mono <assembly_name>

The easiest way to do this is to use the MONO command prompt.

The School

Let us try to write a small .NET console application, compile it under Windows using Microsoft as well as MONO tools, and execute it under Windows.

The piece of C# code shown in Listing 1 outputs Hello World! to the standard console.

Listing 1. Hello World application in C#

using System;
namespace FirstHomeWork{
   class First{
      [STAThread]
      static void Main(string[] args){
         System.Console.WriteLine(“Hello World!”);
         System.Console.ReadLine();
      }
   }
}

Let us save it under first.cs and compile it using csc (Microsoft’s .NET C# compiler) into first_ms.exe:

>csc /out:first_ms.exe first.cs
>first_ms.exe
Hello World!

As expected, the execution outputs the programmers' beloved string on the standard console and waits until Enter is pressed.

Let us do the same with MONO, using msc (MONO C# compiler):

>msc -out:first_mono.exe first.cs

As a result we get first_mono.exe file. Trying to run it we will receive the same result as for first_ms.exe file:

>first_mono.exe
Hello World!

But what actually happened here? We just executed the C# source code compiled using the MONO msc compiler, under ... the Microsoft .NET Framework!

To execute first_mono.exe under the MONO Framework, we will have to use:

>mono first_mono.exe
Hello World!

The result is (as hoped) the same. The assembly files produced by mcs and csc have the same size. However, when we compare them byte-by-byte, it will appear that some byte codes differ.

One important note here is how the MONO assemblies are executed. For the latter example, we used a mono command to run them. This command uses a Just in Time (JIT) compiler to translate the code from intermediate language into the target platform native, stores the result in memory, and executes from there afterwards. A copy of the JIT-compiled code stays in memory for subsequent use.

One MONO feature should be introduced here: mint. Mint is an interpreter. In order to execute a MONO application using mint, we can call the following from the command line:

mint first_mono.exe

The output will be quite the same. But under the hood, execution will be done in a different way. Mint, as an interpreter, does not use JIT at all, it just walks through the compiled code, interprets each of the ECMA-CLI bytes, and executes them against the x86 instruction set. Because interpretation is done at each step, overall, applications run by mint tend to be slower than the ones run under mono. However, JIT compilation takes some time at the beginning, and small applications can be faster with mint than when they are run with mono.

But, because mint interprets the code, the C# compiler can be used for debugging purposes. Additionally, by avoiding JIT, compilation allows for execution on platforms where JIT is not available yet (OS X).

We now know some basic principles of MONO; now let us try to write a simple dialog application. The application will be created using Visual Studio .NET (see Figure 1 and Figure 2).

Figure 1. Simple dialog application – a label and a ‘Say Hello’ button

Figure 2. Simple dialog application after clicking ‘Say Hello’

We have the source code, let us try to compile it under MONO:

mcs –out:form_mono.exe form.cs
form.cs(13) error CS0246: Cannot find type ‘System.Windows.Forms.Form’
Compilation failed: 1 error(s), 0 warnings

No luck? The reason for our failure is quite simple: the MONO Framework implements CLI functionality, but not fully. Unfortunately, some of the packages are still marked as unstable or unimplemented (a list of packages included in MONO 1.0 can be found in the section Mono components). Moreover, some packages probably won’t be implemented at all.

Let us try to concentrate on the GUI. As stated in the introduction, the MONO project's aim is to create frameworks based upon CLI specifications, for platforms other than x86 and MS Windows. The .NET Framework for building Windows applications uses Windows Forms (System.Windows.Forms namespace). But it contains a set of controls designed especially for Microsoft Windows. Does it mean that there is no possibility to build GUIs with MONO? Of course not. The difference is that Unix / Linux – based systems have their own, widely accepted graphical package: Gtk.

Gtk was, from the beginning, created to support GIMP – a graphical tool for X-Windows systems. After that, it became an important builder of graphical interfaces operating under Unix / Linux. Now, its managed version called Gtk# is also distributed with the MONO project under the Gtk and GtkSharp namespaces (gtk-sharp.dll).

The assemblies compiled with MONO will be executable under the .NET Framework (not: MONO Framework) as long as only .NET Framework namespaces implemented in MONO are used.

So, to create MONO GUI applications that will work under either Windows or Linux, Gtk# has to be used. Once again, there is, for now, no possibility to compile stable .NET Windows Forms applications under MONO (and execute them across platforms).

The good thing is that Gtk# is quite easy to learn, and a lot of functionality is the same as in Windows Forms. Let’s try to test this conclusion (Listing 2). We save it to form_mono.cs file and compile using mcs:

mcs –out:form_mono.exe –pkg:gtk-sharp form_mono.cs

Please note that a reference to the gtk-sharp package must be added (-pkg:gtk-sharp). Let us execute the application over the MONO Framework by calling: mono form_mono.exe. The result can be seen in Figure 3.

Listing 2. Dialog application that uses Gtk# for building a graphical interface

using System;
using Gtk;
using GtkSharp;

namespace FirstDialogMONOApp{
   public class form_mono{
      private static Label lbl;
      static void onWindowDelete(
         object obj, DeleteEventArgs args){
            Application.Quit();
      }
      static void onBtnClick(object obj, EventArgs args){
         lbl.Text = "Hello to you!";
      }
      static void Main() {
         Application.Init();
         Window win = new Window("First Form - mono");
         win.DeleteEvent+=
            new DeleteEventHandler(onWindowDelete);
         VBox vbox = new VBox(false,1);
         lbl = new Label("???");
         vbox.PackStart(lbl,false,false,1);
         Button btn = new Button ("Say Hello");
         btn.Clicked+=new EventHandler(onBtnClick);
         vbox.PackStart(btn,true,true,1);
         win.Add(vbox);
         win.ShowAll();
         Application.Run();
      }
   }
}

Figure 3. Simple dialog application – MONO version

If we compare the source code of the Windows and MONO versions, a lot of similarities would be found. Some widget names are quite the same (Label, Button), some methods and attributes are the same (e.g. Label constructor, Label.Text attribute), and events also match (DeleteEvent – DeleteEventHandler, Clicked – EventHandler).

OK, we now know that to build a GUI application in MONO, Gtk# must be used, because MONO 1.0 does not contain an implementation of System.Windows.Forms. That’s why a .NET application won’t compile directly using MONO. A question emerges: can a MONO GUI application that uses Gtk# be executed over a .NET Framework? Let us check by calling: form_mono.exe. As a result, we will receive a Just-In-Time Debugging dialog with an exception (Figure 4). Looking more closely at Figure 4, we will discover that the exception type is a System.IO.FileNotFoundException. By allowing the debugger to run, the extended message will provide us with some more details (Figure 5).

Figure 4. JIT exception dialog

Figure 5. Debugger exception window

glib-sharp is known to us now – we referenced it while compiling our MONO dialog application. It seems, however, that during application execution, it can no longer be found. Since this library will be used for several applications – let us try to make it accessible.

The best place to put a .NET (or .NET compatible) assembly that must be shared with many applications is the Global Assembly Cache (GAC). GAC is a special, isolated storage area, where applications can be stored and executed with policies assigned to them. The contents of the .NET Framework GAC can be listed via Administrative Tools/Microsoft .NET Framework Configuration (Figure 6).

By clicking View List of assemblies in the Assembly Cache, we will receive a list of assemblies that reside in GAC. Since our task is rather to add gtk-sharp to GAC, we choose Add an Assembly to the Assembly Cache, find gtk-sharp.dll (should be in Mono-1.0/lib/mono/gtk-sharp), and add it. Let's check whether it has been added, using View List of assemblies in the Assembly Cache option this time (Figure 7).

The contents of the GAC can also be listed from the command line, using the gacutil tool:

gacutil /lr

Other command line switches enable GAC manipulations.

Figure 6. .NET configuration

Figure 7. .NET GAC containing the ‘gtk-sharp’ assembly

Let us try to execute our MONO application again by calling: form_mono.exe.

Still no luck? We will receive the same error message. The exception message, however, contains the statement that either gtk-sharp or one of its dependencies was not found. Since we put gtk-sharp into GAC already, we can assume that the first condition has been satisfied. We have to check what kind of dependencies are required for Gtk#.

Manifest

The .NET executable files differ very much from regular Windows executables. Besides the content of intermediate code instead of system native, their organisation is a bit different. What’s most important at this point is that they contain a manifest. Manifest data contains information about the assembly and a list of assemblies that it depends upon. It also contains all the publicly exposed types and resources. Investigating it is a good way of gathering knowledge of how the module can be used.

Let us check the dependencies using Microsoft’s ildasm tool (Figure 8). The ILDSAM tool parses any .NET Framework EXE/DLL module and shows the information in an easily-readable format. It allows the user to browse a manifest that contains the used .NET namespaces, types, and dependencies. MONO provides a command line disassembler: monodis.

Figure 8. Ildasm tool lists the contents of the manifest of gtk-sharp.dll

As we can see, the gtk-sharp assembly depends upon several other assemblies:

  • mscorlib, which is a .NET engine
  • glib-sharp – general-purpose utilities
  • atk-sharp – the accessibility toolkit
  • gdk-sharp – an intermediate layer which isolates Gtk from the details of the windowing system
  • pango-sharp – advanced font and text handling that is used for GDK and Gtk
  • System, a default namespace

As we did with gtk-sharp above – let us add atk-sharp, gdk-sharp, glib-sharp, and pango-sharp to the GAC (Figure 9).

Figure 9. GAC updated with Gtk# and its dependencies

The final step – running the application – should succeed now. This proves that Gtk# and its dependencies are fully compliant with the Microsoft .NET Framework.

The Study

I believe that we know enough at this point to try to make our application distributed. As seen in MONO components, the MONO Framework implements the System.Web and System.Web.Services namespaces – we will try to use them in order to build an application that takes some data out from the Web server using Web services. The Hello dialog application will be extended here to use the Web service to get a welcome string value. We will test whether MONO can create a Web service that will be used either by an application written in .NET or MONO.

The MONO Web Server

MONO comes with its own small Web server which is called XSP. It can be run in batch using:

c:\mono\Mono-1.0\bin\startXSP.bat

By default, it resides on port 8088. To see its start page, an address like:

http://localhost:8088/

should be entered in a Web browser.

First, let us create hello.asmx and place it in the Web server root:

<%@ WebService Language="c#" 
      Class="helloServices.hello" 
      Codebehind="hello.asmx.cs" %>

The header specifies that:

  • the code–behind class is implemented in C#
  • the namespace is defined as helloServices, the class of the Web service is named hello
  • the class is implemented in the hello.asmx.cs file

Now, let us create the Web service implementation (Listing 3).

Listing 3. Class that implements the Web service

using System;
using System.Web.Services;

namespace helloServices{
   public class hello:System.Web.Services.WebService {
      [WebMethod]
      public string getHello(){
         return "Hello (from the Web Service)";
      }
   }
}

The Web service code – behind file (as in Listing 3), contains a Web method called getHello which returns a welcome string. Now we will compile the code–behind file, adding a reference to System.Web.Services (/r: switch) and specifying the target as library (/t: switch):

>mcs /r:System.Web.Services.dll /t:library hello.asmx.cs

The output file (hello.asmx.dll) should be stored in the Web server /bin directory. Let us try to use a Web browser to see whether the Web service works (Figure 10).

Figure 10. Hello Web service called from within a Web browser

And it worked! We can now invoke a method, clicking on the getHello link and Invoke after that.

OK – we wrote a Web service, compiled it under MONO using mcs, and that works even when invoked from IIS. But how about using it in our application?

Let's start with the easier solution: using MONO’s Web service inside a .NET application. To do this, a Web reference in a Visual Studio .NET solution to the hello service must be added, and the button click handler modified as shown in:

private void btn_Click(object sender, System.EventArgs e){
   localhost.hello helloService = new localhost.hello();
   lbl.Text = helloService.getHello();
}

Since I operated on a single machine, a reference to localhost has been added. By doing this, Visual Studio generates a proxy for the service (localhost.hello class), which is further instantiated and the getHello Web method invoked. The result is as expected (Figure 11).

Figure 11. Dialog app that acquired the welcome string from the Web service created using MONO

How about our MONO application? As mentioned, Visual Studio creates a proxy automatically. Since there is no special development tool for MONO under Windows yet – we have to do it ourselves using a tool called wsdl (included in the MONO package):

>wsdl.exe –n:helloWS http://localhost/hello.asmx?wsdl

This will generate the hello.cs file containing the proxy code. Please always specify the namespace of the proxy using the -n switch (here: namespace was set to helloWS). Let’s modify the Say Hello button handler:

static void onBtnClick(object obj, EventArgs args){
   helloWS.hello helloService = new helloWS.hello();
   lbl.Text = helloService.getHello();
}

The web server XSP, included in the MONO distribution, enables proxy generation too. Let’s compile everything, adding a reference to the Web service:

> mcs -out:form_mono.exe -pkg:gtk-sharp
      -r:System.Web.Services.dll form_mono.cs hello.cs

...and run under MONO using: mono form_mono.exe, achieving the results as can be seen in Figure 12.

Figure 12. MONO application calling the Web service

Thanks to adding Gtk# to GAC, the application executed under the .NET Framework also works fine! So now, we can build Web services and proper clients using MONO. We saw that the code can be compiled by either .NET or MONO, and executed against both frameworks. This is, of course, because the MONO Framework implements a System.Web namespace.

Marriage

A key reason for using MONO instead of Microsoft .NET is MONO’s ability to operate over different platforms. For a Linux platform, a Red Hat 9 Server with GNOME desktop will be used here as our test machine.

Install MONO under Linux

In order to install MONO under Red Hat Linux:

Download mono_all.zip from here.

  • Unpack it – it should contain several rpm packages
  • Execute shell: >rpm –Uvh –nodeps *.rpm
  • Run Web server: >mono /usr/bin/xsp.exe
  • Run MonoDevelop from menu

I’ve managed to install MONO under SuSE Linux 9.1 Personal with success. The MonoDevelop tool didn’t run though, because of unresolved package dependencies (SuSE uses KDE instead of GNOME, by default).

Figure 13. MonoDevelop under Red Hat 9

MonoDevelop

A GUI based development tool has been created for the MONO platform. MonoDevelop is not as sophisticated as Visual Studio .NET. It contains a simple editor with syntax highlighting, context help, code auto-completion, and it enables applications to be compiled. It organises files into projects and solutions, and several project templates are available.

In order to test MONO portability, we will try to execute applications compiled under Windows using MONO mcs against RedHat with MONO 1.0. To do this, the assembly is simply copied to a Linux machine and executed there (Figure 14).

Figure 14. Dialog application executed under Linux

And it worked again! Looking more closely at Figure 14, we can see that the application looks a bit different to the one executed on Windows. This is because Gtk# for Windows uses the Windows look & feel – Linux desktop widgets differ slightly.

The Well Informed Friend

One of the issues specific for enterprise-level applications is that they perform a lot of processing, cooperating closely with databases. Microsoft’s .NET improves on the great Microsoft SQL client, with DataSets that significantly improve work with databases. The DataSet is one of the major components of ADO.NET. A DataSet is the result of querying a database against views, tables, or rows (using either stored procedures or an SQL query). It can be considered a snapshot of the queried data. It not only provides data, but incorporates the functionality of synchronisation with the data source (insert, delete, and update).

MONO can interact with a variety of commercial and open source databases using ADO.NET (and some third-party tools), as listed in Table 1.

Table 1. Databases or database engines supported by MONO

Database/ Engine

Namespace

Assemblies to reference with ‘-r’ option

Notes

.NET

MySQL

ByteFX.Data

System.Data.dll

ByteFX.Data.dll

A third-party package distributed with MONO

 

ODBC

 

System.Data.Odbc

System.Data.dll

 

Yes

Microsoft SQL

System.Data.SqlClient

System.Data.dll

Support for ver. 7 and 2000

 

Yes

Oracle

System.Data.OracleClient

System.Data.dll System.Data.OracleClient.dll

 

 

Yes

PostgreSQL

Npgsql

System.Data.dll

Npgsql.dll

 

 

 

Firebird/ Interbase

FirebirdSql.Data.Firebird

System.Data.dll

FirebirdSql.Data.Firebird.dll

A third-party package distributed with MONO

 

IBM DB2

IBM.Data.DB2

System.Data.dll

IBM.Data.DB2.dll

 

 

 

OLE DB

System.Data.OleDb

System.Data.dll

Using Gnome DB, i.e., Access

Yes

SQL Lite

Mono.Data.SqlliteClient

System.Data.dll

Mono.Data.SqliteClient.dll

Embeddable SQL database engine

 

 

Sybase

Mono.Data.SybaseClient

System.Data.dll Mono.Data.SybaseClient.dll

 

 

 

As can be seen, the number of databases supported by the MONO Framework is far richer than the number offered by Microsoft. Of course, libraries for databases missing in .NET also exist – but they are not distributed along with Visual Studio .NET.

A sample use of the Microsoft SQL database engine is given in Listing 4.

Listing 4. SQL client source code

using System;
using System.Data;
using System.Data.SqlClient;
 
namespace SQLClient{
class sqlclient{
   [STAThread]
   static void Main(string[] args){
      SqlConnection conn;
      conn = new SqlConnection();
      conn.ConnectionString = 
         "user id=sa;data source=192.168.89.176;" + 
         "persist security info=False;
         initial catalog=MONOTest";
 
      SqlCommand sel = new SqlCommand();
      sel.Connection = conn;
      sel.CommandText =
         "SELECT id, Name, Surname FROM people";
 
      SqlDataAdapter dataAdapter = new SqlDataAdapter();
      dataAdapter.SelectCommand = sel;
 
      DataSet ds = new DataSet();
      dataAdapter.Fill(ds, "people");
 
      DataTable table = ds.Tables[0];
      for(int row = 0; row < table.Rows.Count; row++){
         System.Console.Write("{0}:  ", row+1);
         for(int col = 1; col < table.Columns.Count; col++)            
            System.Console.Write("{1}:{2},  ",
               row+1,
               table.Columns[col].ColumnName,
               table.Rows[row].ItemArray[col].ToString());
            System.Console.WriteLine("");
         }
      System.Console.ReadLine();
   }
}

It can be compiled under .NET (using csc) or MONO (using mcs; add the -r:System.Data switch). It can be executed under .NET or the MONO (calling mono) Framework, and it works well under either Windows or Red Hat (Figure 15).

Figure 15. SQL client executed in Red Hat

The Rat Race

A comparison of any two platforms cannot be seen as complete without a performance test. As a sample test, I chose an application from the previous section, and augmented it with insert and delete operations. The test database contained 2500 rows to select. ‘Insert’ added another 500 rows. ‘Delete’ removed them. As a test machine, a Pentium III 500 with 64 MB of RAM was used. The results are shown in Table 2.

The table shows that applications executed against MONO tend to be much slower than the ones executed against the .NET Framework. Actually, it is not surprising that Microsoft’s framework connecting to Microsoft’s database is faster than MONO. What is actually surprising is that a MONO application executed against the .NET Framework was as fast as Microsoft’s native.

Table 2. Comparison of application performance compiled using MONO and .NET run under Windows 2000

 

MS Windows 2000

Platform MONO

Platform .NET

MONO app*

[ms]

.NET app**

[ms]

MONO app***

[ms]

NET app****

[ms]

Connection Creation

1071

891

111

111

Select 2500 rows

2667

2353

781

781

Insert 500 rows

2072

2125

719

713

Delete 500 rows

2720

2754

663

662

* Application compiled under MONO (using mcs) and executed against MONO platform (using mono).

** Application compiled under .NET (using csc) and executed against MONO platform (using mono).

*** Application compiled under MONO (using mcs) and executed against .NET platform.

**** Application compiled under .NET (using csc) and executed against .NET platform.

One important note here is that both MONO and .NET assemblies executed at almost the same speed.

For accurateness, let us have a quick look at the test made by executing the application under the mint interpreter (Table 4). The comparison shows that, for some operations, the interpreter can be quicker; for others though, they can be even four times slower!

Table 3. Comparison of application performance compiled using MONO and .NET, run under Red Hat 9

 

Red Hat 9

MONO/Win app*

[ms]

MONO/Linux app**

[ms]

.NET/Win app***

[ms]

Connection Creation

510

510

509

Select 2500 rows

664

626

652

Insert 500 rows

2072

2039

2061

Delete 500 rows

2770

2726

2859

* Application compiled under Windows MONO (using mcs) and executed against MONO platform under RedHat 9 (using mono).

** Application compiled under RedHat MONO (using mcs) and executed against MONO platform under RedHat 9 (using mono).

*** Application compiled under Windows .NET (using csc) and executed against MONO platform under RedHat 9 (using mono).

Table 4. A comparison of MONO applications run using the ‘mono’ command (i.e. using JIT) and the ‘mint’ command (i.e. interpreted)

 

Windows 2000

Red Hat 9

mono <>*

[ms]

mint <>**

[ms]

mint <>***

[ms]

Connection creation

1071

1106

807

Select 2500 rows

2667

3620

1419

Insert 500 rows

2072

5491

5932

Delete 500 rows

2720

9720

10889

* Application compiled under MONO (using mcs) and executed against MONO platform (using mono) hosted by Windows (for comparison).

** Application compiled under MONO (using mcs) and executed against MONO platform (using mint) hosted by Windows.

*** Application compiled under MONO (using mcs) and executed against MONO platform (using mint) hosted by RedHat.

A Short Coffee Break

A MONO package also contains and supports a set of third-party tools, one of which is called IKVM.NET. IKVM.NET is an implementation of the Java Virtual Machine for .NET or MONO runtimes (see Figure 13 – one of the project template-type nodes is called Java). IKVM.NET has ambitions to join what are considered as mutually exclusive technologies of Sun and Microsoft to allow seamless execution against one runtime framework.

The project is still under active development, and a lot of functionality needs to be implemented (e.g., AWT and Swing components!). However, some key points for the future should be mentioned:

  • A Java Virtual Machine implemented in .NET.
  • A .NET implementation of the Java class libraries.
  • The ikvmc tool, that enables conversion of Java jars into .NET compatible assemblies.

A promise of taking advantage of legacy systems written in Java and interoperability of .NET is quite interesting. Still, this is for the future.

Weekend: Hit the Glade

One of the interesting parts of MONO is called glade. The Glade classes give applications the ability to load user interfaces from XML files at runtime. So, generally, changes in the application’s look and feel can be performed without the need to recompile it!

Let’s implement our dialog application using Glade. First of all, we will create a GUI outline. As stated above, it is defined inside an XML text file. It can, of course, be written manually, but several freeware builders are available. Here, Glade for Windows will be used (Figure 16). Glade for Windows can be downloaded from SourceForge. A Linux version can be found here.

Figure 16. ‘Glade for Windows’ while building a sample application

A generated gui.glade XML file that defines a GUI, looks like the one shown in Listing 5 (some lines were removed for clarity).

Listing 5. XML file with GUI definition

<?xml version="1.0" standalone="no"?>
   <!--*- mode: xml -*-->
<!DOCTYPE glade-interface
   SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
 
<glade-interface>
 
<widget class="GtkWindow" id="window1">
   <property name="title" translatable="yes">
      First Form</property>
   ...
   <property name="resizable">False</property>
   ...
   <signal name="delete_event" 
      handler="OnWindowDeleteEvent" />
 
   <child>
      <widget class="GtkVBox" id="vbox1">
         <property name="visible">True</property>
         <property name="homogeneous">False</property>
         <property name="spacing">0</property>
 
         <child>
            <widget class="GtkLabel" id="lbl">
               <property name="label" translatable="yes">???</property>
               ...
            </widget>
         </child>
         ...
         <child>
            <widget class="GtkButton" id="btn">
               <property name="label" translatable="yes">
                  Say Hello</property>
               ...
               <signal name="clicked"
                  handler="onBtnClick"/>
            </widget>
         </child>
         ...
      </widget>
   </child>
</widget>
</glade-interface>

Please take a look at the lines: signal and property. They set basic properties (e.g. name), or define signals connected to them. A Glade signal is actually an event, which will be further handled by a specified method.

We will create a Glade client application (glade.cs; Listing 6). The client code shows the main class that triggers an application and two event handlers (for button click and window close) specified in the respective signal XML statements. Furthermore, a Glade.XML.GetWidget is used to get a reference to the label element. The XML file is given as a Glade.XML constructor parameter.

Listing 6. C# application that uses GUI definition from an XML file

using System;
using Gtk;
using Glade;
 
public class GladeApp {
   private Glade.XML gxml;
   public static void Main (string[] args) {
      new GladeApp (args);
   }
   public GladeApp (string[] args) {
      Application.Init();
      gxml = 
         new Glade.XML (null, "gui.glade", "window1", null);
      gxml.Autoconnect (this);
      Application.Run();
   }
   public void onBtnClick(object obj, EventArgs args){
      helloWS.hello helloService = new helloWS.hello();
      Gtk.Widget wg = gxml.GetWidget("lbl");
      if(wg != null )
         ((Label)wg).Text = helloService.getHello();
   }
 
   public void OnWindowDeleteEvent (object o, DeleteEventArgs args) {
      Application.Quit ();
      args.RetVal = true;
   }
}

Now, let us compile everything as shown in Listing 6:

>mcs –pkg:gtk-sharp –pkg:glade-sharp –r:System.Web.Services
     –linkresource:gui.glade glade.cs hello.cs

Please note that:

  • as before, a reference to Gtk# has been added (-pkg:gtk-sharp switch).
  • a reference to Glade has been added (-pkg:glade-sharp switch).
  • a reference to System.Web.Services has been added (-r: System.Web.Services switch).
  • a reference to the resource file gui.glade has been added (-linkresource:gui.glade switch).
  • glade.cs and hello.cs have been added to the build.

And run by calling:

mono glade.exe

The application does not differ from the one shown in previous samples. This is because Glade uses Gtk# widgets.

The beauty of this solution can be seen when we try to edit gui.glade (using, for example, Notepad). For example – a label name can be changed from ??? into Waiting...; running glade.exe again will display these changes – without the need to recompile anything.

Of course, there are several disadvantages to such a solution. Obviously, an event (signal) handler must be bundled inside the compiled assembly, so if functionality changes, the application will have to be rebuilt anyway. Furthermore, XML, despite being in a text format, is hard to read, and a good tool for creating and editing it is required.

A Life Within

Another interesting feature of MONO is the possibility to embed it within other programs: you can write C code (unmanaged), which calls a MONO assembly (managed). Although it seems to be quite an academic discussion – let us try this out (just to be aware of the possibilities).

C definitely lacks interoperability routines. Wouldn’t it be nice to have Web services accessible?

As a Web service, the known hello will be used. We will create a C client and a managed assembly that will call a Web service. Let us first create an exposeWS.cs file that will perform a call to a Web service (Listing 7).

Listing 7. C# application that calls a Web service

using System;
class helloStub{
   static void Main(){
      Console.WriteLine("Contacting WebService...");
      helloWS.hello helloService = new helloWS.hello();
 
      String answer = helloService.getHello();
      Console.WriteLine("Web service answer: {0}", answer);
   }
}

And compile it under MONO, by calling:

mcs –r:System.Web.Services exposeWS.cs hello.cs.

As a result, the exposeWS.exe assembly will be created. Now, we will write a client.c file containing a C client (Listing 8). We will compile our C client using gcc and linker parameters obtained from the pkg-config tool:

gcc client.c -o:client .exe

`pkg-config –cflags –libs mono`

Listing 8. C application that uses the MONO application

#include <mono/jit/jit.h>
 
int main(int argc, char* argv[]){
   //Create domain for assembly
   MonoDomain *domain;
   domain = mono_jit_init ("exposeWS.exe");
   if(!domain)error();
 
   //Load assembly into domain
   MonoAssembly *assembly;
   assembly =
      mono_domain_assembly_open(domain, "exposeWS.exe");
   if( !assembly )error();
 
   //Execute assembly
   mono_jit_exec(domain,assembly,0,argv);
 
   //Clean up
   mono_jit_cleanup(domain);
 
   return 0;
}

The last thing to do is to run the client:

>client.exe
Contacting WebService...
Web service answer: Hello (from the Web service)

We have just run a C client that connected with the Web service!

The Trouble

One of the basic functionalities of each programming framework is its ability to debug assemblies run against it. As is normal, two features will be examined: tracing and debugging.

Tracing

Tracing outputs a log whenever the assembly has reached a particular point of execution. MONO assemblies can be traced at runtime using the -trace option. Let us try to trace calls to the getHello method from the hello.cs proxy used by the previous exposeWS.exe assembly.

> mono –trace=’M:helloWS.hello:getHello’ exposeWS.exe
Contacting WebService...
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
Web service answer: Hello (from the Web Service)
ENTER: helloWS.hello:getHello ()
   (this:0x80d8f00[helloWS.hello exposeWS.exe], )
LEAVE: helloWS.hello:getHello ()
   [STRING:0x83bf9b0:Hello (from the Web Service)]

Please note that a -trace=’M:helloWS.hello:getHello’ switch has been added to the mono command line. The switch specifies that the method getHello from the class hello stored in the namespace helloWS will be traced. Some exceptions have been logged – four lines such as EXCEPTION handling: FormatException have been outputted. Let us try to check where they were thrown. To do this, we will trace all calls to the helloWS namespace.

> mono –trace=’N:helloWS’ exposeWS.exe
Contacting WebService...
ENTER: (wrapper remoting-invoke-with-check)
   helloWS.hello:.ctor ()
   (this:0x80d8f00[helloWS.hello exposeWS.exe], )
. ENTER: helloWS.hello:.ctor ()
   (this:0x80d8f00[helloWS.hello exposeWS.exe], )
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
Web service answer: Hello (from the Web Service)
. LEAVE: helloWS.hello:.ctor ()
LEAVE: (wrapper remoting-invoke-with-check)
   helloWS.hello:.ctor ()
ENTER: helloWS.hello:getHello ()
   (this:0x80d8f00[helloWS.hello exposeWS.exe], )
LEAVE: helloWS.hello:getHello ()
   [STRING:0x83c1a00:Hello (from the Web Service)]
Contacting WebService...
...
LEAVE: helloWS.hello:getHello ()
   [STRING:0x83bf9b0:Hello (from the Web Service)]

Please note that a -trace=’N:helloWS’ switch has been added to the mcs command line. As has been seen, exceptions were logged in the hello class constructor. Since this one does nothing special (Listing 9), we can suspect that the exception was thrown somewhere in the super class constructor. Let’s try to trace the whole namespace first:

Listing 9. hello class constructor

public class hello :
   System.Web.Services.Protocols.SoapHttpClientProtocol {
   public hello () {
      this.Url = "http://localhost:8080/hello.asmx";
   }
}
> mono –trace=’N: System.Web.Services.Protocols’ exposeWS.exe
Contacting WebService...
ENTER: System.Web.Services.Protocols.SoapHttpClientProtocol:
   .ctor ()(this:0x80d8f00[helloWS.hello exposeWS.exe], )
. ENTER: System.Web.Services.Protocols.HttpWebClientProtocol:
   .ctor ()(this:0x80d8f00[helloWS.hello exposeWS.exe], )
...
. . . . . . ENTER: System.Web.Services.Protocols.SoapExtension:
               InitializeGlobalExtensions ()()
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
EXCEPTION handling: FormatException
. . . . . . LEAVE: System.Web.Services.Protocols.SoapExtension:
               InitializeGlobalEx

As can be seen, the exception occurred in the method InitializeGlobalExtensions() of the class System.Web.Services.Protocols.SoapExtension. So – is it a bug? Actually, no – as the logger output – not an exception, but an act of exception handling happened. An exception was thrown, but also caught (and handled).

The full list of tracing options is accessible by calling: mono –help-trace.

One more thing should be pointed out here, and that is that the presented code does not contain any TRACE methods at all. The output has been generated on the basis of the operational stack.

Trace switches

Another feature that is worth mentioning, common to both MONO and .NET, are trace switches. The idea is quite clear: to enable output trace information being toggled on or off, by setting the proper flag in the external configuration file. This can be used whenever a deployed application requires some investigation at some future period as to what is actually happening during execution. An external flag can be set, trace logs gathered, and after that – unset again (and no logs will be output anymore).

Let us update exposeWS.cs as in Listing 10. First of all, a BooleanSwitch has been created and named as infoWS. Because the output of the trace must be specified, a trace listener has been set-up to use the default console. Furthermore, several Trace.Writeif instructions have been added in order to clarify the execution process. They will output values to the standard console only if the switch sw is enabled.

Listing 10. C# application with trace switches

using System;
using System.Diagnostics;
 
class helloStub{
 
private static BooleanSwitch sw =
   new BooleanSwitch("infoSW", "Info trace:");
 
   static void Main(){
      Trace.Listeners.Add(
         new TextWriterTraceListener(Console.Out));
 
      Console.WriteLine("Contacting WebService...",);
 
      Trace.WriteIf(
         sw.Enabled, "Instantializing Web service proxy...");
      helloWS.hello helloService = new helloWS.hello();
 
      Trace.WriteIf(
         sw.Enabled, "OK\r\nQuerying for hello string...");
 
      String answer = helloService.getHello();
 
      Trace.WriteIf(
         sw.Enabled, "OK\r\nDisplaying hello string...\r\n");
 
      Console.WriteLine("Web service answer: {0}", answer);
 
      Trace.WriteIf(infoSW.Enabled, "OK\r\nLeaving...");
   }

As previously pointed out, an external configuration file will be required here (exposeWS.exe.config; Listing 11).

The configuration file defines a switch named infoWS and sets its value to 0 (please note the similarities between the BooleanSwitch constructor parameters and the add element name attribute in the configuration file). The configuration file must be stored in the same directory as the assembly. Furthermore, configuration files must be named as: <assembly_name_with_extension ><.config>. E.g., for exposeWS.cs compiled into exposeWS.exe, the configuration file must be named exposeWS.exe.config.

Let us try to compile:

>mcs –r:System.Web.Services.dll –d:TRACE exposeWS.cs hello.cs

Please note that the -d:TRACE option has been added to the mcs command line. This is because trace instructions are, by default, removed from the code during compilation. The -d option will prevent this.

Run by calling: mono exposeWS.exe

Anything changed? Of course not – we specified the infoSW value to be 0 (false). Let’s try setting that value to 1.

By executing the application using mono exposeWS.exe, we will see several additional lines added by Trace.WriteIf() calls.

Listing 11. Application configuration file with trace switches definition

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   <system.diagnostics>
      <switches>
         <add name="infoSW" value="0"/>
      </switches>
   </system.diagnostics>
</configuration>

Trace switches

Trace switches can be of two types:

  • BooleanSwitch, which can be set to true or false.
  • TraceSwitch, which can be set to one of the following:
    • 0 – off, no tracing will be outputted.
    • TraceError (1)
    • TraceWarning (2)
    • TraceInfo (3)
    • TraceVerbose (4) – all traces will be outputted

Debugging

OK, now we know enough about tracing and trace switches, but what about debugging? At this point, I am afraid that I have to disappoint everyone who just started to be potential MONO freaks. According to the MONO manual, in order to run an assembly in debug mode, it must first be compiled with the --g option that generates *.gdb files that contain debugger symbols. Under Windows, such attempts result in the error that Mono.CSharp.Debugger.dll cannot be found. This is logged as one of the known bugs and it is supposed to be fixed in newer releases.

The Linux version of mcs from MONO 1.0 (also MONO 1.0.1 or MONO 1.0.2) also cannot generate debugger symbols. The output assembly is bigger than the one compiled without them, but this is all that actually happens.

The MONO team promised to provide a debugger by the beginning of 2004. I will keep you informed about the progress of this work.

Debugger for MONO

There are some ways around these problems (e.g., use of gdb under Linux), but I have concentrated on the package as-is.

An interesting initiative was started by Martin Baulig. He wrote a debugger that can be downloaded from here. The current version is 0.9. The last version of IDE that was released, was version 0.4; the next releases will integrate with MonoDevelop.

The Meaning of Life

Based on the considerations listed in this article, several conclusions can be drawn:

  • MONO provides a Framework within which applications can be developed. It enables applications to be compiled and executed.
  • The MONO Framework is implemented for several Unix / Linux platforms, which enables assemblies to be truly portable. MONO Framework packages are available for Windows, SuSe, RedHat, Debian, Mac OS, and OS/2.
  • Unfortunately, MONO does not implement full CLI functionality, which puts the choice of MONO as a development platform in question.
  • Windows Forms are not fully implemented yet (unstable) – GTK# must be used instead.
  • Code Access Security is missing.
  • If used, namespaces common for .NET and MONO applications compiled under MONO can be executed against either .NET or MONO Frameworks (still working under Linux).
  • System.Data is also not fully ported, especially SQLClient used for communication with Microsoft SQL Server databases.
  • The support for debugging assemblies is very poor. Actually – for now – it is only limited to tracing. We have to wait for the next releases.
  • C# is fully supported by the MONO Framework. VB.NET is marked as unstable.
  • Glade can be used for building a GUI defined in external XML files.
  • A rich number of commercial and open source database engines are supported.

Still, we have to take into account that MONO is under development. It is a promising alternative where portability is required. However, current releases can be used only for smaller projects.

MONO components

The list below presents the status of particular modules:

Stable:

  • Commons.RelaxNG
  • Cscompmgd
  • Mono.Data
  • Mono.Data.Tds
  • Mono.Posix
  • Mono.Security
  • Mono.Security.Win32
  • System.Web
  • System.Configuration.Install
  • System.Data
  • System.Data.OracleClient
  • System.DirectoryServices
  • System
  • System.Drawing
  • System.Runtime.Remoting
  • System.Security
  • System.Web.Services
  • System.XML

Unstable:

  • Accessibility
  • Mono.Cairo
  • Mono.CSharp.Debugger
  • Mono.Data.DB2Client
  • Mono.Data.SqlLite
  • Mono.Data.SybaseClient
  • Mono.GetOptions
  • System.Web.Mobile
  • System.Design
  • System.Drawing.Design
  • System.Windows.Forms
  • Formatters.Soap
  • Mono.Data.TdsClient (older Sybase and MS SQL)

Missing:

  • System.EnterpriseServices
  • System.Management
  • System.Messaging
  • System.ServiceProcess
  • System.Web.RegularExpressions

Third party assemblies included:

  • ByteFX.Data
  • Npgsql
  • PEAPI
  • SharpZipLib.
  • Java integration with IKVM.NET

Languages

Stable:

  • C#

Unstable:

  • VB.NET

Not ready:

  • JScript

On the 'Net:

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here