| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- * Embedding the Mono runtime, preliminary version
- 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
- Slides for Paolo's presentation at .NET ONE on the embedding
- API are available here: <a
- href="http://primates.ximian.com/~lupus/slides/embed">Hosting the Mono
- Runtime</a>. You can also get his <a
- href="http://primates.ximian.com/~lupus/slides/embed/Mono-0.01.tar.gz">sample
- Mono module for Perl</a>
- Authors: Paolo Molaro, Miguel de Icaza.
- * Embedding the runtime.
- Embedding the runtime consists of various steps:
- <ul>
- * Compiling and linking the Mono runtime
- * Initializing the Mono runtime
- * Optionally expose C code to the C#/CIL universe.
- </ul>
- 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:
- <pre>
- pkg-config --cflags --libs mono
- </pre>
- Like this:
- <pre>
- gcc sample.c `pkg-config --cflags --libs mono`
- </pre>
- You can separate the compilation flags from the linking flags, for
- instance, you can use the following macros in your makefile:
- <pre>
- CFLAGS=`pkg-config --cflags mono`
- LDFLAGS=`pkg-config --libs mono`
- </pre>
- ** Initializing the Mono runtime
- To initialize the runtime, call mono_jit_init, like this:
- <pre>
- MonoDomain *domain;
- domain = mono_jit_init ("domain-name");
- </pre>
- 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:
- <pre>
- void *user_data = NULL;
- mono_runtime_exec_managed_code (domain, main_thread_handler, user_data);
- </pre>
- Where your main_thread_handler can load your assembly and execute it:
- <pre>
- static void main_thread_handler (gpointer user_data)
- {
- MonoAssembly *assembly;
- assembly = mono_domain_assembly_open (domain, "file.dll");
- if (!assembly)
- error ();
- </pre>
- 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:
- <pre>
- retval = mono_jit_exec (domain, assembly, argc - 1, argv + 1);
- </pre>
- 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:
- <pre>
- mono_jit_cleanup (domain);
- </pre>
- ** 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:
- <pre>
- mono_add_internal_call ("Hello::Sample", sample);
- </pre>
- Now, you need to declare this on the C# side:
- <pre>
- using System;
- using System.Runtime.CompilerServices;
- </pre>
- <pre>
- class Hello {
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- extern static string Sample ();
- }
- </pre>
- Since this routine returns a string, here is the C definition:
- <pre>
- static MonoString*
- Sample ()
- {
- return mono_string_new (mono_domain_get (), "Hello!");
- }
- </pre>
- 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:
- <ul>
- * Obtaining the MonoMethod handle to the method.
- * The method invocation.
- </ul>
- ** Obtaining a MonoMethod
- To get a MonoMethod there are several ways.
- You can get a MonoClass (the structure representing a type)
- using:
- <pre>
- MonoClass *
- mono_class_from_name (MonoImage *image, const char* name_space, const char *name);
- </pre>
- 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:
-
- <pre>
- "System.Object:GetHashCode()"
- </pre>
-
- and create a MonoMethodDesc out of it with:
-
- <pre>
- MonoMethodDesc* mono_method_desc_new (const char *name, gboolean include_namespace);
- </pre>
-
- You can then use:
-
- <pre>
- MonoMethod* mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass);
- MonoMethod* mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image);
- </pre>
-
- 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:
-
- <pre>
- 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);
- </pre>
-
- 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
- reference.
-
- We have plans for providing an additional method that returns
- an unmanaged->managed thunk like this:
-
- <pre>
- void* mono_method_get_unmanaged_thunk (MonoMethod *method);
- </pre>
-
- You'll be able to store the returned pointer in a function
- pointer with the proper signature and call that directly from
- C:
-
- <pre>
- typedef gint32 (*GetHashCode) (MonoObject *obj);
-
- GetHashCode func = mono_method_get_unmanaged_thunk (System_Object_GetHashCode_method);
-
- gint32 hashvalue = func (myobject);
- </pre>
-
- 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.
|