Embedding Mono

This document describes how to embed the Mono runtime in your application, and how to invoke managed methods from C, and how to invoke C code from managed code.

For a general overview of why you would like to embed Mono in your application see the Scripting With Mono article.

Source Code

Source code and samples for Mono embedding can be found in the Mono distribution in the mono/samples/embed directory.

How Embedding Works

Typically you would begin with an existing C application:

Existing.png

Embedding links the mono runtime with your application, so your application now has a full virtual execution system running side-by-side with it. This is done by linking `libmono’ with your application (we will cover the details about this later). Once linked, the address space of your application would look like this:

Linked.png

The Mono embedded API exposes the Mono Runtime to the existing C code. The interface exposed by the Mono runtime lets the developer control various aspects of the runtime and inspect the code that runs on the CIL world inside the Mono runtime.

Once you have the Mono runtime initialized, the most interesting thing to do is to load some CIL/.NET code into it. The code can be written in any of the Mono supported languages like C#, Java, IronPython or Visual Basic. This will result in an address space like this:

Loaded.png

The C code is typically referred as unmanaged code, while the CIL code generated by a CIL-compiler is referred to as managed code.

But to make the system more interesting than just load some managed code and have it run side-by-side with your code, it is desirable to have the managed code invoke C code, and the C code invoke managed code.

Managed code can invoke unmanaged code in two ways, using P/Invoke or using the low-level Mono embedding API.

Now your existing C code can trigger methods in the managed world, and the managed world can react and notify of any interesting changes to the C code:

The loaded assembly can be as simple as you want, some common things that developers have done:

  • Load a library of methods that are wired to the user interface of an application: GUI elements, dialog boxes are then handled on the managed world, while core processing remains in C.
  • Load user defined code as assemblies, and trigger invocations of those from the existing C code base.
  • Move some of the development to the managed world, gaining all of the benefits of managed development (exception handling, runtime type checking, just-in-time compilation, rich introspection system, type-safe libraries and more) while keeping your existing investment in C intact.
  • A launcher that launches background threads to perform some work on behalf of the application.
  • Embed a web application or a web service server inside your application.
  • Use Mono to host the user scripting interface, turning Mono and the scripts into a generic plugin interface.
  • Integrate the Mono object system with third-party object systems.

Since the Mono framework is a fairly powerful framework there are almost no limitations on the different kind of applications that you can create with the above setup.

Embedding the Runtime

Check the reference guide for details on the API.

Embedding the runtime consists of various steps:

  • Compiling and linking the Mono runtime
  • Initializing the Mono runtime
  • Optionally expose C code to the C#/CIL universe.

These are discussed in detail next.

Compiling and Linking

To embed the runtime, you have to link your code against the Mono runtime libraries. To do this, you want to pass the flags returned by pkg-config to your compiler:

pkg-config --cflags --libs mono-2

is used to get the flags for the JIT runtime.

Like this:

gcc sample.c `pkg-config --cflags --libs mono-2`

You can separate the compilation flags from the linking flags, for instance, you can use the following macros in your makefile:

CFLAGS=`pkg-config --cflags mono-2`
LDFLAGS=`pkg-config --libs mono-2`

For mono versions before 2.8, use “mono” instead of “mono-2” in the above commands: the ABI version of the libmono library has changed.

On Windows, depending on the GC implementation to be used (Boehm or SGen), you need to link your application with mono-2.0.lib or monosgen-2.0.lib.

Initializing the Mono runtime

To initialize the JIT runtime, call mono_jit_init, like this:

#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>

MonoDomain *domain;

domain = mono_jit_init (domain_name);

That will return a MonoDomain where your code will be executed. domain_name is the name of the main application domain. This call will initialize the default framework version, which could be 2.0 or 4.0, depending on the Mono version used. To specify a framework version, you can use something like:

domain = mono_jit_init_version ("myapp", "v4.0.30319");

The first thing you usually do is to load your assembly and execute it:

MonoAssembly *assembly;

assembly = mono_domain_assembly_open (domain, "file.exe");
if (!assembly)
  error ();

In the above example, the contents of `file.exe’ will be loaded into the domain. This only loads the code, but it will not execute anything yet. You can replace `file.exe’ with another transport file, like `file.dll’.

To start executing code, you must invoke a method in the assembly, or if you have provided a static Main method (an entry point), you can use the convenience function:

retval = mono_jit_exec (domain, assembly, argc - 1, argv + 1);

And execute c program like:

./c_program file.exe

mono_jit_exec expects the assembly file name in argv[0], so we adjust c argc and argv to respect the requirement.

Make sure you always provide a Main() method and execute it with mono_jit_exec() at startup: this sets up some additional information in the application domain, like the main assembly and the base loading path. You will be able to execute other methods even after Main() returns.

If you want to invoke a different method, look at the Invoking Methods in the CIL universe section.

Certain features of the runtime like Dll remapping depend on a configuration file, to load the configuration file, just add:

mono_config_parse (NULL);

Which will load the Mono configuration file (typically /etc/mono/config), but if you want to load your own configuration file, pass the filename as the argument to mono_config_parse:

mono_config_parse ("my_mappings");

Configuring the Runtime

When Mono is embedded into an application it needs a way of finding its runtime assemblies and configuration files. By default it will use the system defined locations that the runtime was built with (typically assemblies in /usr/lib/mono and configuration in /etc/mono). This will work out of the box for you.

But if you are using a Mono that was relocated from an original distribution, for example if you are distributing your application with Mono, you must inform the Mono runtime where to find its assemblies and configuration files. To do so, you must call the mono_set_dirs routine:

mono_set_dirs (myapp_lib, myapp_etc);

In Windows this is common since you will install your Mono on a different directory, so for example:

mono_set_dirs("C:\\Mono-2.6.7\\lib","C:\\Mono-2.6.7\\etc");

Shutting down the runtime

To shutdown the Mono runtime, you have to clean up all the domains that were created, use this function:

mono_jit_cleanup (domain);

Note that for current versions of Mono, the mono runtime can’t be reloaded into the same process, so call mono_jit_cleanup() only if you’re never going to initialize it again.

Exposing C code to the CIL universe

The Mono runtime provides two mechanisms to expose C code to the CIL universe: internal calls and native C code. Internal calls are tightly integrated with the runtime, and provide no support for marshalling between runtime types and C types.

For example, passing a C# string will result into a MonoString* in the C function when using an internal call (that is, it will be a pointer to the managed heap object representing the string). A C# string passed to a P/Invoke C function will result instead in, for example, a utf8 char pointer, depending on the marshalling attributes.

The other option is to use the Platform Invoke (P/Invoke) to call C code from the CIL universe, using the standard P/Invoke mechanisms.

To use the P/Invoke system, you have to make your C function public, for example:

void DoSomething ()
{
  /* ... */
}

Since this method tends to be an unuses symbol in the C code, instruct the linker to add all symbols to the dynamic symbol table, not just the used ones.

gcc sample.c -rdynamic `pkg-config --cflags --libs mono-2`

To make the runtime then lookup this symbol in the current executable, use the special library name __Internal like this, in your DllImport attribute:

using System.Runtime.InteropServices;

[DllImport ("__Internal", EntryPoint="DoSomething")]
static extern void DoSomething ();

The “__Internal” library name will instruct Mono not to look this up in an external library, but to try to satisfy the symbol referenced (DoSomething) in the current executable image.

The P/Invoke framework provides extensive marshalling capabilities (converting strings, converting data types, mapping delegates to function pointers and much more). This is the simplest mechanism to use.

If you want direct access to managed objects you can register C code with the runtime, and later bind to it from managed code.

To register an internal call, use this call in the C code:

mono_add_internal_call ("Hello::Sample", sample);

Now, you need to declare this on the C# side:

using System;
using System.Runtime.CompilerServices;

class Hello {
  [MethodImplAttribute(MethodImplOptions.InternalCall)]
  extern static string Sample ();
}

Since this routine returns a string, here is the C definition:

static MonoString*
Sample ()
{
  return mono_string_new (mono_domain_get (), "Hello!");
}

Notice that we have to return a `MonoString’, and we use the `mono_string_new’ API call to obtain this from a string.

Windows Considerations

On Windows, it is necessary for you to flag any methods that you want to expose through P/Invoke to be flagged with __dllexport or __declspec(dllexport).

Updates for Mono version 2.8+

To support advanced GC implementations, implement a few optimizations and to do a much needed cleanup, the API and ABI exposed by the Mono library starting with version 2.8 has changed. Most of the API remains unchanged, but a few tweaks may be needed in the build setup and in the code. As much as possible the changes needed will allow the code to compile with both API versions.

The first thing to change is to use mono-2 instead of mono in the pkg-config invocations. To get the preprocessor flags to use during compilation, for example, you’ll use:

pkg-config --cflags mono-2

Please also note that mono may not use GLib anymore internally, so if you use it in your program, make sure you also link to it explicitly. No other change to the build setup should be needed.

Now let’s list the changes that may be needed to the source code.

  • The glib.h header file is no longer included in the mono header files, so if you use GLib and relied on mono including the header for you, you’ll need to explicitly include the glib.h header now.

  • A few functions in the API return a pointer to allocated memory: this memory must be freed with mono_free() instead of g_free().

FIXME: provide a list or a small script to allow people to easily grep for them in their code.

  • A few internal headers are not installed anymore. You should simply remove the inclusion on those headers. This includes headers in the mono/utils directory (except mono-logger.h and the newly added mono-publib.h), headers in the mono/io-layer directory and mono/metadata/mempool.h.

  • The MonoType struct is no longer fully visible: if you have code that accesses its fields directly, you need to change it to use the accessors that are already provided also in the 1.0 version of the mono API.

For example, if you accessed the byref flag of the type using:

type->byref

you need to change the code to read:

mono_type_is_byref (type)
  • The MonoMethodSignature struct is no longer fully visible: if you have code that accesses its fields directly, you need to change it to use the accessors that are already provided also in the 1.0 version of the mono API.

For example, if you accessed the return type of the signature using:

signature->ret

you need to change the code to read:

mono_signature_get_return_type (signature)
  • The MonoArray and MonoString structures are no longer visible.

If you accessed the fields directly, you need to change the code to use the macros and functions already part of the API.

  • The MonoAssemblyName struct is no longer fully visible: to access its fields

you need to use the newly-provided accessors. Note also that it can’t be allocated on the stack anymore and you’ll need to create and destroy it with the following API:

MonoAssemblyName *aname = mono_assembly_name_new ("mscorlib");
mono_assembly_name_free (aname);
  • The MonoMethodHeader object is now transient: you need to free it when you’re done to avoid leaking by calling the (already existing and safe to call) function mono_metadata_free_mh ().

  • Array API changes: the integer types that represent array lengths and array boundaries has changed to uintptr_t and intptr_t. This allows us to transparently provide a mono build that supports 64 bit indices on 64 bit systems. The affected APIs are:

mono_array_new ()
mono_array_new_full ()
mono_array_new_specific ()
mono_array_length ()
  • Profiler API changes.

Mono can now support multiple profilers running at the same time. The profiler initialization sequence must first install the profiler and only after that install the callbacks and set the event mask.

Also note that objects can move in memory. Use the

void mono_profiler_install_gc_moves    (MonoProfileGCMoveFunc callback);

function to get notifications of the old and new addresses of objects as they are moved.

  • The mono logger API now exposes only the ability to set the trace level and trace mask with the two functions:
void mono_trace_set_mask_string (const char *value);
void mono_trace_set_level_string (const char *value);
  • The following methods and structures (which were really unlikely to be used/useful to anyone outside of the mono internals) are not part of the public API anymore:
mono_remote_class() MonoRemoteClass
ves_icall_System_Environment_GetOSVersionString()
mono_alloc_special_static_data()/mono_get_special_static_data()

Invoking Methods in the CIL universe

Calling a method in the CIL universe from C requires a number of steps:

  • Obtaining the MonoMethod handle to the method.
  • The method invocation.

To get a MonoMethod there are several ways.

You can get a MonoClass (the structure representing a type) using:

MonoImage *
mono_assembly_get_image  (MonoAssembly *assembly);

MonoClass *
mono_class_from_name (MonoImage *image, const char* name_space, const char *name);

and then loop in the returned class method array until you get the one you’re looking for. There are examples of such searches as static functions in several C files in metadata/*.c: we need to expose one through the API and remove the duplicates.

The other, simpler, way is to use the functions in debug-helpers.h: there are examples of their use in monograph, mint and the jit as well. You basically use a string description of the method, like:

“System.Object:GetHashCode()”

and create a MonoMethodDesc out of it with:

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

MonoMethodDesc* mono_method_desc_new (const char *name, gboolean include_namespace);

You can then use:

MonoMethod*     mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass);
MonoMethod*     mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image);

and

#include <mono/metadata/assembly.h>

MonoImage    *mono_assembly_get_image  (MonoAssembly *assembly);

to search for the method in a class or in an image. You would typically do this just once at the start of the program and store the result for reuse somewhere.

Invoking a Method

You can invoke methods either with Unmanaged to Managed Thunks which create a custom version of an invocation method or by using the mono_runtime_invoke () methods.

The unmanaged to managed thunks are faster because they will create a custom trampoline from unmanaged code to managed code for the particular method signature that you are invoking. The mono_runtime_invoke methods can be used with any arguments and any method signatures.

If you are doing a few invocations, mono_runtime_invoke will probably suffice, but if you are adding this to a code that in your code that requires the best possible performance, you will want to use the thunks.

There are two functions to call a managed method:

MonoObject*
mono_runtime_invoke   (MonoMethod *method, void *obj, void **params,
                       MonoObject **exc);

and

MonoObject*
mono_runtime_invoke_array   (MonoMethod *method, void *obj, MonoArray *params,
                             MonoObject **exc);

obj is the ‘this’ pointer, it should be NULL for static methods, a MonoObject* for object instances and a pointer to the value type for value types.

The params array contains the arguments to the method with the same convention: MonoObject* pointers for object instances and pointers to the value type otherwise. The _invoke_array variant takes a C# object[] as the params argument (MonoArray *params): in this case the value types are boxed inside the respective reference representation.

From unmanaged code you’ll usually use the mono_runtime_invoke() variant.

Note that this function doesn’t handle virtual methods for you, it will exec the exact method you pass: we still need to expose a function to lookup the derived class implementation of a virtual method (there are examples of this in the code, though).

You can pass NULL as the exc argument if you don’t want to catch exceptions, otherwise, *exc will be set to the exception thrown, if any. if an exception is thrown, you can’t use the MonoObject* result from the function.

If the method returns a value type, it is boxed in an object.

For example, to invoke the following C# methods:

class MyClass {
  static void Foo (int value) {
    ...
  }

  int Bar (string name) {
    ...
  }
}

assuming you got the corresponding MonoMethod* in foo_method and bar_method and this_arg is a MonoObject* of type MyClass, you simply execute:

/* we execute methods that take one argument */
void *args [1];
int val = 10;
/* Note we put the address of the value type in the args array */
args [0] = &val;

/* execute Foo (10);
 * it's a static method, so use NULL as the second argument.
 */
mono_runtime_invoke (foo_method, NULL, args, NULL);

/* a string is a reference, so we put it directly in the args array */
args [0] = mono_string_new (domain, "Hello");
/* execute my_class_instance.Bar ("Hello");
 * See the Creating Objects section to learn how to get this_arg.
 */
MonoObject *result = mono_runtime_invoke (bar_method, this_arg, args, NULL);
/* we always get a MonoObject* from mono_runtime_invoke (), so to get
 * the integer value we need to unbox (which returns a pointer to
 * the value stored in the object) and dereference.
 */
int int_result = *(int*)mono_object_unbox (result);

Creating objects

Creating an object involves two separate actions: allocating the memory and invoking the constructor.

For constructors that take no arguments this is very simple:

/* we usually get the class we need during initialization */
MonoImage *image = mono_assembly_get_image (assembly);
MonoClass *my_class = mono_class_from_name (image, "MyNamespace", "MyClass");
...
/* allocate memory for the object */
MonoObject *my_class_instance = mono_object_new (domain, my_class);
/* execute the default argument-less constructor */
mono_runtime_object_init (my_class_instance);

For more complex constructors or if you want to have more control of the execution of the constructor, you can use mono_runtime_invoke() as explained in the previous section, after getting the MonoMethod* representing the constructor:

/* execute my_class_instance = new MyClass ("Mono rocks"); */
MonoObject *my_class_instance = mono_object_new (domain, my_class);
void *args [1];
args [0] = mono_string_new (domain, "Mono rocks");
/* constructor methods return void, so we ignore the return value,
 * the constructed object is my_class_instance.
 */
mono_runtime_invoke (ctor_method, my_class_instance, args, NULL);

Data types

Unlike PInvoke, there is no intermediate layer that translates the managed types into unmanaged typed or the other way around. With the embedded runtime, when you register an internal call, or when you call a method, you need to use the data types expected by the runtime.

This means that you need to convert your C types into Mono runtime types before you can pass them to Mono, or you must convert those Mono types to C types before you can consume them”

C Type Mono Type C to Mono Mono to C
strings, char * MonoString * mono_string_new, mono_string_new_len, mono_string_new_wrapper, mono_string-new_utf16 mono_string_to_utf8, mono_string_to_utf16
array of x MonoArray * mono_array_new, mono_array_new_full, mono_array_new_specific  

See the embedded API documentation for more details about these.

Unmanaged to Managed Thunks

With Mono 2.0 we introduced a new function that can wrap a MonoMethod into a function pointer:

void* mono_method_get_unmanaged_thunk (MonoMethod *method);

You’ll be able to store the returned pointer in a function pointer with the proper signature and call that directly from C:

typedef gint32 (*GetHashCode) (MonoObject *obj, MonoException** exc);

GetHashCode func = mono_method_get_unmanaged_thunk (System_Object_GetHashCode_method);

gint32 hashvalue = func (myobject);

Another approach is calling Marshal.GetFunctionPointerForDelegate () either from managed code, or using mono_runtime_invoke ().

C# to C delegate registration

Another common pattern to have C code invoke C# code is to pass C# delegates to C through the Interop with Native Libraries support in Mono.

When you pass a C# delegate to C, the Mono runtime will create the thunk automatically for you. The function received on the C side will be a function pointer that points to a thunk that invokes code in the C# world.

The C# side of the code would look like this:

using System.Runtime.InteropServices;

class Demo {
  delegate int MyCallback1 (int a, int b);

  [DllImport ("MyRuntime")]
  extern static void RegisterCallback (MyCallback1 callback1);

  static int Add (int a, int b) { return a + b; }
  static int Sub (int a, int b) { return a - b; }

  void Init ()
  {
    // This one registers the method "Add" to be invoked back by C code
    RegisterCallback (Add);
  }
}

Then your C code would look like this:

#include <stdlib.h>

typedef int (*callback_t) (int a, int b);
static callback_t my_callback;

void RegisterCallback (callback_t cb)
{
  my_callback = cb;
}

int InvokeManagedCode (int a, int b)
{
  if (my_callback == NULL){
    printf ("Managed code has not initialized this library yet");
    abort ();
  }
  return (*my_callback) (a, b);
}

Threading issues

If your application creates threads on its own, and you want them to be able to interact with Mono, you have to register the thread so that the runtime knows about it.

To do so, call the mono_thread_attach() function before you execute any other Mono API or before manipulating any managed object.

Signal handling

Mono consumes a set of signals during execution that your applications will not be able to consume, here is what these are:

  • SIGPWR, SIGXCPU: these are used internally by the GC and pthreads.
  • SIGFPE: caught so we can turn that into an exception
  • SIGQUIT, SIGKILL to produce ExecutionEngineException.
  • SIGSEGV: to produce NullReferenceExceptions
  • SIGCHLD: to track the life-cycle of processes (notably System.Diagnostics.Process)

One signal picked at startup time between SIGRTMIN and SIGRTMAX. The signal is picked up by finding a signal in that range which is set to SIG_DFL.

Optionally:

  • SIGUSR2: this is used when –trace=disable is passed on the command line and its used to turn on/off the output of trace.

Currently Mono does not provide a mechanism for signal chaining, but one might be available in the future.

API Documentation

Mono’s API Documentation covers the various APIs that you can invoke from your C/C++ application.

Common Problems

Threads

If your applications has threads that will access Mono, access Mono variables, point to Mono objects, be called back by Mono, these threads must be registered with the Mono runtime using the mono_thread_attach.

Missing functionality

If the embedding API is missing some functionality, you might be able to work around it by invoking managed code using mono_runtime_invoke (), i.e. for creating delegates you can call Delegate.CreateDelegate () etc.

Chicken/Egg

If you have a .NET app which P/Invokes to an unmanaged library, which embeds Mono for basically 100% of its logic, bad things can happen:

Under Linux, libmono is statically linked by default. If you p/invoke a library that it turn is linked against libmono, you’ll end up with 2 runtime instances.

Even if you link libmono dynamically (there is a `configure’ switch for this), you must take care to initialize the runtime only once. This means that you can’t call mono_jit_init/cleanup from the SO.

Samples

See the sample programs in mono/samples/embed for examples of embedding the Mono runtime in your application.

Outdated Presentation

Paolo Molaro did a presentation on embedding the Mono Runtime.