a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
294 lines
8.8 KiB
Plaintext
294 lines
8.8 KiB
Plaintext
|
|
Embedding the Mono runtime, preliminary version
|
|
Miguel de Icaza (miguel@ximian.com),
|
|
Paolo Molaro (lupus@ximian.com)
|
|
|
|
|
|
This document describes how to embed the Mono runtime in your
|
|
application, and how to invoke CIL methods from C, and how to
|
|
invoke C code from CIL. Both the JIT and interpreter can be
|
|
embedded in very similar ways so most of what is described
|
|
here can be used in either case.
|
|
|
|
* IMPORTANT
|
|
|
|
This document is now outdated, see:
|
|
|
|
http://www.mono-project.com/Embedding_Mono
|
|
|
|
For an up-to-date version of this document
|
|
|
|
* Embedding the runtime.
|
|
|
|
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
|
|
|
|
is used to get the flags for the JIT runtime and
|
|
|
|
pkg-config --cflags --libs mint
|
|
|
|
for the interpreted runtime.
|
|
|
|
Like this:
|
|
|
|
gcc sample.c `pkg-config --cflags --libs mono`
|
|
|
|
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`
|
|
LDFLAGS=`pkg-config --libs mono`
|
|
|
|
** Initializing the Mono runtime
|
|
|
|
To initialize the JIT runtime, call mono_jit_init, like this:
|
|
|
|
#include <mono/mini/jit.h>
|
|
|
|
MonoDomain *domain;
|
|
|
|
domain = mono_jit_init ("domain-name");
|
|
|
|
For the interpreted runtime use mono_interp_init instead:
|
|
|
|
#include <mono/interpreter/embed.h>
|
|
|
|
MonoDomain *domain;
|
|
|
|
domain = mono_interp_init ("domain-name");
|
|
|
|
That will return a MonoDomain where your code will be
|
|
executed. You can create multiple domains. Each domain is
|
|
isolated from the other domains and code in one domain will
|
|
not interfere with code in other domains. This is useful if
|
|
you want to host different applications in your program.
|
|
|
|
Now, it is necessary to transfer control to Mono, and setup
|
|
the threading infrastructure, you do this like this:
|
|
|
|
void *user_data = NULL;
|
|
|
|
mono_runtime_exec_managed_code (domain, main_thread_handler, user_data);
|
|
|
|
Where your main_thread_handler can load your assembly and execute it:
|
|
|
|
static void main_thread_handler (gpointer user_data)
|
|
|
|
MonoAssembly *assembly;
|
|
|
|
assembly = mono_domain_assembly_open (domain, "file.dll");
|
|
if (!assembly)
|
|
error ();
|
|
|
|
In the above example, the contents of `file.dll' will be
|
|
loaded into the domain. This only loads the code, but it will
|
|
not execute anything yet. You can replace `file.dll' with
|
|
another transport file, like `file.exe'
|
|
|
|
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);
|
|
|
|
or when using the interpreter use:
|
|
|
|
retval = mono_interp_exec (domain, assembly, argc - 1, argv + 1);
|
|
|
|
If you want to invoke a different method, look at the
|
|
`Invoking Methods in the CIL universe' section later on.
|
|
|
|
** 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);
|
|
|
|
Or in the case of the interpreted runtime use:
|
|
|
|
mono_interp_cleanup (domain);
|
|
|
|
** Applications that use threads.
|
|
|
|
The Boehm GC system needs to catch your calls to the pthreads
|
|
layer, so in each file where you use pthread.h you should
|
|
include the <gc/gc.h> file.
|
|
|
|
If you can not do this for any reasons, just remember that you
|
|
can not store pointers to Mono Objects on the stack, you can
|
|
store them safely in the heap, or in global variables though
|
|
|
|
* 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 have the
|
|
least overhead, as they use the same data types that the
|
|
runtime uses.
|
|
|
|
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 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.
|
|
|
|
* 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.
|
|
|
|
** Obtaining a MonoMethod
|
|
|
|
To get a MonoMethod there are several ways.
|
|
|
|
You can get a MonoClass (the structure representing a type)
|
|
using:
|
|
|
|
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:
|
|
|
|
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);
|
|
|
|
to search for the method in a class or in an image. You would
|
|
tipically do this just once at the start of the program and
|
|
store the result for reuse somewhere.
|
|
|
|
** Invoking a Method
|
|
|
|
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.
|
|
|
|
These functions can be used in both the JIT and the interpreted
|
|
environments.
|
|
|
|
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
|
|
reference.
|
|
|
|
We have plans for providing an additional method that returns
|
|
an unmanaged->managed thunk like this:
|
|
|
|
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);
|
|
|
|
GetHashCode func = mono_method_get_unmanaged_thunk (System_Object_GetHashCode_method);
|
|
|
|
gint32 hashvalue = func (myobject);
|
|
|
|
It may not be possible to manage exceptions in that case,
|
|
though. I need to think more about it.
|
|
|
|
** Threading issues
|
|
|
|
If your application creates threads on its own, and you want them to
|
|
be able to call code into the CIL universe with Mono, you have to
|
|
register the thread with Mono before issuing the call.
|
|
|
|
To do so, call the mono_thread_attach() function before you execute
|
|
any managed code from the thread
|
|
|
|
* Samples
|
|
|
|
See the sample programs in mono/sample/embed for examples of
|
|
embedding the Mono runtime in your application.
|
|
|
|
|