test-invoke.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. #include <mono/jit/jit.h>
  2. #include <mono/metadata/environment.h>
  3. #include <mono/metadata/assembly.h>
  4. #include <mono/metadata/debug-helpers.h>
  5. #include <string.h>
  6. #include <stdlib.h>
  7. /*
  8. * Simple mono embedding example.
  9. * We show how to create objects and invoke methods and set fields in them.
  10. * Compile with:
  11. * gcc -Wall -o test-invoke test-invoke.c `pkg-config --cflags --libs mono` -lm
  12. * mcs invoke.cs
  13. * Run with:
  14. * ./test-invoke invoke.exe
  15. */
  16. typedef struct
  17. {
  18. MonoDomain *domain;
  19. const char *file;
  20. int argc;
  21. char **argv;
  22. } MainThreadArgs;
  23. static void
  24. access_valuetype_field (MonoObject *obj)
  25. {
  26. MonoClass *klass;
  27. MonoClassField *field;
  28. int val;
  29. klass = mono_object_get_class (obj);
  30. /* Now we'll change the value of the 'val' field (see invoke.cs) */
  31. field = mono_class_get_field_from_name (klass, "val");
  32. /* This time we also add a bit of error checking... */
  33. if (!field) {
  34. fprintf (stderr, "Can't find field val in MyType\n");
  35. exit (1);
  36. }
  37. /* Check that val is an int (if you're paranoid or if you need to
  38. * show how this API is used)
  39. */
  40. if (mono_type_get_type (mono_field_get_type (field)) != MONO_TYPE_I4) {
  41. fprintf (stderr, "Field val is not a 32 bit integer\n");
  42. exit (1);
  43. }
  44. /* Note we pass a pointer to the value */
  45. mono_field_get_value (obj, field, &val);
  46. printf ("Value of field is: %d\n", val);
  47. val = 10;
  48. /* Note we pass a pointer to the value here as well */
  49. mono_field_set_value (obj, field, &val);
  50. }
  51. static void
  52. access_reference_field (MonoObject *obj)
  53. {
  54. MonoClass *klass;
  55. MonoDomain *domain;
  56. MonoClassField *str;
  57. MonoString *strval;
  58. char *p;
  59. klass = mono_object_get_class (obj);
  60. domain = mono_object_get_domain (obj);
  61. /* Now we'll see that a reference type is handled slightly differently.
  62. * First, get the MonoClassField representing it.
  63. */
  64. str = mono_class_get_field_from_name (klass, "str");
  65. /* No change here, we always pass a pointer */
  66. mono_field_get_value (obj, str, &strval);
  67. /* get the string in UTF-8 encoding to print it */
  68. p = mono_string_to_utf8 (strval);
  69. printf ("Value of str is: %s\n", p);
  70. /* we need to free the result from mono_string_to_utf8 () */
  71. g_free (p);
  72. /* string are immutable, so we need to create a different string */
  73. strval = mono_string_new (domain, "hello from the embedding API");
  74. /* Here is the slight difference: for reference types we pass
  75. * the pointer directly, instead of a pointer to the value.
  76. */
  77. mono_field_set_value (obj, str, strval);
  78. }
  79. /* Demostrate how to call methods */
  80. static void
  81. call_methods (MonoObject *obj)
  82. {
  83. MonoClass *klass;
  84. MonoDomain *domain;
  85. MonoMethod *method = NULL, *m = NULL, *ctor = NULL, *fail = NULL, *mvalues;
  86. MonoProperty *prop;
  87. MonoObject *result, *exception;
  88. MonoString *str;
  89. char *p;
  90. gpointer iter;
  91. gpointer args [2];
  92. int val;
  93. klass = mono_object_get_class (obj);
  94. domain = mono_object_get_domain (obj);
  95. /* retrieve all the methods we need */
  96. iter = NULL;
  97. while ((m = mono_class_get_methods (klass, &iter))) {
  98. if (strcmp (mono_method_get_name (m), "method") == 0) {
  99. method = m;
  100. } else if (strcmp (mono_method_get_name (m), "Fail") == 0) {
  101. fail = m;
  102. } else if (strcmp (mono_method_get_name (m), "Values") == 0) {
  103. mvalues = m;
  104. } else if (strcmp (mono_method_get_name (m), ".ctor") == 0) {
  105. /* Check it's the ctor that takes two args:
  106. * as you see a contrsuctor is a method like any other.
  107. */
  108. MonoMethodSignature * sig = mono_method_signature (m);
  109. if (mono_signature_get_param_count (sig) == 2) {
  110. ctor = m;
  111. }
  112. }
  113. }
  114. /* Now we'll call method () on obj: since it takes no arguments
  115. * we can pass NULL as the third argument to mono_runtime_invoke ().
  116. * The method will print the updated value.
  117. */
  118. mono_runtime_invoke (method, obj, NULL, NULL);
  119. /* mono_object_new () doesn't call any constructor: this means that
  120. * we'll have to invoke the constructor if needed ourselves. Note:
  121. * invoking a constructor is no different than calling any other method,
  122. * so we'll still call mono_runtime_invoke (). This also means that we
  123. * can invoke a constructor at any time, like now.
  124. * First, setup the array of arguments and their values.
  125. */
  126. /* As usual, we use the address of the data for valuetype arguments */
  127. val = 7;
  128. args [0] = &val;
  129. /* and the pointer for reference types: mono_array_new () returns a MonoArray* */
  130. args [1] = mono_array_new (domain, mono_get_byte_class (), 256);
  131. mono_runtime_invoke (ctor, obj, args, NULL);
  132. /* A property exists only as a metadata entity, so getting or setting the value
  133. * is nothing more than calling mono_runtime_invoke () on the getter or setter method.
  134. */
  135. prop = mono_class_get_property_from_name (klass, "Value");
  136. method = mono_property_get_get_method (prop);
  137. result = mono_runtime_invoke (method, obj, NULL, NULL);
  138. /* mono_runtime_invoke () always boxes the return value if it's a valuetype */
  139. val = *(int*)mono_object_unbox (result);
  140. printf ("Value of val from property is: %d\n", val);
  141. /* we also have an helper method: note that reference types are returned as is */
  142. prop = mono_class_get_property_from_name (klass, "Message");
  143. str = (MonoString*)mono_property_get_value (prop, obj, NULL, NULL);
  144. /* get the string in UTF-8 encoding to print it */
  145. p = mono_string_to_utf8 (str);
  146. printf ("Value of str from property is: %s\n", p);
  147. /* we need to free the result from mono_string_to_utf8 () */
  148. g_free (p);
  149. /* Now we'll show two things:
  150. * 1) static methods are invoked with mono_runtime_invoke () as well,
  151. * we just pass NULL as the second argument.
  152. * 2) we can catch exceptions thrown by the called method.
  153. * Note: fail is declared as static void Fail () in invoke.cs.
  154. * We first set result to NULL: if after the invocation it will have
  155. * a different value, it will be the exception that was thrown from
  156. * the Fail () method. Note that if an exception was thrown, the return
  157. * value (if any) is undefined and can't be used in any way (yes, the above
  158. * invocations don't have this type of error checking to make things simpler).
  159. */
  160. exception = NULL;
  161. mono_runtime_invoke (fail, NULL, NULL, &exception);
  162. if (exception) {
  163. printf ("An exception was thrown in Fail ()\n");
  164. }
  165. /* Now let's see how to handle methods that take by ref arguments:
  166. * Valuetypes continue to be passed as pointers to the data.
  167. * Reference arguments passed by ref (ref or out is the same)
  168. * are handled the same way: a pointer to the pointer is used
  169. * (so that the result can be read back).
  170. * Small note: in this case (a System.Int32 valuetype) we can just
  171. * use &val where val is a C 32 bit integer. In the general case
  172. * unmanaged code doesn't know the size of a valuetype, since the
  173. * runtime may decide to lay it out in what it thinks is a better way
  174. * (unless ExplicitLayout is set). To avoid issues, the best thing is to
  175. * create an object of the valuetype's class and retrieve the pointer
  176. * to the data with the mono_object_unbox () function.
  177. */
  178. val = 100;
  179. str = mono_string_new (domain, "another string");
  180. args [0] = &val;
  181. args [1] = &str;
  182. mono_runtime_invoke (mvalues, obj, args, NULL);
  183. /* get the string in UTF-8 encoding to print it */
  184. p = mono_string_to_utf8 (str);
  185. printf ("Values of str/val from Values () are: %s/%d\n", p, val);
  186. /* we need to free the result from mono_string_to_utf8 () */
  187. g_free (p);
  188. }
  189. static void
  190. more_methods (MonoDomain *domain)
  191. {
  192. MonoClass *klass;
  193. MonoMethodDesc* mdesc;
  194. MonoMethod *method, *vtmethod;
  195. MonoString *str;
  196. MonoObject *obj;
  197. char *p;
  198. int val;
  199. /* Now let's call an instance method on a valuetype. There are two
  200. * different case:
  201. * 1) calling a virtual method defined in a base class, like ToString ():
  202. * we need to pass the value boxed in an object
  203. * 2) calling a normal instance method: in this case
  204. * we pass the address to the valuetype as the second argument
  205. * instead of an object.
  206. * First some initialization.
  207. */
  208. val = 25;
  209. klass = mono_get_int32_class ();
  210. obj = mono_value_box (domain, klass, &val);
  211. /* A different way to search for a method */
  212. mdesc = mono_method_desc_new (":ToString()", FALSE);
  213. vtmethod = mono_method_desc_search_in_class (mdesc, klass);
  214. str = (MonoString*)mono_runtime_invoke (vtmethod, &val, NULL, NULL);
  215. /* get the string in UTF-8 encoding to print it */
  216. p = mono_string_to_utf8 (str);
  217. printf ("25.ToString (): %s\n", p);
  218. /* we need to free the result from mono_string_to_utf8 () */
  219. g_free (p);
  220. /* Now: see how the result is different if we search for the ToString ()
  221. * method in System.Object: mono_runtime_invoke () doesn't do any sort of
  222. * virtual method invocation: it calls the exact method that it was given
  223. * to execute. If a virtual call is needed, mono_object_get_virtual_method ()
  224. * can be called.
  225. */
  226. method = mono_method_desc_search_in_class (mdesc, mono_get_object_class ());
  227. str = (MonoString*)mono_runtime_invoke (method, obj, NULL, NULL);
  228. /* get the string in UTF-8 encoding to print it */
  229. p = mono_string_to_utf8 (str);
  230. printf ("25.ToString (), from System.Object: %s\n", p);
  231. /* we need to free the result from mono_string_to_utf8 () */
  232. g_free (p);
  233. /* Now get the method that overrides ToString () in obj */
  234. vtmethod = mono_object_get_virtual_method (obj, method);
  235. if (mono_class_is_valuetype (mono_method_get_class (vtmethod))) {
  236. printf ("Need to unbox this for call to virtual ToString () for %s\n", mono_class_get_name (klass));
  237. }
  238. mono_method_desc_free (mdesc);
  239. }
  240. static void
  241. create_object (MonoDomain *domain, MonoImage *image)
  242. {
  243. MonoClass *klass;
  244. MonoObject *obj;
  245. klass = mono_class_from_name (image, "Embed", "MyType");
  246. if (!klass) {
  247. fprintf (stderr, "Can't find MyType in assembly %s\n", mono_image_get_filename (image));
  248. exit (1);
  249. }
  250. obj = mono_object_new (domain, klass);
  251. /* mono_object_new () only allocates the storage:
  252. * it doesn't run any constructor. Tell the runtime to run
  253. * the default argumentless constructor.
  254. */
  255. mono_runtime_object_init (obj);
  256. access_valuetype_field (obj);
  257. access_reference_field (obj);
  258. call_methods (obj);
  259. more_methods (domain);
  260. }
  261. static void main_thread_handler (gpointer user_data)
  262. {
  263. MainThreadArgs *main_args=(MainThreadArgs *)user_data;
  264. MonoAssembly *assembly;
  265. /* Loading an assembly makes the runtime setup everything
  266. * needed to execute it. If we're just interested in the metadata
  267. * we'd use mono_image_load (), instead and we'd get a MonoImage*.
  268. */
  269. assembly = mono_domain_assembly_open (main_args->domain,
  270. main_args->file);
  271. if (!assembly)
  272. exit (2);
  273. /*
  274. * mono_jit_exec() will run the Main() method in the assembly.
  275. * The return value needs to be looked up from
  276. * System.Environment.ExitCode.
  277. */
  278. mono_jit_exec (main_args->domain, assembly, main_args->argc,
  279. main_args->argv);
  280. create_object (main_args->domain, mono_assembly_get_image (assembly));
  281. }
  282. int
  283. main (int argc, char* argv[]) {
  284. MonoDomain *domain;
  285. const char *file;
  286. int retval;
  287. MainThreadArgs main_args;
  288. if (argc < 2){
  289. fprintf (stderr, "Please provide an assembly to load\n");
  290. return 1;
  291. }
  292. file = argv [1];
  293. /*
  294. * mono_jit_init() creates a domain: each assembly is
  295. * loaded and run in a MonoDomain.
  296. */
  297. domain = mono_jit_init (file);
  298. main_args.domain=domain;
  299. main_args.file=file;
  300. main_args.argc=argc-1;
  301. main_args.argv=argv+1;
  302. mono_runtime_exec_managed_code (domain, main_thread_handler,
  303. &main_args);
  304. retval=mono_environment_exitcode_get ();
  305. mono_jit_cleanup (domain);
  306. return retval;
  307. }