Mono Embedded – Your Own .NET Plugin

I’ve embedded either for fun or work a number of languages to see what hosting them in C is like – both for performance and ease of marshalling. So far I’m impressed with the internals of Mono, but feel like there’s a lack of proper GOTCHA’s documented anywhere. Some are spread out around the web, but most don’t directly state key points. As such, I’m going to be posted a few as I come across them.

WARNING: Some of this may not make complete sense without reading http://www.mono-project.com/Embedding_Mono first.

Creating Objects
Like the above docs day, all you really need to do is:

/* get handle for class from assembly */
MonoImage *image = mono_assembly_get_image(MonoAssembly*);
MonoClass *klass = mono_class_from_name(image, "Namespace", "Class");

/* instantiate class and call .ctor (as they're nothing more than methods) */
MonoObject *obj = mono_object_new(MonoDomain*, klass);
mono_runtime_init(obj); /* call .ctor w/out args */

Reusing Objects
If you need to reuse this new MonoObject instance EVER I would highly suggest calling mono_gchandle_new(MonoObject*, pinned) otherwise the GC will get to it before you want it again – I GUARANTEE THIS. The reason this happens is because objects created from your C code have no real “SCOPE”, so they’re collected quickly just like they were local variables in a method. Read the following link, it’s helpful:

http://www.go-mono.org/docs/index.aspx?link=xhtml%3Adeploy%2Fmono-api-gchandle.html

As a side note, you can actually call methods on objects that have been collected as long as the methods usage of `this` is only limited to `this.GetType()` (or nearly static). Try it, you’ll get some really funny results!

Finding Methods
The MonoClass* struct contains (or points to) lots of lists of data. The methods of a class are essentially one big long list and each method (every single variation) is a different instance of MonoMethod*. Take a look at the following C# class:

namespace Test
{
	public class Foo
	{
		public Foo()
		{
		}

		public void Log(int num)
		{
		}

		public void Log(string msg)
		{
		}
	}
}

NOTICE: The Log method is overloaded!

One way to call Foo.Log(?) is like so:

/* get handle for class from assembly */
MonoImage *image = mono_assembly_get_image(MonoAssembly*);
MonoClass *klass = mono_class_from_name(image, "Namespace", "Class");

/* instantiate class and call .ctor (as they're nothing more than methods) */
MonoObject *obj = mono_object_new(MonoDomain*, klass);
mono_runtime_init(obj); /* call .ctor w/out args */

/* Get Log method from MonoClass that takes 1 argument
MonoMethod *m = mono_class_get_method_from_name(klass, "Log", 1);

/* create single argument */
void *args[1] = { mono_string_new(MonoDomain*, "Hello World"); };

/* last argument is to capture exceptions */
mono_runtime_invoke(m, obj, args, NULL);

Any guess what might have just happened? Well, we just passed a string to the Log(int) method because mono_class_get_method_from_name returns the first one it finds, and not the one based on the type. To fix this you could write your own method which loops through MonoMethod*’s and find the correct signature or use the helper methods:

#include <mono/metadata/debug-helpers.h>

MonoMethodDesc *d = mono_method_desc_new(":Log(int)", FALSE);
MonoMethod *m = mono_method_desc_search_in_class(d, klass);
mono_method_desc_free(d);

The mono_method_desc_* functions are extremely helpful and remove some of the crazy and changing internals – so I’d suggest using them for much of your calls.

Here are some further examples of the string descriptor for mono_method_desc_new:

  • Namespace.Test:Log(string) -> find Log method in Test class (requires second argument to be TRUE)
  • :Log(string) -> find Log method which takes a string
  • :Log(XmlReader) -> find Log method which takes an XmlReader
  • :Log(My.Namespace.XmlReader) -> find the method which takes YOUR funky XmlReader

As you can see it covers the gamut – so thank you to whomever created that extremely useful class/struct/function-set.

~ Till next time!