| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- #include <mono/jit/jit.h>
- #include <mono/metadata/object.h>
- #include <mono/metadata/environment.h>
- #include <mono/metadata/assembly.h>
- #include <mono/metadata/debug-helpers.h>
- #include <string.h>
- #include <stdlib.h>
- /*
- * Simple mono embedding example.
- * We show how to create objects and invoke methods and set fields in them.
- * Compile with:
- * gcc -Wall -o test-invoke test-invoke.c `pkg-config --cflags --libs mono` -lm
- * mcs invoke.cs
- * Run with:
- * ./test-invoke invoke.exe
- */
- static void
- access_valuetype_field (MonoObject *obj)
- {
- MonoClass *klass;
- MonoClassField *field;
- int val;
- klass = mono_object_get_class (obj);
- /* Now we'll change the value of the 'val' field (see invoke.cs) */
- field = mono_class_get_field_from_name (klass, "val");
- /* This time we also add a bit of error checking... */
- if (!field) {
- fprintf (stderr, "Can't find field val in MyType\n");
- exit (1);
- }
- /* Check that val is an int (if you're paranoid or if you need to
- * show how this API is used)
- */
- if (mono_type_get_type (mono_field_get_type (field)) != MONO_TYPE_I4) {
- fprintf (stderr, "Field val is not a 32 bit integer\n");
- exit (1);
- }
-
- /* Note we pass a pointer to the value */
- mono_field_get_value (obj, field, &val);
- printf ("Value of field is: %d\n", val);
- val = 10;
- /* Note we pass a pointer to the value here as well */
- mono_field_set_value (obj, field, &val);
- }
- static void
- access_reference_field (MonoObject *obj)
- {
- MonoClass *klass;
- MonoDomain *domain;
- MonoClassField *str;
- MonoString *strval;
- char *p;
- klass = mono_object_get_class (obj);
- domain = mono_object_get_domain (obj);
- /* Now we'll see that a reference type is handled slightly differently.
- * First, get the MonoClassField representing it.
- */
- str = mono_class_get_field_from_name (klass, "str");
-
- /* No change here, we always pass a pointer */
- mono_field_get_value (obj, str, &strval);
-
- /* get the string in UTF-8 encoding to print it */
- p = mono_string_to_utf8 (strval);
- printf ("Value of str is: %s\n", p);
- /* we need to free the result from mono_string_to_utf8 () */
- g_free (p);
- /* string are immutable, so we need to create a different string */
- strval = mono_string_new (domain, "hello from the embedding API");
- /* Here is the slight difference: for reference types we pass
- * the pointer directly, instead of a pointer to the value.
- */
- mono_field_set_value (obj, str, strval);
- }
- /* Demostrate how to call methods */
- static void
- call_methods (MonoObject *obj)
- {
- MonoClass *klass;
- MonoDomain *domain;
- MonoMethod *method = NULL, *m = NULL, *ctor = NULL, *fail = NULL, *mvalues;
- MonoProperty *prop;
- MonoObject *result, *exception;
- MonoString *str;
- char *p;
- gpointer iter;
- gpointer args [2];
- int val;
- klass = mono_object_get_class (obj);
- domain = mono_object_get_domain (obj);
- /* retrieve all the methods we need */
- iter = NULL;
- while ((m = mono_class_get_methods (klass, &iter))) {
- if (strcmp (mono_method_get_name (m), "method") == 0) {
- method = m;
- } else if (strcmp (mono_method_get_name (m), "Fail") == 0) {
- fail = m;
- } else if (strcmp (mono_method_get_name (m), "Values") == 0) {
- mvalues = m;
- } else if (strcmp (mono_method_get_name (m), ".ctor") == 0) {
- /* Check it's the ctor that takes two args:
- * as you see a contrsuctor is a method like any other.
- */
- MonoMethodSignature * sig = mono_method_signature (m);
- if (mono_signature_get_param_count (sig) == 2) {
- ctor = m;
- }
- }
- }
- /* Now we'll call method () on obj: since it takes no arguments
- * we can pass NULL as the third argument to mono_runtime_invoke ().
- * The method will print the updated value.
- */
- mono_runtime_invoke (method, obj, NULL, NULL);
- /* mono_object_new () doesn't call any constructor: this means that
- * we'll have to invoke the constructor if needed ourselves. Note:
- * invoking a constructor is no different than calling any other method,
- * so we'll still call mono_runtime_invoke (). This also means that we
- * can invoke a constructor at any time, like now.
- * First, setup the array of arguments and their values.
- */
- /* As usual, we use the address of the data for valuetype arguments */
- val = 7;
- args [0] = &val;
- /* and the pointer for reference types: mono_array_new () returns a MonoArray* */
- args [1] = mono_array_new (domain, mono_get_byte_class (), 256);
- mono_runtime_invoke (ctor, obj, args, NULL);
- /* A property exists only as a metadata entity, so getting or setting the value
- * is nothing more than calling mono_runtime_invoke () on the getter or setter method.
- */
- prop = mono_class_get_property_from_name (klass, "Value");
- method = mono_property_get_get_method (prop);
- result = mono_runtime_invoke (method, obj, NULL, NULL);
- /* mono_runtime_invoke () always boxes the return value if it's a valuetype */
- val = *(int*)mono_object_unbox (result);
-
- printf ("Value of val from property is: %d\n", val);
-
- /* we also have an helper method: note that reference types are returned as is */
- prop = mono_class_get_property_from_name (klass, "Message");
- str = (MonoString*)mono_property_get_value (prop, obj, NULL, NULL);
- /* get the string in UTF-8 encoding to print it */
- p = mono_string_to_utf8 (str);
- printf ("Value of str from property is: %s\n", p);
- /* we need to free the result from mono_string_to_utf8 () */
- g_free (p);
- /* Now we'll show two things:
- * 1) static methods are invoked with mono_runtime_invoke () as well,
- * we just pass NULL as the second argument.
- * 2) we can catch exceptions thrown by the called method.
- * Note: fail is declared as static void Fail () in invoke.cs.
- * We first set result to NULL: if after the invocation it will have
- * a different value, it will be the exception that was thrown from
- * the Fail () method. Note that if an exception was thrown, the return
- * value (if any) is undefined and can't be used in any way (yes, the above
- * invocations don't have this type of error checking to make things simpler).
- */
- exception = NULL;
- mono_runtime_invoke (fail, NULL, NULL, &exception);
- if (exception) {
- printf ("An exception was thrown in Fail ()\n");
- }
- /* Now let's see how to handle methods that take by ref arguments:
- * Valuetypes continue to be passed as pointers to the data.
- * Reference arguments passed by ref (ref or out is the same)
- * are handled the same way: a pointer to the pointer is used
- * (so that the result can be read back).
- * Small note: in this case (a System.Int32 valuetype) we can just
- * use &val where val is a C 32 bit integer. In the general case
- * unmanaged code doesn't know the size of a valuetype, since the
- * runtime may decide to lay it out in what it thinks is a better way
- * (unless ExplicitLayout is set). To avoid issues, the best thing is to
- * create an object of the valuetype's class and retrieve the pointer
- * to the data with the mono_object_unbox () function.
- */
- val = 100;
- str = mono_string_new (domain, "another string");
- args [0] = &val;
- args [1] = &str;
- mono_runtime_invoke (mvalues, obj, args, NULL);
- /* get the string in UTF-8 encoding to print it */
- p = mono_string_to_utf8 (str);
- printf ("Values of str/val from Values () are: %s/%d\n", p, val);
- /* we need to free the result from mono_string_to_utf8 () */
- g_free (p);
- }
- static void
- more_methods (MonoDomain *domain)
- {
- MonoClass *klass;
- MonoMethodDesc* mdesc;
- MonoMethod *method, *vtmethod;
- MonoString *str;
- MonoObject *obj;
- char *p;
- int val;
- /* Now let's call an instance method on a valuetype. There are two
- * different case:
- * 1) calling a virtual method defined in a base class, like ToString ():
- * we need to pass the value boxed in an object
- * 2) calling a normal instance method: in this case
- * we pass the address to the valuetype as the second argument
- * instead of an object.
- * First some initialization.
- */
- val = 25;
- klass = mono_get_int32_class ();
- obj = mono_value_box (domain, klass, &val);
- /* A different way to search for a method */
- mdesc = mono_method_desc_new (":ToString()", FALSE);
- vtmethod = mono_method_desc_search_in_class (mdesc, klass);
- str = (MonoString*)mono_runtime_invoke (vtmethod, &val, NULL, NULL);
- /* get the string in UTF-8 encoding to print it */
- p = mono_string_to_utf8 (str);
- printf ("25.ToString (): %s\n", p);
- /* we need to free the result from mono_string_to_utf8 () */
- g_free (p);
- /* Now: see how the result is different if we search for the ToString ()
- * method in System.Object: mono_runtime_invoke () doesn't do any sort of
- * virtual method invocation: it calls the exact method that it was given
- * to execute. If a virtual call is needed, mono_object_get_virtual_method ()
- * can be called.
- */
- method = mono_method_desc_search_in_class (mdesc, mono_get_object_class ());
- str = (MonoString*)mono_runtime_invoke (method, obj, NULL, NULL);
- /* get the string in UTF-8 encoding to print it */
- p = mono_string_to_utf8 (str);
- printf ("25.ToString (), from System.Object: %s\n", p);
- /* we need to free the result from mono_string_to_utf8 () */
- g_free (p);
- /* Now get the method that overrides ToString () in obj */
- vtmethod = mono_object_get_virtual_method (obj, method);
- if (mono_class_is_valuetype (mono_method_get_class (vtmethod))) {
- printf ("Need to unbox this for call to virtual ToString () for %s\n", mono_class_get_name (klass));
- }
- mono_method_desc_free (mdesc);
- }
- static void
- create_object (MonoDomain *domain, MonoImage *image)
- {
- MonoClass *klass;
- MonoObject *obj;
- klass = mono_class_from_name (image, "Embed", "MyType");
- if (!klass) {
- fprintf (stderr, "Can't find MyType in assembly %s\n", mono_image_get_filename (image));
- exit (1);
- }
- obj = mono_object_new (domain, klass);
- /* mono_object_new () only allocates the storage:
- * it doesn't run any constructor. Tell the runtime to run
- * the default argumentless constructor.
- */
- mono_runtime_object_init (obj);
- access_valuetype_field (obj);
- access_reference_field (obj);
- call_methods (obj);
- more_methods (domain);
- }
- static void main_function (MonoDomain *domain, const char *file, int argc, char **argv)
- {
- MonoAssembly *assembly;
- /* Loading an assembly makes the runtime setup everything
- * needed to execute it. If we're just interested in the metadata
- * we'd use mono_image_load (), instead and we'd get a MonoImage*.
- */
- assembly = mono_domain_assembly_open (domain, file);
- if (!assembly)
- exit (2);
- /*
- * mono_jit_exec() will run the Main() method in the assembly.
- * The return value needs to be looked up from
- * System.Environment.ExitCode.
- */
- mono_jit_exec (domain, assembly, argc, argv);
- create_object (domain, mono_assembly_get_image (assembly));
- }
- int
- main (int argc, char* argv[]) {
- MonoDomain *domain;
- const char *file;
- int retval;
-
- if (argc < 2){
- fprintf (stderr, "Please provide an assembly to load\n");
- return 1;
- }
- file = argv [1];
- /*
- * mono_jit_init() creates a domain: each assembly is
- * loaded and run in a MonoDomain.
- */
- domain = mono_jit_init (file);
- main_function (domain, file, argc - 1, argv + 1);
- retval = mono_environment_exitcode_get ();
-
- mono_jit_cleanup (domain);
- return retval;
- }
|