Advanced Tips for Mono

This article focus on programming in C# with Mono. It contains a compilation of extremely useful tips and workarounds, especially for people used to lower level programming, like C programmers. Since the Mono documentation is still far from finished, and I found from my experience that it is still very hard to find information and help with C# issues when using Mono, I compiled a series of tips that I gathered from my experience with Mono.

1st Tip: Another good place to find information.

For most of the people out there, this will save them a few bucks in buying books about C# or a few trips to the public library. For others, this will just save them a few hours Googling. Many of you will find yourselves in the same situation: I only had a IDE (Kate, in my case), Mono and Monodoc. Well, I had Google too. Monodoc is not bad, but is rather insufficient. It is extremely incomplete. So, I found myself peeking at the MSDN site, where they have all the class docs. Here is a direct link for the class docs.

Note: Before you attempt to use any class, method or property, you should check monodoc first. Why? Because even though monodoc does not document everything, it has the pointers to every implemented class, method and property. Even though it does not explain every one of them, it will tell you if they are implemented, not implemented or incomplete. If you come to use XML with your Mono program, you will notice, when you take a look at monodoc, that a few methods and properties in the most important XML classes are not implemented. I’m talking about XmlDocument.GetElementById, for example. This method is marked unfinished and actually, it is. I was not paying much attention to monodoc when I tryed to used it, and I wondered for hours why my program kept crashing with null pointer exception in the call to that method. Then, when “re-browsing” monodoc I noticed the Unfinished tag. I tryed to get a workaround for this. Well, I coudn’t. At least, I didn’t try hard. So beware, that there are a lot of unfinished methods around.

2nd Tip: Before starting to use Mono.

Before starting to use Mono, there is a special tweak you can prepare if you use Linux and have superuser permissions on the machine. I allways found it anoying having to type mono programname.exe everytime I want to run a .Net program. So, I used a trick, that takes advantage of a Linux kernel feature (if you’re used to compiling the kernel, its the Misc Binary Format Support). Remember that you MUST have support for it in the kernel (in general, the distributions come with support for Misc Binary Format activated, but if not, you will have to recompile the kernel).
This trick allows me to call a program just typing ./programname.exe (if you’re using BASH, you won’t have to type much :)). When you’re root, edit your /etc/fstab and add the following line

none /proc/sys/fs/binfmt_misc binfmt_misc defaults 0 0

With this line, the binfmt_misc virtual filesystem will be mounted in /proc, and you will be able to instruct the kernel to interpret different binary file formats. I believe Java programmers have been using this, and some distributions used to come preconfigured to suport this for use with Wine, to allow a user to easily run Win32 binaries.
Now that you’ve edited fstab, you need to mount the filesystem. Just run this in your shell:

mount /proc/sys/fs/binfmt_misc

Don’t worry, you will not need to run this everytime you’re computer boots because it is already in your fstab. Next time your computer boots it will mount it “automagically”.
Now, you have to tell the kernel that there is a special kind of binaries he has to be aware of, and that there is a proper interpreter for them. You can do that by executing the following line in your shell, as root, of course:

echo ':CLR:M::MZ::/opt/gnome/bin/mono:' > /proc/sys/fs/binfmt_misc/register

Notice that you have an MZ in that line. MZ is the identifier for executables in Microsoft OS’s. That MZ stands for Mark Zbikowsky, if i’m not mistaken, which was the responsible for the creation of the original EXE format. For those of you using this trick with Wine, some of you may have noticed that this can interfere with Wine and cause conflicts. There are ways of going around this, although I’m not discussing them here.
Once you’ve done this, try running a .Net program in your shell, just by calling it’s name: ./programname.exe. Didn’t it work nice? If you do not want to have to type this command every time your computer reboots, you should add it to your /etc/rc.d/rc.local (actual file and path may vary across distributions).

3rd Tip: Executing external programs (shelling).

One of the things I needed to do in a small program I made, was to be able to execute an external command. I searched all the Monodoc info, and even went to MSDN. But, I had little luck understanting. I finally found somewhere where it was explained how to do it. Use the following code:

<pre>
System.Diagnostics.Process proc = new System.Diagnostics.Process(); // (1)
proc.EnableRaisingEvents=false; // (2)
proc.StartInfo.FileName= "ls"; // (3)
proc.StartInfo.Arguments = "-lh"; // (4)
proc.Start(); // (5)
proc.WaitForExit(); // (6)

if(0 != proc.ExitCode) Console.WriteLine("Error"); // (7)
</pre>

The code is very easy to understand. You create an object of the Process class of assembly System.Diagnostics (1), you let him know you don’t want any events raised (2) and you let him know the command (3) and the arguments (4). Then you tell him to start executing the program (5) and to wait for its completion (6). When complete, you can test the exit code for errors (7). How different can it be from system("ls -lh");? 😉

4th Tip: Using hashing classes

If you come to need to hash data, for example, digest a password with MD5 to save it, you will need to use the HashAlgorithm class. It is very simple to use. First you need to create an instance of the class for the hash algorithm you need (it can be one of a few, including MD5, SHA1 and others):

HashAlgorithm digest = HashAlgorithm.Create("MD5");

In the preceeding line we created an instance of HashAlgorithm for MD5 “digestion”. If you need to use any other algorithm, just replace the “MD5” for the correct one. I’m not sure if it is imperative to be in uppercase.
Then you can digest the data. ComputeHash expects a byte[], so, assume that buffer is a byte[].

byte [] byteHash = digest.ComputeHash(buffer);

The byteHash variable will then hold the result of the hashing. It is also very probable that you want to apply it to a String object. In that case you will need to convert from a String to a Byte[]. There are three aproaches you can use: 1) you use the ASCIIEncoding encoder; 2) you use the UTF encoders; 3) you create your own encoder that converts literaly the string to a byte[] without performing any conversion.
The ASCIIEncoding and the UTF encoders are already part of the class library, so you do not have to create them. With the ASCIIEncoding it is very easy. Assuming stringToHash is the string you want to hash, do it like this:

<pre>
// create a new encoder instance
ASCIIEncoding encoder = new ASCIIEncoding();

// geting the byte[] array needed for the hashing process
encoder.GetBytes(stringToHash, 0, stringToHash.Length, buffer, 0);
</pre>

You can then use buffer directly with ComputeHash.

But, if you use ASCIIEncoding, you cannot use international characters with it, because that encoder will replace everything above charcode 127 with a question mark. On the other hand, you could do it with UTF encoders like this (example for UTF8):

<pre>
// create a new encoder instance
UTF8Encoding encoder = new UTF8Encoding();

// geting the byte[] array needed for the hashing process
encoder.GetBytes(stringToHash);
</pre>

Notice that with UTF8Encoding you didn’t have to pass all those parameters you had to when you used ASCIIEncoding.
UTF encoders will work but, if you plan on using them, for example, in password challenging in autenticating a connection, you cannot be sure of the encoding used in the other side. It could be anything. In my case it would probably be CP860, ISO-8859-1 or ISO-8859-15. Since the UTF encoders would replace every char above 127 with the UTF-8 code (in the UTF8 encoder case) or every char of the charset with an UTF16 (in the UTF16 encoder case), there would be problems.
So, you’re left with creating your own encoder. I created one which I called NullEncoding, because it doesn’t encode… just converts directly between a string and a byte[], and vice-versa:

<pre>
public class NullEncoding
{
// will give you the bytes from a string
public static byte[] GetBytes(string s)
{
byte [] result = new byte[s.Length];
for(int i = 0; i < s.Length; ++ i) result[i] = Convert.ToByte(s[i]);
return result;
}

// will give you a string from the bytes
public static string GetString(byte [] b)
{
string result = String.Empty;
for(int i = 0; i < b.Length; ++ i) result += Convert.ToChar(b[i]);
return result;
}
}
</pre>

It is a little bit different from the other encodings, but it gets the job done. If you payed attention, you will notice the static in the declaration of the methods. This way we do not need to create an instance of the class to encode our stuff. So we can do just like this:

<pre>
HashAlgorithm digest = HashAlgorithm.Create("MD5");
byte [] byteHash = digest.ComputeHash(NullEncoding.GetBytes(stringToHash));
</pre>

You may also have noticed the Convert.ToByte and Convert.ToChar. If you’re a newbie, check monodoc, as an exercise.

5th Tip: Gtk# – The missing widgets

I started programming Gtk with C#, using Gtk#. When I was using a Gtk.Notebook widget, I wanted to place a container widget (an Hbox, in this case) with an icon and a text label as the label for the sheet tabs. But when I runned my program, which would show the window with the ShowAll() method, I noticed that the widgets in the notebook tab wouldn’t get shown. I tryed using just a text label as sheet tab label, and strange enough, the label alone would get shown. As I didn’t have any experience with Gtk until then, I cannot tell if this is a problem related with Gtk itself or with the Gtk# classes. Apparently, the notebook, for the wigets in the tab labels, was only calling Show(), and not ShowAll(). This resulted in only the base widget being shown. The Hbox was being shown, but not the widgets inside. So, remember to have your program calling ShowAll explicitly for the container widget inside the notebook tabs, if you want to have everything visible.

6th Tip: Gtk# – One thing you cannot do in C

One thing you cannot do in C is to derive a window class to create your window. This because C… has no classes. But in C# you can. In fact, you are advised to do it.
When creating a window, make a class that inherits from Gtk.Window. Create your own constructors, and make them set up the window appearance for you, instead of having it done in Main or somewhere else. Don’t forget to call base() in the constructors to make sure everything is done correctly.
This works, and it is extremely usefull, because it allows you to have your code better organized, and to isolate components, so that you can reuse them (hey, that’s part of why OOP was born :D).
Here’s a short example:

<pre>
public class MyForm: Gtk.Window
{
// replace "someimage.png" with a image path of your choice
public Gtk.Image image = new Gtk.Image("someimage.png");
public Gtk.Label text = new Gtk.Label("Welcome to my form");
HBox hboxBase = new HBox();

// constructor
public MyForm(): base("This is my form")
{
// setting up form aspect
this.DefaultWidth = 600;
this.DefaultHeight = 300;
this.AllowGrow = false;
this.AllowShrink = false;
this.Modal = true;
this.SkipTaskbarHint = true;

this.Add(hboxBase);

hboxBase.PackStart(image, false, false, 0);
hboxBase.PackStart(text, false, false, 0);
}
}
</pre>

If you now do this:

<pre>
MyForm form = new MyForm();
form.ShowAll();
</pre>

You will have a window showing with the title “This is my form”, and in the window body, you will have the image of your choice on the left, and a text label showing “Welcome to my form” on the right. Also notice that it won’t show anything on the taskbar about this window because we’ve disabled it with this.SkipTaskbarHint = true;. Take time to try this and to experiment various combinations of widgets. Also, in monodoc there are some tutorials on Gtk# worth taking a look. Notice that all widgets belonging to that form are inside the class, and that it was not necessary to make the base HBox (hboxBase) public.

That’s all folks. I hope you will enjoy this and find it useful.


About the author:
My name is João Manuel Moura Paredes, and I’m a student at Faculdade de Engenharia da Universidade do Porto (Engineering School of University of Porto) in Portugal. I’m almost 22 years old and have been a programmer for more than a decade. I have experience in several programming languages (specially at low-level programming) and databases. I amexperient a few OS’s, specially in Linux. I am the founder and chair of Chefax I&D student group at my University, and am currently chair of NEACM, our local student chapter for ACM (Association for Computing Machinery).


If you would like to see your thoughts or experiences with technology published, please consider writing an article for OSNews.

60 Comments

  1. 2004-04-21 8:30 pm EST
  2. 2004-04-21 8:48 pm EST
  3. 2004-04-21 9:09 pm EST
  4. 2004-04-21 9:13 pm EST
  5. 2004-04-21 9:20 pm EST
  6. 2004-04-21 9:20 pm EST
  7. 2004-04-21 9:22 pm EST
  8. 2004-04-21 9:25 pm EST
  9. 2004-04-21 9:26 pm EST
  10. 2004-04-21 9:26 pm EST
  11. 2004-04-21 9:33 pm EST
  12. 2004-04-21 9:34 pm EST
  13. 2004-04-21 9:38 pm EST
  14. 2004-04-21 9:52 pm EST
  15. 2004-04-21 9:56 pm EST
  16. 2004-04-21 9:56 pm EST
  17. 2004-04-21 9:58 pm EST
  18. 2004-04-21 10:04 pm EST
  19. 2004-04-21 10:20 pm EST
  20. 2004-04-21 10:24 pm EST
  21. 2004-04-21 10:29 pm EST
  22. 2004-04-21 10:35 pm EST
  23. 2004-04-21 10:40 pm EST
  24. 2004-04-21 10:46 pm EST
  25. 2004-04-21 10:55 pm EST
  26. 2004-04-21 10:57 pm EST
  27. 2004-04-22 12:25 am EST
  28. 2004-04-22 1:35 am EST
  29. 2004-04-22 1:44 am EST
  30. 2004-04-22 3:39 am EST
  31. 2004-04-22 4:46 am EST
  32. 2004-04-22 5:07 am EST
  33. 2004-04-22 1:47 pm EST
  34. 2004-04-22 2:06 pm EST
  35. 2004-04-22 6:45 pm EST
  36. 2004-04-22 9:16 pm EST
  37. 2004-04-22 10:06 pm EST
  38. 2004-04-22 10:10 pm EST
  39. 2004-04-22 10:17 pm EST
  40. 2004-04-22 10:29 pm EST
  41. 2004-04-22 10:32 pm EST
  42. 2004-04-22 11:33 pm EST
  43. 2004-04-22 11:39 pm EST
  44. 2004-04-23 1:21 am EST
  45. 2004-04-23 1:33 am EST
  46. 2004-04-23 1:39 am EST
  47. 2004-04-23 1:45 am EST
  48. 2004-04-23 2:37 am EST
  49. 2004-04-23 3:32 am EST
  50. 2004-04-23 9:18 pm EST
  51. 2004-04-23 9:27 pm EST
  52. 2004-04-24 3:35 pm EST
  53. 2004-04-25 1:21 am EST
  54. 2004-04-25 7:22 am EST
  55. 2004-04-25 7:24 am EST
  56. 2004-04-25 11:33 am EST
  57. 2004-04-25 12:45 pm EST
  58. 2004-04-25 2:47 pm EST
  59. 2004-04-25 3:32 pm EST
  60. 2004-04-25 3:57 pm EST