21943000e177e98f85960c9f29ecd4b4335f8c87
[mono.git] / mono / metadata / appdomain.c
1 /*
2  * appdomain.c: AppDomain functions
3  *
4  * Authors:
5  *      Dietmar Maurer (dietmar@ximian.com)
6  *      Patrik Torstensson
7  *      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8  *
9  * (c) 2001-2003 Ximian, Inc. (http://www.ximian.com)
10  */
11
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15
16 #include <mono/os/gc_wrapper.h>
17
18 #include <mono/metadata/object.h>
19 #include <mono/metadata/domain-internals.h>
20 #include "mono/metadata/metadata-internals.h"
21 #include <mono/metadata/assembly.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/threads.h>
24 #include <mono/metadata/socket-io.h>
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/metadata/gc-internal.h>
27 #include <mono/metadata/mono-gc.h>
28 #include <mono/metadata/marshal.h>
29 #include <mono/metadata/monitor.h>
30 #include <mono/metadata/threadpool.h>
31 #include <mono/utils/mono-uri.h>
32
33 #define MONO_CORLIB_VERSION 41
34
35 CRITICAL_SECTION mono_delegate_section;
36
37 static gunichar2 process_guid [36];
38 static gboolean process_guid_set = FALSE;
39
40 static gboolean shutting_down = FALSE;
41
42 static MonoAssembly *
43 mono_domain_assembly_preload (MonoAssemblyName *aname,
44                               gchar **assemblies_path,
45                               gpointer user_data);
46
47 static MonoAssembly *
48 mono_domain_assembly_search (MonoAssemblyName *aname,
49                                                          gpointer user_data);
50
51 static MonoAssembly *
52 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
53                                                                           gpointer user_data);
54
55 static void
56 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
57
58 static void
59 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
60
61 static void
62 mono_domain_unload (MonoDomain *domain);
63
64 /**
65  * mono_runtime_init:
66  * @domain: domain returned by mono_init ()
67  *
68  * Initialize the core AppDomain: this function will run also some
69  * IL initialization code, so it needs the execution engine to be fully 
70  * operational.
71  *
72  * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
73  * we know the entry_assembly.
74  *
75  */
76 void
77 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
78                    MonoThreadAttachCB attach_cb)
79 {
80         MonoAppDomainSetup *setup;
81         MonoAppDomain *ad;
82         MonoClass *class;
83         MonoString *arg;
84         
85         MONO_GC_PRE_INIT ();
86         mono_monitor_init ();
87         mono_thread_pool_init ();
88         mono_marshal_init ();
89
90         mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
91         mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
92         mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
93         mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
94         mono_install_assembly_postload_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
95         mono_install_assembly_postload_refonly_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
96         mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
97         mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
98
99         mono_thread_init (start_cb, attach_cb);
100
101         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
102         setup = (MonoAppDomainSetup *) mono_object_new (domain, class);
103
104         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
105         ad = (MonoAppDomain *) mono_object_new (domain, class);
106         ad->data = domain;
107         domain->domain = ad;
108         domain->setup = setup;
109
110         InitializeCriticalSection (&mono_delegate_section);
111
112         mono_thread_attach (domain);
113         mono_context_init (domain);
114         mono_context_set (domain->default_context);
115
116         mono_type_initialization_init ();
117
118         
119         /*
120          * Create an instance early since we can't do it when there is no memory.
121          */
122         arg = mono_string_new (domain, "Out of memory");
123         domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL);
124         
125         /* 
126          * These two are needed because the signal handlers might be executing on
127          * an alternate stack, and Boehm GC can't handle that.
128          */
129         arg = mono_string_new (domain, "A null value was found where an object instance was required");
130         domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL);
131         arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
132         domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL);
133         
134         /* GC init has to happen after thread init */
135         mono_gc_init ();
136
137         mono_network_init ();
138
139         /* mscorlib is loaded before we install the load hook */
140         mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
141
142         return;
143 }
144
145 static int
146 mono_get_corlib_version (void)
147 {
148         MonoClass *klass;
149         MonoClassField *field;
150         MonoObject *value;
151
152         klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
153         mono_class_init (klass);
154         field = mono_class_get_field_from_name (klass, "mono_corlib_version");
155         if (!field)
156                 return -1;
157         if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
158                 return -1;
159         value = mono_field_get_value_object (mono_domain_get (), field, NULL);
160         return *(gint32*)((gchar*)value + sizeof (MonoObject));
161 }
162
163 const char*
164 mono_check_corlib_version (void)
165 {
166         int version = mono_get_corlib_version ();
167         if (version != MONO_CORLIB_VERSION)
168                 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
169         else
170                 return NULL;
171 }
172
173 void
174 mono_context_init (MonoDomain *domain)
175 {
176         MonoClass *class;
177         MonoAppContext *context;
178
179         class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
180         context = (MonoAppContext *) mono_object_new (domain, class);
181         context->domain_id = domain->domain_id;
182         context->context_id = 0;
183         domain->default_context = context;
184 }
185
186 /**
187  * mono_runtime_cleanup:
188  * @domain: unused.
189  *
190  * Internal routine.
191  *
192  * This must not be called while there are still running threads executing
193  * managed code.
194  */
195 void
196 mono_runtime_cleanup (MonoDomain *domain)
197 {
198         shutting_down = TRUE;
199
200         /* This ends up calling any pending pending (for at most 2 seconds) */
201         mono_gc_cleanup ();
202
203         mono_network_cleanup ();
204 }
205
206 static MonoDomainFunc quit_function = NULL;
207
208 void
209 mono_install_runtime_cleanup (MonoDomainFunc func)
210 {
211         quit_function = func;
212 }
213
214 void
215 mono_runtime_quit ()
216 {
217         if (quit_function != NULL)
218                 quit_function (mono_get_root_domain (), NULL);
219 }
220
221 /** 
222  * mono_runtime_set_shutting_down:
223  *
224  * Invoked by System.Environment.Exit to flag that the runtime
225  * is shutting down.
226  */
227 void
228 mono_runtime_set_shutting_down (void)
229 {
230         shutting_down = TRUE;
231 }
232
233 /**
234  * mono_runtime_is_shutting_down:
235  *
236  * Returns whether the runtime has been flagged for shutdown.
237  *
238  * This is consumed by the P:System.Environment.HasShutdownStarted
239  * property.
240  *
241  */
242 gboolean
243 mono_runtime_is_shutting_down (void)
244 {
245         return shutting_down;
246 }
247
248 /**
249  * mono_domain_has_type_resolve:
250  * @domain: application domains being looked up
251  *
252  * Returns true if the AppDomain.TypeResolve field has been
253  * set.
254  */
255 gboolean
256 mono_domain_has_type_resolve (MonoDomain *domain)
257 {
258         static MonoClassField *field = NULL;
259         MonoObject *o;
260
261         if (field == NULL) {
262                 field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
263                 g_assert (field);
264         }
265
266         mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
267         return o != NULL;
268 }
269
270 /**
271  * mono_domain_try_type_resolve:
272  * @domain: application domainwhere the name where the type is going to be resolved
273  * @name: the name of the type to resolve or NULL.
274  * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
275  *
276  * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
277  * the assembly that matches name.
278  *
279  * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
280  *
281  * Returns: A MonoReflectionAssembly or NULL if not found
282  */
283 MonoReflectionAssembly *
284 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
285 {
286         MonoClass *klass;
287         void *params [1];
288         static MonoMethod *method = NULL;
289
290         g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
291
292         if (method == NULL) {
293                 klass = domain->domain->mbr.obj.vtable->klass;
294                 g_assert (klass);
295
296                 method = mono_class_get_method_from_name (klass, "DoTypeResolve", -1);
297                 if (method == NULL) {
298                         g_warning ("Method AppDomain.DoTypeResolve not found.\n");
299                         return NULL;
300                 }
301         }
302
303         if (name)
304                 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
305         else
306                 *params = tb;
307         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
308 }
309
310 /**
311  * mono_domain_owns_vtable_slot:
312  *
313  *  Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
314  */
315 gboolean
316 mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
317 {
318         gboolean res;
319
320         mono_domain_lock (domain);
321         res = mono_mempool_contains_addr (domain->mp, vtable_slot);
322         mono_domain_unlock (domain);
323         return res;
324 }
325
326 /**
327  * mono_domain_set:
328  * @domain: domain
329  * @force: force setting.
330  *
331  * Set the current appdomain to @domain. If @force is set, set it even
332  * if it is being unloaded.
333  *
334  * Returns:
335  *   TRUE on success;
336  *   FALSE if the domain is unloaded
337  */
338 gboolean
339 mono_domain_set (MonoDomain *domain, gboolean force)
340 {
341         if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
342                 return FALSE;
343
344         mono_domain_set_internal (domain);
345
346         return TRUE;
347 }
348
349 MonoObject *
350 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
351 {
352         MonoDomain *add = ad->data;
353         MonoObject *o;
354         char *str;
355
356         MONO_ARCH_SAVE_REGS;
357
358         g_assert (ad != NULL);
359
360         if (name == NULL)
361                 mono_raise_exception (mono_get_exception_argument_null ("name"));
362
363         str = mono_string_to_utf8 (name);
364
365         mono_domain_lock (add);
366
367         if (!strcmp (str, "APPBASE"))
368                 o = (MonoObject *)add->setup->application_base;
369         else if (!strcmp (str, "APP_CONFIG_FILE"))
370                 o = (MonoObject *)add->setup->configuration_file;
371         else if (!strcmp (str, "DYNAMIC_BASE"))
372                 o = (MonoObject *)add->setup->dynamic_base;
373         else if (!strcmp (str, "APP_NAME"))
374                 o = (MonoObject *)add->setup->application_name;
375         else if (!strcmp (str, "CACHE_BASE"))
376                 o = (MonoObject *)add->setup->cache_path;
377         else if (!strcmp (str, "PRIVATE_BINPATH"))
378                 o = (MonoObject *)add->setup->private_bin_path;
379         else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
380                 o = (MonoObject *)add->setup->private_bin_path_probe;
381         else if (!strcmp (str, "SHADOW_COPY_DIRS"))
382                 o = (MonoObject *)add->setup->shadow_copy_directories;
383         else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
384                 o = (MonoObject *)add->setup->shadow_copy_files;
385         else 
386                 o = mono_g_hash_table_lookup (add->env, name);
387
388         mono_domain_unlock (add);
389         g_free (str);
390
391         if (!o)
392                 return NULL;
393
394         return o;
395 }
396
397 void
398 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
399 {
400         MonoDomain *add = ad->data;
401
402         MONO_ARCH_SAVE_REGS;
403
404         g_assert (ad != NULL);
405
406         if (name == NULL)
407                 mono_raise_exception (mono_get_exception_argument_null ("name"));
408
409         mono_domain_lock (add);
410
411         mono_g_hash_table_insert (add->env, name, data);
412
413         mono_domain_unlock (add);
414 }
415
416 MonoAppDomainSetup *
417 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
418 {
419         MONO_ARCH_SAVE_REGS;
420
421         g_assert (ad != NULL);
422         g_assert (ad->data != NULL);
423
424         return ad->data->setup;
425 }
426
427 MonoString *
428 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
429 {
430         MONO_ARCH_SAVE_REGS;
431
432         g_assert (ad != NULL);
433         g_assert (ad->data != NULL);
434
435         return mono_string_new (ad->data, ad->data->friendly_name);
436 }
437
438 MonoAppDomain *
439 ves_icall_System_AppDomain_getCurDomain ()
440 {
441         MonoDomain *add = mono_domain_get ();
442
443         MONO_ARCH_SAVE_REGS;
444
445         return add->domain;
446 }
447
448 MonoAppDomain *
449 ves_icall_System_AppDomain_getRootDomain ()
450 {
451         MonoDomain *root = mono_get_root_domain ();
452
453         MONO_ARCH_SAVE_REGS;
454
455         return root->domain;
456 }
457
458 MonoAppDomain *
459 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
460 {
461         MonoDomain *domain = mono_domain_get ();
462         MonoClass *adclass;
463         MonoAppDomain *ad;
464         MonoDomain *data;
465         GSList *tmp;
466         
467         MONO_ARCH_SAVE_REGS;
468
469         adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
470
471         /* FIXME: pin all those objects */
472         data = mono_domain_create();
473
474         ad = (MonoAppDomain *) mono_object_new (data, adclass);
475         ad->data = data;
476         data->domain = ad;
477         data->setup = setup;
478         data->friendly_name = mono_string_to_utf8 (friendly_name);
479         data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException");
480
481         if (!setup->application_base) {
482                 /* Inherit from the root domain since MS.NET does this */
483                 MonoDomain *root = mono_get_root_domain ();
484                 setup->application_base = mono_string_new_utf16 (data, mono_string_chars (root->setup->application_base), mono_string_length (root->setup->application_base));
485         }
486
487         mono_context_init (data);
488
489         /* The new appdomain should have all assemblies loaded */
490         mono_domain_assemblies_lock (domain);
491         /*g_print ("copy assemblies from domain %p (%s)\n", domain, domain->friendly_name);*/
492         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next)
493                 add_assemblies_to_domain (data, tmp->data, NULL);
494         mono_domain_assemblies_unlock (domain);
495
496         return ad;
497 }
498
499 MonoArray *
500 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly)
501 {
502         MonoDomain *domain = ad->data; 
503         MonoAssembly* ass;
504         static MonoClass *System_Reflection_Assembly;
505         MonoArray *res;
506         GSList *tmp;
507         int i, count;
508         
509         MONO_ARCH_SAVE_REGS;
510
511         if (!System_Reflection_Assembly)
512                 System_Reflection_Assembly = mono_class_from_name (
513                         mono_defaults.corlib, "System.Reflection", "Assembly");
514
515         count = 0;
516         /* Need to skip internal assembly builders created by remoting */
517         mono_domain_assemblies_lock (domain);
518         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
519                 ass = tmp->data;
520                 if (refonly && !ass->ref_only)
521                         continue;
522                 if (!ass->corlib_internal)
523                         count++;
524         }
525         res = mono_array_new (domain, System_Reflection_Assembly, count);
526         i = 0;
527         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
528                 ass = tmp->data;
529                 if (refonly && !ass->ref_only)
530                         continue;
531                 if (ass->corlib_internal)
532                         continue;
533                 mono_array_set (res, gpointer, i, mono_assembly_get_object (domain, ass));
534                 ++i;
535         }
536         mono_domain_assemblies_unlock (domain);
537
538         return res;
539 }
540
541 static MonoReflectionAssembly *
542 try_assembly_resolve (MonoDomain *domain, MonoString *fname, gboolean refonly)
543 {
544         MonoClass *klass;
545         MonoMethod *method;
546         MonoBoolean isrefonly;
547         gpointer params [2];
548
549         g_assert (domain != NULL && fname != NULL);
550
551         klass = domain->domain->mbr.obj.vtable->klass;
552         g_assert (klass);
553         
554         method = mono_class_get_method_from_name (klass, "DoAssemblyResolve", -1);
555         if (method == NULL) {
556                 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
557                 return NULL;
558         }
559
560         isrefonly = refonly ? 1 : 0;
561         params [0] = fname;
562         params [1] = &isrefonly;
563         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
564 }
565
566 static MonoAssembly *
567 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
568                                                                           gpointer user_data)
569 {
570         gboolean refonly = GPOINTER_TO_UINT (user_data);
571         MonoReflectionAssembly *assembly;
572         MonoDomain *domain = mono_domain_get ();
573         char *aname_str;
574
575         aname_str = mono_stringify_assembly_name (aname);
576
577         /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
578         assembly = try_assembly_resolve (domain, mono_string_new (domain, aname_str), refonly);
579
580         g_free (aname_str);
581
582         if (assembly)
583                 return assembly->assembly;
584         else
585                 return NULL;
586 }
587         
588 /*
589  * LOCKING: assumes assemblies_lock in the domain is already locked.
590  */
591 static void
592 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
593 {
594         gint i;
595         GSList *tmp;
596         gboolean destroy_ht = FALSE;
597
598         if (!ass->aname.name)
599                 return;
600
601         if (!ht) {
602                 ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
603                 destroy_ht = TRUE;
604         }
605
606         /* FIXME: handle lazy loaded assemblies */
607         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
608                 g_hash_table_insert (ht, tmp->data, tmp->data);
609         }
610         if (!g_hash_table_lookup (ht, ass)) {
611                 mono_assembly_addref (ass);
612                 g_hash_table_insert (ht, ass, ass);
613                 domain->domain_assemblies = g_slist_prepend (domain->domain_assemblies, ass);
614         }
615
616         if (ass->image->references) {
617                 for (i = 0; ass->image->references [i] != NULL; i++) {
618                         if (!g_hash_table_lookup (ht, ass->image->references [i])) {
619                                 add_assemblies_to_domain (domain, ass->image->references [i], ht);
620                         }
621                 }
622         }
623         if (destroy_ht)
624                 g_hash_table_destroy (ht);
625 }
626
627 static void
628 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
629 {
630         static MonoClassField *assembly_load_field;
631         static MonoMethod *assembly_load_method;
632         MonoDomain *domain = mono_domain_get ();
633         MonoReflectionAssembly *ref_assembly;
634         MonoClass *klass;
635         gpointer load_value;
636         void *params [1];
637
638         if (!domain->domain)
639                 /* This can happen during startup */
640                 return;
641
642         klass = domain->domain->mbr.obj.vtable->klass;
643
644         mono_domain_assemblies_lock (domain);
645         add_assemblies_to_domain (domain, assembly, NULL);
646         mono_domain_assemblies_unlock (domain);
647
648         if (assembly_load_field == NULL) {
649                 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
650                 g_assert (assembly_load_field);
651         }
652
653         mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
654         if (load_value == NULL) {
655                 /* No events waiting to be triggered */
656                 return;
657         }
658
659         ref_assembly = mono_assembly_get_object (domain, assembly);
660         g_assert (ref_assembly);
661
662         if (assembly_load_method == NULL) {
663                 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
664                 g_assert (assembly_load_method);
665         }
666
667         *params = ref_assembly;
668         mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
669 }
670
671 static gchar *
672 reduce_path (const gchar *dirname)
673 {
674         gchar **parts;
675         gchar *part;
676         GList *list, *tmp;
677         GString *result;
678         gchar *res;
679         gint i;
680
681         parts = g_strsplit (dirname, G_DIR_SEPARATOR_S, 0);
682         list = NULL;
683         for (i = 0; (part = parts [i]) != NULL; i++) {
684                 if (!strcmp (part, "."))
685                         continue;
686
687                 if (!strcmp (part, "..")) {
688                         if (list && list->next) /* Don't remove root */
689                                 list = g_list_delete_link (list, list);
690                 } else {
691                         list = g_list_prepend (list, part);
692                 }
693         }
694
695         result = g_string_new ("");
696         list = g_list_reverse (list);
697
698         for (tmp = list; tmp; tmp = tmp->next) {
699                 gchar *data = (gchar *) tmp->data;
700
701                 if (data && *data) {
702 #ifdef PLATFORM_WIN32
703                         if (result->len == 0)
704                                 g_string_append_printf (result, "%s\\", data);
705                         else if (result->str [result->len - 1] == '\\')
706                                 g_string_append_printf (result, "%s", data);
707                         else
708 #endif
709                                 g_string_append_printf (result, "%c%s",
710                                                         G_DIR_SEPARATOR, data);
711                 }
712         }
713         
714         res = result->str;
715         g_string_free (result, FALSE);
716         g_list_free (list);
717         g_strfreev (parts);
718         return res;
719 }
720
721 static void
722 set_domain_search_path (MonoDomain *domain)
723 {
724         MonoAppDomainSetup *setup;
725         gchar **tmp;
726         gchar *utf8;
727         gint i;
728         gint npaths = 0;
729         gchar **pvt_split = NULL;
730         GError *error = NULL;
731         gint appbaselen = -1;
732
733         if ((domain->search_path != NULL) && !domain->setup->path_changed)
734                 return;
735         if (!domain->setup)
736                 return;
737
738         setup = domain->setup;
739         if (!setup->application_base)
740                 return; /* Must set application base to get private path working */
741
742         npaths++;
743         if (setup->private_bin_path) {
744                 utf8 = mono_string_to_utf8 (setup->private_bin_path);
745                 pvt_split = g_strsplit (utf8, G_SEARCHPATH_SEPARATOR_S, 1000);
746                 g_free (utf8);
747                 for (tmp = pvt_split; *tmp; tmp++, npaths++);
748         }
749
750         if (!npaths) {
751                 if (pvt_split)
752                         g_strfreev (pvt_split);
753                 /*
754                  * Don't do this because the first time is called, the domain
755                  * setup is not finished.
756                  *
757                  * domain->search_path = g_malloc (sizeof (char *));
758                  * domain->search_path [0] = NULL;
759                 */
760                 return;
761         }
762
763         if (domain->search_path)
764                 g_strfreev (domain->search_path);
765
766         domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
767         tmp [npaths] = NULL;
768
769         *tmp = mono_string_to_utf8 (setup->application_base);
770
771         /* FIXME: is this needed? */
772         if (strncmp (*tmp, "file://", 7) == 0) {
773                 gchar *file = *tmp;
774                 gchar *uri = *tmp;
775                 gchar *tmpuri;
776
777                 if (uri [7] != '/')
778                         uri = g_strdup_printf ("file:///%s", uri + 7);
779
780                 tmpuri = uri;
781                 uri = mono_escape_uri_string (tmpuri);
782                 *tmp = g_filename_from_uri (uri, NULL, &error);
783                 g_free (uri);
784
785                 if (tmpuri != file)
786                         g_free (tmpuri);
787
788                 if (error != NULL) {
789                         g_warning ("%s\n", error->message);
790                         g_error_free (error);
791                         *tmp = file;
792                 } else {
793                         g_free (file);
794                 }
795         }
796
797         for (i = 1; pvt_split && i < npaths; i++) {
798                 if (g_path_is_absolute (pvt_split [i - 1])) {
799                         tmp [i] = g_strdup (pvt_split [i - 1]);
800                 } else {
801                         tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
802                 }
803
804                 if (strchr (tmp [i], '.')) {
805                         gchar *reduced;
806                         gchar *freeme;
807
808                         reduced = reduce_path (tmp [i]);
809                         if (appbaselen == -1)
810                                 appbaselen = strlen (tmp [0]);
811
812                         if (strncmp (tmp [0], reduced, appbaselen)) {
813                                 g_free (reduced);
814                                 g_free (tmp [i]);
815                                 tmp [i] = g_strdup ("");
816                                 continue;
817                         }
818
819                         freeme = tmp [i];
820                         tmp [i] = reduced;
821                         g_free (freeme);
822                 }
823         }
824         
825         if (setup->private_bin_path_probe != NULL) {
826                 g_free (tmp [0]);
827                 tmp [0] = g_strdup ("");
828         }
829                 
830         domain->setup->path_changed = FALSE;
831
832         g_strfreev (pvt_split);
833 }
834
835 static gboolean
836 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
837                                         const gchar *path3, const gchar *path4, gboolean refonly)
838 {
839         gchar *fullpath;
840
841         *assembly = NULL;
842         fullpath = g_build_filename (path1, path2, path3, path4, NULL);
843         if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
844                 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
845
846         g_free (fullpath);
847         return (*assembly != NULL);
848 }
849
850 static MonoAssembly *
851 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
852 {
853         MonoAssembly *result = NULL;
854         gchar **path;
855         gchar *filename;
856         const gchar *local_culture;
857         gint len;
858
859         if (!culture || *culture == '\0') {
860                 local_culture = "";
861         } else {
862                 local_culture = culture;
863         }
864
865         filename =  g_strconcat (name, ".dll", NULL);
866         len = strlen (filename);
867
868         for (path = search_path; *path; path++) {
869                 if (**path == '\0')
870                         continue; /* Ignore empty ApplicationBase */
871
872                 /* See test cases in bug #58992 and bug #57710 */
873                 /* 1st try: [culture]/[name].dll (culture may be empty) */
874                 strcpy (filename + len - 4, ".dll");
875                 if (try_load_from (&result, *path, local_culture, "", filename, refonly))
876                         break;
877
878                 /* 2nd try: [culture]/[name].exe (culture may be empty) */
879                 strcpy (filename + len - 4, ".exe");
880                 if (try_load_from (&result, *path, local_culture, "", filename, refonly))
881                         break;
882
883                 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
884                 strcpy (filename + len - 4, ".dll");
885                 if (try_load_from (&result, *path, local_culture, name, filename, refonly))
886                         break;
887
888                 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
889                 strcpy (filename + len - 4, ".exe");
890                 if (try_load_from (&result, *path, local_culture, name, filename, refonly))
891                         break;
892         }
893
894         g_free (filename);
895         return result;
896 }
897
898 /*
899  * Try loading the assembly from ApplicationBase and PrivateBinPath 
900  * and then from assemblies_path if any.
901  */
902 static MonoAssembly *
903 mono_domain_assembly_preload (MonoAssemblyName *aname,
904                               gchar **assemblies_path,
905                               gpointer user_data)
906 {
907         MonoDomain *domain = mono_domain_get ();
908         MonoAssembly *result = NULL;
909         gboolean refonly = GPOINTER_TO_UINT (user_data);
910
911         set_domain_search_path (domain);
912
913         if (domain->search_path && domain->search_path [0] != NULL) {
914                 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
915         }
916
917         if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
918                 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
919         }
920
921         return result;
922 }
923
924 /*
925  * Check whenever a given assembly was already loaded in the current appdomain.
926  */
927 static MonoAssembly *
928 mono_domain_assembly_search (MonoAssemblyName *aname,
929                                                          gpointer user_data)
930 {
931         MonoDomain *domain = mono_domain_get ();
932         GSList *tmp;
933         MonoAssembly *ass;
934         gboolean refonly = GPOINTER_TO_UINT (user_data);
935
936         mono_domain_assemblies_lock (domain);
937         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
938                 ass = tmp->data;
939                 /* Dynamic assemblies can't match here in MS.NET */
940                 if (ass->dynamic || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
941                         continue;
942
943                 mono_domain_assemblies_unlock (domain);
944                 return ass;
945         }
946         mono_domain_assemblies_unlock (domain);
947
948         return NULL;
949 }
950
951 MonoReflectionAssembly *
952 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
953 {
954         MonoDomain *domain = mono_domain_get ();
955         char *name, *filename;
956         MonoImageOpenStatus status = MONO_IMAGE_OK;
957         MonoAssembly *ass;
958
959         MONO_ARCH_SAVE_REGS;
960
961         if (fname == NULL) {
962                 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
963                 mono_raise_exception (exc);
964         }
965                 
966         name = filename = mono_string_to_utf8 (fname);
967
968         ass = mono_assembly_open_full (filename, &status, refOnly);
969         
970         g_free (name);
971
972         if (!ass){
973                 MonoException *exc = mono_get_exception_file_not_found (fname);
974                 mono_raise_exception (exc);
975         }
976
977         return mono_assembly_get_object (domain, ass);
978 }
979
980 MonoReflectionAssembly *
981 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, 
982                                                                                         MonoArray *raw_assembly,
983                                                                                         MonoArray *raw_symbol_store, MonoObject *evidence,
984                                                                                         MonoBoolean refonly)
985 {
986         MonoAssembly *ass;
987         MonoReflectionAssembly *refass = NULL;
988         MonoDomain *domain = ad->data;
989         MonoImageOpenStatus status;
990         guint32 raw_assembly_len = mono_array_length (raw_assembly);
991         MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
992
993         if (raw_symbol_store)
994                 mono_raise_exception (mono_get_exception_not_implemented ("LoadAssemblyRaw: Raw Symbol Store not Implemented"));
995   
996         if (!image) {
997                 mono_raise_exception (mono_get_exception_bad_image_format (""));
998                 return NULL;
999         }
1000
1001         ass = mono_assembly_load_from_full (image, "", &status, refonly);
1002
1003         if (!ass) {
1004                 mono_image_close (image);
1005                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1006                 return NULL; 
1007         }
1008
1009         refass = mono_assembly_get_object (domain, ass);
1010         refass->evidence = evidence;
1011         return refass;
1012 }
1013
1014 MonoReflectionAssembly *
1015 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
1016 {
1017         MonoDomain *domain = ad->data; 
1018         MonoImageOpenStatus status = MONO_IMAGE_OK;
1019         MonoAssembly *ass;
1020         MonoAssemblyName aname;
1021         MonoReflectionAssembly *refass = NULL;
1022         gchar *name;
1023         gboolean parsed;
1024
1025         MONO_ARCH_SAVE_REGS;
1026
1027         g_assert (assRef != NULL);
1028
1029         name = mono_string_to_utf8 (assRef);
1030         parsed = mono_assembly_name_parse (name, &aname);
1031         g_free (name);
1032
1033         if (!parsed) {
1034                 MonoException *exc;
1035
1036                 /* This is a parse error... */
1037                 exc = mono_get_exception_file_not_found (assRef);
1038                 mono_raise_exception (exc);
1039         }
1040
1041         ass = mono_assembly_load_full (&aname, NULL, &status, refOnly);
1042         mono_assembly_name_free (&aname);
1043
1044         if (!ass && (refass = try_assembly_resolve (domain, assRef, refOnly)) == NULL){
1045                 /* FIXME: it doesn't make much sense since we really don't have a filename ... */
1046                 MonoException *exc = mono_get_exception_file_not_found (assRef);
1047                 mono_raise_exception (exc);
1048         }
1049
1050         if (refass == NULL)
1051                 refass = mono_assembly_get_object (domain, ass);
1052
1053         refass->evidence = evidence;
1054         return refass;
1055 }
1056
1057 void
1058 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
1059 {
1060         MonoDomain * domain = mono_domain_get_by_id (domain_id);
1061
1062         MONO_ARCH_SAVE_REGS;
1063
1064         if (NULL == domain) {
1065                 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
1066                 mono_raise_exception (exc);
1067         }
1068         
1069         if (domain == mono_get_root_domain ()) {
1070                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
1071                 return;
1072         }
1073
1074         /* 
1075          * Unloading seems to cause problems when running NUnit/NAnt, hence
1076          * this workaround.
1077          */
1078         if (g_getenv ("MONO_NO_UNLOAD"))
1079                 return;
1080
1081         mono_domain_unload (domain);
1082 }
1083
1084 gboolean
1085 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
1086 {
1087         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1088
1089         if (!domain)
1090                 return TRUE;
1091
1092         return mono_domain_is_unloading (domain);
1093 }
1094
1095 gint32
1096 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file, 
1097                                             MonoObject *evidence, MonoArray *args)
1098 {
1099         MonoAssembly *assembly;
1100         MonoImage *image;
1101         MonoMethod *method;
1102         char *filename;
1103         gint32 res;
1104         MonoReflectionAssembly *refass;
1105
1106         MONO_ARCH_SAVE_REGS;
1107
1108         filename = mono_string_to_utf8 (file);
1109         assembly = mono_assembly_open (filename, NULL);
1110         g_free (filename);
1111
1112         if (!assembly)
1113                 mono_raise_exception (mono_get_exception_file_not_found (file));
1114
1115         image = assembly->image;
1116
1117         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1118
1119         if (!method)
1120                 g_error ("No entry point method found in %s", image->name);
1121
1122         if (!args)
1123                 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1124
1125         refass = mono_assembly_get_object (ad->data, assembly);
1126         refass->evidence = evidence;
1127
1128         res = mono_runtime_exec_main (method, (MonoArray *)args, NULL);
1129
1130         return res;
1131 }
1132
1133 gint32 
1134 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) 
1135 {
1136         MONO_ARCH_SAVE_REGS;
1137
1138         return ad->data->domain_id;
1139 }
1140
1141 MonoAppDomain * 
1142 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
1143 {
1144         MonoDomain *old_domain = mono_domain_get();
1145
1146         MONO_ARCH_SAVE_REGS;
1147
1148         if (!mono_domain_set (ad->data, FALSE))
1149                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1150
1151         return old_domain->domain;
1152 }
1153
1154 MonoAppDomain * 
1155 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
1156 {
1157         MonoDomain *current_domain = mono_domain_get ();
1158         MonoDomain *domain = mono_domain_get_by_id (domainid);
1159
1160         MONO_ARCH_SAVE_REGS;
1161
1162         if (!domain || !mono_domain_set (domain, FALSE))        
1163                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1164
1165         return current_domain->domain;
1166 }
1167
1168 void
1169 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
1170 {
1171         MONO_ARCH_SAVE_REGS;
1172
1173         mono_thread_push_appdomain_ref (ad->data);
1174 }
1175
1176 void
1177 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
1178 {
1179         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1180
1181         MONO_ARCH_SAVE_REGS;
1182
1183         if (!domain)
1184                 /* 
1185                  * Raise an exception to prevent the managed code from executing a pop
1186                  * later.
1187                  */
1188                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1189
1190         mono_thread_push_appdomain_ref (domain);
1191 }
1192
1193 void
1194 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1195 {
1196         MONO_ARCH_SAVE_REGS;
1197
1198         mono_thread_pop_appdomain_ref ();
1199 }
1200
1201 MonoAppContext * 
1202 ves_icall_System_AppDomain_InternalGetContext ()
1203 {
1204         MONO_ARCH_SAVE_REGS;
1205
1206         return mono_context_get ();
1207 }
1208
1209 MonoAppContext * 
1210 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1211 {
1212         MONO_ARCH_SAVE_REGS;
1213
1214         return mono_domain_get ()->default_context;
1215 }
1216
1217 MonoAppContext * 
1218 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
1219 {
1220         MonoAppContext *old_context = mono_context_get ();
1221
1222         MONO_ARCH_SAVE_REGS;
1223
1224         mono_context_set (mc);
1225
1226         return old_context;
1227 }
1228
1229 MonoString *
1230 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
1231 {
1232         MonoDomain* mono_root_domain = mono_get_root_domain ();
1233         mono_domain_lock (mono_root_domain);
1234         if (process_guid_set) {
1235                 mono_domain_unlock (mono_root_domain);
1236                 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
1237         }
1238         memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
1239         process_guid_set = TRUE;
1240         mono_domain_unlock (mono_root_domain);
1241         return newguid;
1242 }
1243
1244 gboolean
1245 mono_domain_is_unloading (MonoDomain *domain)
1246 {
1247         if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
1248                 return TRUE;
1249         else
1250                 return FALSE;
1251 }
1252
1253 static void
1254 clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
1255 {
1256         MonoClass *klass = (MonoClass*)key;
1257         MonoDomain *domain = (MonoDomain*)user_data;
1258         MonoClassRuntimeInfo *runtime_info;
1259
1260         runtime_info = klass->runtime_info;
1261         if (runtime_info && runtime_info->max_domain >= domain->domain_id)
1262                 runtime_info->domain_vtables [domain->domain_id] = NULL;
1263 }
1264
1265 typedef struct unload_data {
1266         MonoDomain *domain;
1267         char *failure_reason;
1268 } unload_data;
1269
1270 static guint32 WINAPI
1271 unload_thread_main (void *arg)
1272 {
1273         unload_data *data = (unload_data*)arg;
1274         MonoDomain *domain = data->domain;
1275
1276         /* 
1277          * FIXME: Abort our parent thread last, so we can return a failure 
1278          * indication if aborting times out.
1279          */
1280         if (!mono_threads_abort_appdomain_threads (domain, 10000)) {
1281                 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
1282                 return 1;
1283         }
1284
1285         /* Finalize all finalizable objects in the doomed appdomain */
1286         if (!mono_domain_finalize (domain, 10000)) {
1287                 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
1288                 return 1;
1289         }
1290
1291         /* Clear references to our vtables in class->runtime_info.
1292          * We also hold the loader lock because we're going to change
1293          * class->runtime_info.
1294          */
1295         mono_domain_lock (domain);
1296         mono_loader_lock ();
1297         g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable, domain);
1298         mono_loader_unlock ();
1299         mono_domain_unlock (domain);
1300
1301         mono_threads_clear_cached_culture (domain);
1302
1303         domain->state = MONO_APPDOMAIN_UNLOADED;
1304
1305         /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1306
1307         mono_domain_free (domain, FALSE);
1308
1309         mono_gc_collect (mono_gc_max_generation ());
1310
1311         return 0;
1312 }
1313
1314 /*
1315  * mono_domain_unload:
1316  * @domain: The domain to unload
1317  *
1318  *  Unloads an appdomain. Follows the process outlined in:
1319  *  http://blogs.gotdotnet.com/cbrumme
1320  *
1321  *  If doing things the 'right' way is too hard or complex, we do it the 
1322  *  'simple' way, which means do everything needed to avoid crashes and
1323  *  memory leaks, but not much else.
1324  */
1325 static void
1326 mono_domain_unload (MonoDomain *domain)
1327 {
1328         HANDLE thread_handle;
1329         gsize tid;
1330         guint32 res;
1331         MonoAppDomainState prev_state;
1332         MonoMethod *method;
1333         MonoObject *exc;
1334         unload_data thread_data;
1335         MonoDomain *caller_domain = mono_domain_get ();
1336
1337         /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
1338
1339         /* Atomically change our state to UNLOADING */
1340         prev_state = InterlockedCompareExchange ((gint32*)&domain->state,
1341                                                                                          MONO_APPDOMAIN_UNLOADING,
1342                                                                                          MONO_APPDOMAIN_CREATED);
1343         if (prev_state != MONO_APPDOMAIN_CREATED) {
1344                 if (prev_state == MONO_APPDOMAIN_UNLOADING)
1345                         mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1346                 else
1347                         if (prev_state == MONO_APPDOMAIN_UNLOADED)
1348                                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1349                 else
1350                         g_assert_not_reached ();
1351         }
1352
1353         mono_domain_set (domain, FALSE);
1354         /* Notify OnDomainUnload listeners */
1355         method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1); 
1356         g_assert (method);
1357
1358         exc = NULL;
1359         mono_runtime_invoke (method, domain->domain, NULL, &exc);
1360         if (exc) {
1361                 /* Roll back the state change */
1362                 domain->state = MONO_APPDOMAIN_CREATED;
1363                 mono_domain_set (caller_domain, FALSE);
1364                 mono_raise_exception ((MonoException*)exc);
1365         }
1366
1367         thread_data.domain = domain;
1368         thread_data.failure_reason = NULL;
1369
1370         /* 
1371          * First we create a separate thread for unloading, since
1372          * we might have to abort some threads, including the current one.
1373          */
1374         /*
1375          * If we create a non-suspended thread, the runtime will hang.
1376          * See:
1377          * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1378          */ 
1379 #if 0
1380         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
1381 #else
1382         thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1383         ResumeThread (thread_handle);
1384 #endif
1385
1386         /* Wait for the thread */       
1387         while ((res = WaitForSingleObjectEx (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION)) {
1388                 if (mono_thread_has_appdomain_ref (mono_thread_current (), domain) && (mono_thread_interruption_requested ()))
1389                         /* The unload thread tries to abort us */
1390                         /* The icall wrapper will execute the abort */
1391                         return;
1392         }
1393
1394         mono_domain_set (caller_domain, FALSE);
1395
1396         if (thread_data.failure_reason) {
1397                 MonoException *ex;
1398
1399                 /* Roll back the state change */
1400                 domain->state = MONO_APPDOMAIN_CREATED;
1401
1402                 g_warning (thread_data.failure_reason);
1403
1404                 ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
1405
1406                 g_free (thread_data.failure_reason);
1407                 thread_data.failure_reason = NULL;
1408
1409                 mono_raise_exception (ex);
1410         }
1411 }