2 * appdomain.c: AppDomain functions
5 * Dietmar Maurer (dietmar@ximian.com)
7 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 * (c) 2001-2003 Ximian, Inc. (http://www.ximian.com)
11 #undef ASSEMBLY_LOAD_DEBUG
18 #include <sys/types.h>
20 #include <mono/os/gc_wrapper.h>
22 #include <mono/metadata/object.h>
23 #include <mono/metadata/domain-internals.h>
24 #include "mono/metadata/metadata-internals.h"
25 #include <mono/metadata/assembly.h>
26 #include <mono/metadata/exception.h>
27 #include <mono/metadata/threads.h>
28 #include <mono/metadata/socket-io.h>
29 #include <mono/metadata/tabledefs.h>
30 #include <mono/metadata/gc-internal.h>
31 #include <mono/metadata/mono-gc.h>
32 #include <mono/metadata/marshal.h>
33 #include <mono/metadata/monitor.h>
34 #include <mono/metadata/threadpool.h>
35 #include <mono/metadata/mono-debug.h>
36 #include <mono/utils/mono-uri.h>
37 #include <mono/utils/mono-logger.h>
38 #include <mono/utils/mono-path.h>
39 #include <mono/utils/mono-stdlib.h>
44 #define MONO_CORLIB_VERSION 54
46 CRITICAL_SECTION mono_delegate_section
;
48 static gunichar2 process_guid
[36];
49 static gboolean process_guid_set
= FALSE
;
51 static gboolean shutting_down
= FALSE
;
54 mono_domain_assembly_preload (MonoAssemblyName
*aname
,
55 gchar
**assemblies_path
,
59 mono_domain_assembly_search (MonoAssemblyName
*aname
,
63 mono_domain_assembly_postload_search (MonoAssemblyName
*aname
,
67 mono_domain_fire_assembly_load (MonoAssembly
*assembly
, gpointer user_data
);
70 add_assemblies_to_domain (MonoDomain
*domain
, MonoAssembly
*ass
, GHashTable
*hash
);
73 mono_domain_unload (MonoDomain
*domain
);
77 * @domain: domain returned by mono_init ()
79 * Initialize the core AppDomain: this function will run also some
80 * IL initialization code, so it needs the execution engine to be fully
83 * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
84 * we know the entry_assembly.
88 mono_runtime_init (MonoDomain
*domain
, MonoThreadStartCB start_cb
,
89 MonoThreadAttachCB attach_cb
)
91 MonoAppDomainSetup
*setup
;
98 mono_thread_pool_init ();
101 mono_install_assembly_preload_hook (mono_domain_assembly_preload
, GUINT_TO_POINTER (FALSE
));
102 mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload
, GUINT_TO_POINTER (TRUE
));
103 mono_install_assembly_search_hook (mono_domain_assembly_search
, GUINT_TO_POINTER (FALSE
));
104 mono_install_assembly_refonly_search_hook (mono_domain_assembly_search
, GUINT_TO_POINTER (TRUE
));
105 mono_install_assembly_postload_search_hook (mono_domain_assembly_postload_search
, GUINT_TO_POINTER (FALSE
));
106 mono_install_assembly_postload_refonly_search_hook (mono_domain_assembly_postload_search
, GUINT_TO_POINTER (TRUE
));
107 mono_install_assembly_load_hook (mono_domain_fire_assembly_load
, NULL
);
108 mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token
);
110 mono_thread_init (start_cb
, attach_cb
);
112 class = mono_class_from_name (mono_defaults
.corlib
, "System", "AppDomainSetup");
113 setup
= (MonoAppDomainSetup
*) mono_object_new (domain
, class);
115 class = mono_class_from_name (mono_defaults
.corlib
, "System", "AppDomain");
116 ad
= (MonoAppDomain
*) mono_object_new (domain
, class);
119 domain
->setup
= setup
;
121 InitializeCriticalSection (&mono_delegate_section
);
123 mono_thread_attach (domain
);
124 mono_context_init (domain
);
125 mono_context_set (domain
->default_context
);
127 mono_type_initialization_init ();
131 * Create an instance early since we can't do it when there is no memory.
133 arg
= mono_string_new (domain
, "Out of memory");
134 domain
->out_of_memory_ex
= mono_exception_from_name_two_strings (mono_defaults
.corlib
, "System", "OutOfMemoryException", arg
, NULL
);
137 * These two are needed because the signal handlers might be executing on
138 * an alternate stack, and Boehm GC can't handle that.
140 arg
= mono_string_new (domain
, "A null value was found where an object instance was required");
141 domain
->null_reference_ex
= mono_exception_from_name_two_strings (mono_defaults
.corlib
, "System", "NullReferenceException", arg
, NULL
);
142 arg
= mono_string_new (domain
, "The requested operation caused a stack overflow.");
143 domain
->stack_overflow_ex
= mono_exception_from_name_two_strings (mono_defaults
.corlib
, "System", "StackOverflowException", arg
, NULL
);
145 /* GC init has to happen after thread init */
148 mono_network_init ();
150 /* mscorlib is loaded before we install the load hook */
151 mono_domain_fire_assembly_load (mono_defaults
.corlib
->assembly
, NULL
);
157 mono_get_corlib_version (void)
160 MonoClassField
*field
;
163 klass
= mono_class_from_name (mono_defaults
.corlib
, "System", "Environment");
164 mono_class_init (klass
);
165 field
= mono_class_get_field_from_name (klass
, "mono_corlib_version");
168 if (! (field
->type
->attrs
& FIELD_ATTRIBUTE_STATIC
))
170 value
= mono_field_get_value_object (mono_domain_get (), field
, NULL
);
171 return *(gint32
*)((gchar
*)value
+ sizeof (MonoObject
));
175 mono_check_corlib_version (void)
177 int version
= mono_get_corlib_version ();
178 if (version
!= MONO_CORLIB_VERSION
)
179 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION
, version
);
185 mono_context_init (MonoDomain
*domain
)
188 MonoAppContext
*context
;
190 class = mono_class_from_name (mono_defaults
.corlib
, "System.Runtime.Remoting.Contexts", "Context");
191 context
= (MonoAppContext
*) mono_object_new (domain
, class);
192 context
->domain_id
= domain
->domain_id
;
193 context
->context_id
= 0;
194 domain
->default_context
= context
;
198 * mono_runtime_cleanup:
203 * This must not be called while there are still running threads executing
207 mono_runtime_cleanup (MonoDomain
*domain
)
209 shutting_down
= TRUE
;
211 /* This ends up calling any pending pending (for at most 2 seconds) */
214 mono_thread_cleanup ();
216 mono_network_cleanup ();
218 mono_marshal_cleanup ();
220 mono_type_initialization_cleanup ();
222 mono_monitor_cleanup ();
225 static MonoDomainFunc quit_function
= NULL
;
228 mono_install_runtime_cleanup (MonoDomainFunc func
)
230 quit_function
= func
;
236 if (quit_function
!= NULL
)
237 quit_function (mono_get_root_domain (), NULL
);
241 * mono_runtime_set_shutting_down:
243 * Invoked by System.Environment.Exit to flag that the runtime
247 mono_runtime_set_shutting_down (void)
249 shutting_down
= TRUE
;
253 * mono_runtime_is_shutting_down:
255 * Returns whether the runtime has been flagged for shutdown.
257 * This is consumed by the P:System.Environment.HasShutdownStarted
262 mono_runtime_is_shutting_down (void)
264 return shutting_down
;
268 * mono_domain_has_type_resolve:
269 * @domain: application domains being looked up
271 * Returns true if the AppDomain.TypeResolve field has been
275 mono_domain_has_type_resolve (MonoDomain
*domain
)
277 static MonoClassField
*field
= NULL
;
281 field
= mono_class_get_field_from_name (mono_defaults
.appdomain_class
, "TypeResolve");
285 mono_field_get_value ((MonoObject
*)(domain
->domain
), field
, &o
);
290 * mono_domain_try_type_resolve:
291 * @domain: application domainwhere the name where the type is going to be resolved
292 * @name: the name of the type to resolve or NULL.
293 * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
295 * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
296 * the assembly that matches name.
298 * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
300 * Returns: A MonoReflectionAssembly or NULL if not found
302 MonoReflectionAssembly
*
303 mono_domain_try_type_resolve (MonoDomain
*domain
, char *name
, MonoObject
*tb
)
307 static MonoMethod
*method
= NULL
;
309 g_assert (domain
!= NULL
&& ((name
!= NULL
) || (tb
!= NULL
)));
311 if (method
== NULL
) {
312 klass
= domain
->domain
->mbr
.obj
.vtable
->klass
;
315 method
= mono_class_get_method_from_name (klass
, "DoTypeResolve", -1);
316 if (method
== NULL
) {
317 g_warning ("Method AppDomain.DoTypeResolve not found.\n");
323 *params
= (MonoObject
*)mono_string_new (mono_domain_get (), name
);
326 return (MonoReflectionAssembly
*) mono_runtime_invoke (method
, domain
->domain
, params
, NULL
);
330 * mono_domain_owns_vtable_slot:
332 * Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
335 mono_domain_owns_vtable_slot (MonoDomain
*domain
, gpointer vtable_slot
)
339 mono_domain_lock (domain
);
340 res
= mono_mempool_contains_addr (domain
->mp
, vtable_slot
);
341 mono_domain_unlock (domain
);
348 * @force: force setting.
350 * Set the current appdomain to @domain. If @force is set, set it even
351 * if it is being unloaded.
355 * FALSE if the domain is unloaded
358 mono_domain_set (MonoDomain
*domain
, gboolean force
)
360 if (!force
&& domain
->state
== MONO_APPDOMAIN_UNLOADED
)
363 mono_domain_set_internal (domain
);
369 ves_icall_System_AppDomain_GetData (MonoAppDomain
*ad
, MonoString
*name
)
377 g_assert (ad
!= NULL
);
379 g_assert (add
!= NULL
);
382 mono_raise_exception (mono_get_exception_argument_null ("name"));
384 str
= mono_string_to_utf8 (name
);
386 mono_domain_lock (add
);
388 if (!strcmp (str
, "APPBASE"))
389 o
= (MonoObject
*)add
->setup
->application_base
;
390 else if (!strcmp (str
, "APP_CONFIG_FILE"))
391 o
= (MonoObject
*)add
->setup
->configuration_file
;
392 else if (!strcmp (str
, "DYNAMIC_BASE"))
393 o
= (MonoObject
*)add
->setup
->dynamic_base
;
394 else if (!strcmp (str
, "APP_NAME"))
395 o
= (MonoObject
*)add
->setup
->application_name
;
396 else if (!strcmp (str
, "CACHE_BASE"))
397 o
= (MonoObject
*)add
->setup
->cache_path
;
398 else if (!strcmp (str
, "PRIVATE_BINPATH"))
399 o
= (MonoObject
*)add
->setup
->private_bin_path
;
400 else if (!strcmp (str
, "BINPATH_PROBE_ONLY"))
401 o
= (MonoObject
*)add
->setup
->private_bin_path_probe
;
402 else if (!strcmp (str
, "SHADOW_COPY_DIRS"))
403 o
= (MonoObject
*)add
->setup
->shadow_copy_directories
;
404 else if (!strcmp (str
, "FORCE_CACHE_INSTALL"))
405 o
= (MonoObject
*)add
->setup
->shadow_copy_files
;
407 o
= mono_g_hash_table_lookup (add
->env
, name
);
409 mono_domain_unlock (add
);
419 ves_icall_System_AppDomain_SetData (MonoAppDomain
*ad
, MonoString
*name
, MonoObject
*data
)
425 g_assert (ad
!= NULL
);
427 g_assert (add
!= NULL
);
430 mono_raise_exception (mono_get_exception_argument_null ("name"));
432 mono_domain_lock (add
);
434 mono_g_hash_table_insert (add
->env
, name
, data
);
436 mono_domain_unlock (add
);
440 ves_icall_System_AppDomain_getSetup (MonoAppDomain
*ad
)
444 g_assert (ad
!= NULL
);
445 g_assert (ad
->data
!= NULL
);
447 return ad
->data
->setup
;
451 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain
*ad
)
455 g_assert (ad
!= NULL
);
456 g_assert (ad
->data
!= NULL
);
458 return mono_string_new (ad
->data
, ad
->data
->friendly_name
);
462 ves_icall_System_AppDomain_getCurDomain ()
464 MonoDomain
*add
= mono_domain_get ();
472 ves_icall_System_AppDomain_getRootDomain ()
474 MonoDomain
*root
= mono_get_root_domain ();
482 ves_icall_System_AppDomain_createDomain (MonoString
*friendly_name
, MonoAppDomainSetup
*setup
)
490 adclass
= mono_class_from_name (mono_defaults
.corlib
, "System", "AppDomain");
492 /* FIXME: pin all those objects */
493 data
= mono_domain_create();
495 ad
= (MonoAppDomain
*) mono_object_new (data
, adclass
);
499 data
->friendly_name
= mono_string_to_utf8 (friendly_name
);
500 data
->out_of_memory_ex
= mono_exception_from_name_domain (data
, mono_defaults
.corlib
, "System", "OutOfMemoryException");
502 if (!setup
->application_base
) {
503 /* Inherit from the root domain since MS.NET does this */
504 MonoDomain
*root
= mono_get_root_domain ();
505 if (root
->setup
->application_base
)
506 MONO_OBJECT_SETREF (setup
, application_base
, mono_string_new_utf16 (data
, mono_string_chars (root
->setup
->application_base
), mono_string_length (root
->setup
->application_base
)));
509 mono_context_init (data
);
511 add_assemblies_to_domain (data
, mono_defaults
.corlib
->assembly
, NULL
);
517 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain
*ad
, MonoBoolean refonly
)
519 MonoDomain
*domain
= ad
->data
;
521 static MonoClass
*System_Reflection_Assembly
;
528 if (!System_Reflection_Assembly
)
529 System_Reflection_Assembly
= mono_class_from_name (
530 mono_defaults
.corlib
, "System.Reflection", "Assembly");
533 /* Need to skip internal assembly builders created by remoting */
534 mono_domain_assemblies_lock (domain
);
535 for (tmp
= domain
->domain_assemblies
; tmp
; tmp
= tmp
->next
) {
537 if (refonly
&& !ass
->ref_only
)
539 if (!ass
->corlib_internal
)
542 res
= mono_array_new (domain
, System_Reflection_Assembly
, count
);
544 for (tmp
= domain
->domain_assemblies
; tmp
; tmp
= tmp
->next
) {
546 if (refonly
&& !ass
->ref_only
)
548 if (ass
->corlib_internal
)
550 mono_array_setref (res
, i
, mono_assembly_get_object (domain
, ass
));
553 mono_domain_assemblies_unlock (domain
);
558 static MonoReflectionAssembly
*
559 try_assembly_resolve (MonoDomain
*domain
, MonoString
*fname
, gboolean refonly
)
563 MonoBoolean isrefonly
;
566 g_assert (domain
!= NULL
&& fname
!= NULL
);
568 klass
= domain
->domain
->mbr
.obj
.vtable
->klass
;
571 method
= mono_class_get_method_from_name (klass
, "DoAssemblyResolve", -1);
572 if (method
== NULL
) {
573 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
577 isrefonly
= refonly
? 1 : 0;
579 params
[1] = &isrefonly
;
580 return (MonoReflectionAssembly
*) mono_runtime_invoke (method
, domain
->domain
, params
, NULL
);
583 static MonoAssembly
*
584 mono_domain_assembly_postload_search (MonoAssemblyName
*aname
,
587 gboolean refonly
= GPOINTER_TO_UINT (user_data
);
588 MonoReflectionAssembly
*assembly
;
589 MonoDomain
*domain
= mono_domain_get ();
592 aname_str
= mono_stringify_assembly_name (aname
);
594 /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
595 assembly
= try_assembly_resolve (domain
, mono_string_new (domain
, aname_str
), refonly
);
600 return assembly
->assembly
;
606 * LOCKING: assumes assemblies_lock in the domain is already locked.
609 add_assemblies_to_domain (MonoDomain
*domain
, MonoAssembly
*ass
, GHashTable
*ht
)
613 gboolean destroy_ht
= FALSE
;
615 if (!ass
->aname
.name
)
619 ht
= g_hash_table_new (mono_aligned_addr_hash
, NULL
);
623 /* FIXME: handle lazy loaded assemblies */
624 for (tmp
= domain
->domain_assemblies
; tmp
; tmp
= tmp
->next
) {
625 g_hash_table_insert (ht
, tmp
->data
, tmp
->data
);
627 if (!g_hash_table_lookup (ht
, ass
)) {
628 mono_assembly_addref (ass
);
629 g_hash_table_insert (ht
, ass
, ass
);
630 domain
->domain_assemblies
= g_slist_prepend (domain
->domain_assemblies
, ass
);
631 mono_trace (G_LOG_LEVEL_INFO
, MONO_TRACE_ASSEMBLY
, "Assembly %s %p added to domain %s, ref_count=%d\n", ass
->aname
.name
, ass
, domain
->friendly_name
, ass
->ref_count
);
634 if (ass
->image
->references
) {
635 for (i
= 0; ass
->image
->references
[i
] != NULL
; i
++) {
636 if (!g_hash_table_lookup (ht
, ass
->image
->references
[i
])) {
637 add_assemblies_to_domain (domain
, ass
->image
->references
[i
], ht
);
642 g_hash_table_destroy (ht
);
646 mono_domain_fire_assembly_load (MonoAssembly
*assembly
, gpointer user_data
)
648 static MonoClassField
*assembly_load_field
;
649 static MonoMethod
*assembly_load_method
;
650 MonoDomain
*domain
= mono_domain_get ();
651 MonoReflectionAssembly
*ref_assembly
;
657 /* This can happen during startup */
659 #ifdef ASSEMBLY_LOAD_DEBUG
660 fprintf (stderr
, "Loading %s into domain %s\n", assembly
->aname
.name
, domain
->friendly_name
);
662 klass
= domain
->domain
->mbr
.obj
.vtable
->klass
;
664 mono_domain_assemblies_lock (domain
);
665 add_assemblies_to_domain (domain
, assembly
, NULL
);
666 mono_domain_assemblies_unlock (domain
);
668 if (assembly_load_field
== NULL
) {
669 assembly_load_field
= mono_class_get_field_from_name (klass
, "AssemblyLoad");
670 g_assert (assembly_load_field
);
673 mono_field_get_value ((MonoObject
*) domain
->domain
, assembly_load_field
, &load_value
);
674 if (load_value
== NULL
) {
675 /* No events waiting to be triggered */
679 ref_assembly
= mono_assembly_get_object (domain
, assembly
);
680 g_assert (ref_assembly
);
682 if (assembly_load_method
== NULL
) {
683 assembly_load_method
= mono_class_get_method_from_name (klass
, "DoAssemblyLoad", -1);
684 g_assert (assembly_load_method
);
687 *params
= ref_assembly
;
688 mono_runtime_invoke (assembly_load_method
, domain
->domain
, params
, NULL
);
692 set_domain_search_path (MonoDomain
*domain
)
694 MonoAppDomainSetup
*setup
;
699 gchar
**pvt_split
= NULL
;
700 GError
*error
= NULL
;
701 gint appbaselen
= -1;
703 if ((domain
->search_path
!= NULL
) && !domain
->setup
->path_changed
)
708 setup
= domain
->setup
;
709 if (!setup
->application_base
)
710 return; /* Must set application base to get private path working */
713 if (setup
->private_bin_path
) {
714 utf8
= mono_string_to_utf8 (setup
->private_bin_path
);
715 pvt_split
= g_strsplit (utf8
, G_SEARCHPATH_SEPARATOR_S
, 1000);
717 for (tmp
= pvt_split
; *tmp
; tmp
++, npaths
++);
722 g_strfreev (pvt_split
);
724 * Don't do this because the first time is called, the domain
725 * setup is not finished.
727 * domain->search_path = g_malloc (sizeof (char *));
728 * domain->search_path [0] = NULL;
733 if (domain
->search_path
)
734 g_strfreev (domain
->search_path
);
736 domain
->search_path
= tmp
= g_malloc ((npaths
+ 1) * sizeof (gchar
*));
739 *tmp
= mono_string_to_utf8 (setup
->application_base
);
741 /* FIXME: is this needed? */
742 if (strncmp (*tmp
, "file://", 7) == 0) {
748 uri
= g_strdup_printf ("file:///%s", uri
+ 7);
751 uri
= mono_escape_uri_string (tmpuri
);
752 *tmp
= g_filename_from_uri (uri
, NULL
, &error
);
759 g_warning ("%s\n", error
->message
);
760 g_error_free (error
);
767 for (i
= 1; pvt_split
&& i
< npaths
; i
++) {
768 if (g_path_is_absolute (pvt_split
[i
- 1])) {
769 tmp
[i
] = g_strdup (pvt_split
[i
- 1]);
771 tmp
[i
] = g_build_filename (tmp
[0], pvt_split
[i
- 1], NULL
);
774 if (strchr (tmp
[i
], '.')) {
778 reduced
= mono_path_canonicalize (tmp
[i
]);
779 if (appbaselen
== -1)
780 appbaselen
= strlen (tmp
[0]);
782 if (strncmp (tmp
[0], reduced
, appbaselen
)) {
785 tmp
[i
] = g_strdup ("");
795 if (setup
->private_bin_path_probe
!= NULL
) {
797 tmp
[0] = g_strdup ("");
800 domain
->setup
->path_changed
= FALSE
;
802 g_strfreev (pvt_split
);
806 shadow_copy_sibling (gchar
*src
, gint srclen
, const char *extension
, gchar
*target
, gint targetlen
, gint tail_len
)
808 guint16
*orig
, *dest
;
809 gboolean copy_result
;
811 strcpy (src
+ srclen
- tail_len
, extension
);
812 if (!g_file_test (src
, G_FILE_TEST_IS_REGULAR
))
814 orig
= g_utf8_to_utf16 (src
, strlen (src
), NULL
, NULL
, NULL
);
816 strcpy (target
+ targetlen
- tail_len
, extension
);
817 dest
= g_utf8_to_utf16 (target
, strlen (target
), NULL
, NULL
, NULL
);
819 copy_result
= CopyFile (orig
, dest
, FALSE
);
827 get_cstring_hash (const char *str
)
833 if (!str
|| !str
[0])
838 for (i
= 0; i
< len
; i
++) {
839 h
= (h
<< 5) - h
+ *p
;
847 get_shadow_assembly_location (const char *filename
)
849 gint32 hash
= 0, hash2
= 0;
852 char *bname
= g_path_get_basename (filename
);
853 MonoDomain
*domain
= mono_domain_get ();
855 hash
= get_cstring_hash (bname
);
856 hash2
= get_cstring_hash (g_path_get_dirname (filename
));
857 g_snprintf (name_hash
, sizeof (name_hash
), "%08x", hash
);
858 g_snprintf (path_hash
, sizeof (path_hash
), "%08x_%08x", hash
^ hash2
, hash2
);
859 return g_build_filename (mono_string_to_utf8 (domain
->setup
->dynamic_base
),
869 ensure_directory_exists (const char *filename
)
871 #ifdef PLATFORM_WIN32
872 gchar
*dir_utf8
= g_path_get_dirname (filename
);
874 gunichar2
*dir_utf16
= NULL
;
877 if (!dir_utf8
|| !dir_utf8
[0])
880 dir_utf16
= g_utf8_to_utf16 (dir_utf8
, strlen (dir_utf8
), NULL
, NULL
, NULL
);
888 /* make life easy and only use one directory seperator */
905 p
= wcschr (p
, '\\');
908 retval
= _wmkdir (dir_utf16
);
909 if (retval
!= 0 && errno
!= EEXIST
) {
922 gchar
*dir
= g_path_get_dirname (filename
);
925 if (!dir
|| !dir
[0])
936 retval
= mkdir (dir
, 0777);
937 if (retval
!= 0 && errno
!= EEXIST
) {
952 make_shadow_copy (const char *filename
)
954 gchar
*sibling_source
, *sibling_target
;
955 gint sibling_source_len
, sibling_target_len
;
956 guint16
*orig
, *dest
;
958 gboolean copy_result
;
961 shadow
= get_shadow_assembly_location (filename
);
962 if (ensure_directory_exists (shadow
) == FALSE
) {
963 exc
= mono_get_exception_execution_engine ("Failed to create shadow copy (ensure directory exists).");
964 mono_raise_exception (exc
);
967 orig
= g_utf8_to_utf16 (filename
, strlen (filename
), NULL
, NULL
, NULL
);
968 dest
= g_utf8_to_utf16 (shadow
, strlen (shadow
), NULL
, NULL
, NULL
);
969 copy_result
= CopyFile (orig
, dest
, FALSE
);
973 if (copy_result
== FALSE
) {
975 exc
= mono_get_exception_execution_engine ("Failed to create shadow copy (CopyFile).");
976 mono_raise_exception (exc
);
979 /* attempt to copy .mdb, .config if they exist */
980 sibling_source
= g_strconcat (filename
, ".config", NULL
);
981 sibling_source_len
= strlen (sibling_source
);
982 sibling_target
= g_strconcat (shadow
, ".config", NULL
);
983 sibling_target_len
= strlen (sibling_target
);
985 copy_result
= shadow_copy_sibling (sibling_source
, sibling_source_len
, ".mdb", sibling_target
, sibling_target_len
, 7);
986 if (copy_result
== TRUE
)
987 copy_result
= shadow_copy_sibling (sibling_source
, sibling_source_len
, ".config", sibling_target
, sibling_target_len
, 7);
989 g_free (sibling_source
);
990 g_free (sibling_target
);
992 if (copy_result
== FALSE
) {
994 exc
= mono_get_exception_execution_engine ("Failed to create shadow copy of sibling data (CopyFile).");
995 mono_raise_exception (exc
);
1002 try_load_from (MonoAssembly
**assembly
, const gchar
*path1
, const gchar
*path2
,
1003 const gchar
*path3
, const gchar
*path4
,
1004 gboolean refonly
, gboolean is_private
)
1010 fullpath
= g_build_filename (path1
, path2
, path3
, path4
, NULL
);
1011 if (g_file_test (fullpath
, G_FILE_TEST_IS_REGULAR
)) {
1013 char *new_path
= make_shadow_copy (fullpath
);
1015 fullpath
= new_path
;
1018 *assembly
= mono_assembly_open_full (fullpath
, NULL
, refonly
);
1022 return (*assembly
!= NULL
);
1025 static MonoAssembly
*
1026 real_load (gchar
**search_path
, const gchar
*culture
, const gchar
*name
, gboolean refonly
)
1028 MonoAssembly
*result
= NULL
;
1031 const gchar
*local_culture
;
1033 gboolean is_private
= FALSE
;
1035 if (!culture
|| *culture
== '\0') {
1038 local_culture
= culture
;
1041 filename
= g_strconcat (name
, ".dll", NULL
);
1042 len
= strlen (filename
);
1044 for (path
= search_path
; *path
; path
++) {
1045 if (**path
== '\0') {
1047 continue; /* Ignore empty ApplicationBase */
1050 /* See test cases in bug #58992 and bug #57710 */
1051 /* 1st try: [culture]/[name].dll (culture may be empty) */
1052 strcpy (filename
+ len
- 4, ".dll");
1053 if (try_load_from (&result
, *path
, local_culture
, "", filename
, refonly
, is_private
))
1056 /* 2nd try: [culture]/[name].exe (culture may be empty) */
1057 strcpy (filename
+ len
- 4, ".exe");
1058 if (try_load_from (&result
, *path
, local_culture
, "", filename
, refonly
, is_private
))
1061 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
1062 strcpy (filename
+ len
- 4, ".dll");
1063 if (try_load_from (&result
, *path
, local_culture
, name
, filename
, refonly
, is_private
))
1066 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
1067 strcpy (filename
+ len
- 4, ".exe");
1068 if (try_load_from (&result
, *path
, local_culture
, name
, filename
, refonly
, is_private
))
1077 * Try loading the assembly from ApplicationBase and PrivateBinPath
1078 * and then from assemblies_path if any.
1080 static MonoAssembly
*
1081 mono_domain_assembly_preload (MonoAssemblyName
*aname
,
1082 gchar
**assemblies_path
,
1085 MonoDomain
*domain
= mono_domain_get ();
1086 MonoAssembly
*result
= NULL
;
1087 gboolean refonly
= GPOINTER_TO_UINT (user_data
);
1089 set_domain_search_path (domain
);
1091 if (domain
->search_path
&& domain
->search_path
[0] != NULL
) {
1092 result
= real_load (domain
->search_path
, aname
->culture
, aname
->name
, refonly
);
1095 if (result
== NULL
&& assemblies_path
&& assemblies_path
[0] != NULL
) {
1096 result
= real_load (assemblies_path
, aname
->culture
, aname
->name
, refonly
);
1103 * Check whenever a given assembly was already loaded in the current appdomain.
1105 static MonoAssembly
*
1106 mono_domain_assembly_search (MonoAssemblyName
*aname
,
1109 MonoDomain
*domain
= mono_domain_get ();
1112 gboolean refonly
= GPOINTER_TO_UINT (user_data
);
1114 mono_domain_assemblies_lock (domain
);
1115 for (tmp
= domain
->domain_assemblies
; tmp
; tmp
= tmp
->next
) {
1117 /* Dynamic assemblies can't match here in MS.NET */
1118 if (ass
->dynamic
|| refonly
!= ass
->ref_only
|| !mono_assembly_names_equal (aname
, &ass
->aname
))
1121 mono_domain_assemblies_unlock (domain
);
1124 mono_domain_assemblies_unlock (domain
);
1129 MonoReflectionAssembly
*
1130 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString
*fname
, MonoBoolean refOnly
)
1132 MonoDomain
*domain
= mono_domain_get ();
1133 char *name
, *filename
;
1134 MonoImageOpenStatus status
= MONO_IMAGE_OK
;
1137 MONO_ARCH_SAVE_REGS
;
1139 if (fname
== NULL
) {
1140 MonoException
*exc
= mono_get_exception_argument_null ("assemblyFile");
1141 mono_raise_exception (exc
);
1144 name
= filename
= mono_string_to_utf8 (fname
);
1146 ass
= mono_assembly_open_full (filename
, &status
, refOnly
);
1151 if (status
== MONO_IMAGE_IMAGE_INVALID
)
1152 exc
= mono_get_exception_bad_image_format2 (NULL
, fname
);
1154 exc
= mono_get_exception_file_not_found2 (NULL
, fname
);
1156 mono_raise_exception (exc
);
1161 return mono_assembly_get_object (domain
, ass
);
1164 MonoReflectionAssembly
*
1165 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain
*ad
,
1166 MonoArray
*raw_assembly
,
1167 MonoArray
*raw_symbol_store
, MonoObject
*evidence
,
1168 MonoBoolean refonly
)
1171 MonoReflectionAssembly
*refass
= NULL
;
1172 MonoDomain
*domain
= ad
->data
;
1173 MonoImageOpenStatus status
;
1174 guint32 raw_assembly_len
= mono_array_length (raw_assembly
);
1175 MonoImage
*image
= mono_image_open_from_data_full (mono_array_addr (raw_assembly
, gchar
, 0), raw_assembly_len
, TRUE
, NULL
, refonly
);
1178 mono_raise_exception (mono_get_exception_bad_image_format (""));
1182 if (raw_symbol_store
!= NULL
)
1183 mono_debug_init_2_memory (image
, mono_array_addr (raw_symbol_store
, guint8
, 0), mono_array_length (raw_symbol_store
));
1185 ass
= mono_assembly_load_from_full (image
, "", &status
, refonly
);
1189 mono_image_close (image
);
1190 mono_raise_exception (mono_get_exception_bad_image_format (""));
1194 refass
= mono_assembly_get_object (domain
, ass
);
1195 MONO_OBJECT_SETREF (refass
, evidence
, evidence
);
1199 MonoReflectionAssembly
*
1200 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain
*ad
, MonoString
*assRef
, MonoObject
*evidence
, MonoBoolean refOnly
)
1202 MonoDomain
*domain
= ad
->data
;
1203 MonoImageOpenStatus status
= MONO_IMAGE_OK
;
1205 MonoAssemblyName aname
;
1206 MonoReflectionAssembly
*refass
= NULL
;
1210 MONO_ARCH_SAVE_REGS
;
1212 g_assert (assRef
!= NULL
);
1214 name
= mono_string_to_utf8 (assRef
);
1215 parsed
= mono_assembly_name_parse (name
, &aname
);
1221 /* This is a parse error... */
1222 exc
= mono_get_exception_file_not_found2 (NULL
, assRef
);
1223 mono_raise_exception (exc
);
1226 ass
= mono_assembly_load_full (&aname
, NULL
, &status
, refOnly
);
1227 mono_assembly_name_free (&aname
);
1229 if (!ass
&& (refass
= try_assembly_resolve (domain
, assRef
, refOnly
)) == NULL
){
1230 /* FIXME: it doesn't make much sense since we really don't have a filename ... */
1231 MonoException
*exc
= mono_get_exception_file_not_found2 (NULL
, assRef
);
1232 mono_raise_exception (exc
);
1236 refass
= mono_assembly_get_object (domain
, ass
);
1238 MONO_OBJECT_SETREF (refass
, evidence
, evidence
);
1243 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id
)
1245 MonoDomain
* domain
= mono_domain_get_by_id (domain_id
);
1247 MONO_ARCH_SAVE_REGS
;
1249 if (NULL
== domain
) {
1250 MonoException
*exc
= mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
1251 mono_raise_exception (exc
);
1254 if (domain
== mono_get_root_domain ()) {
1255 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
1260 * Unloading seems to cause problems when running NUnit/NAnt, hence
1263 if (g_getenv ("MONO_NO_UNLOAD"))
1266 mono_domain_unload (domain
);
1270 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id
)
1272 MonoDomain
*domain
= mono_domain_get_by_id (domain_id
);
1277 return mono_domain_is_unloading (domain
);
1281 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain
*ad
, MonoString
*file
,
1282 MonoObject
*evidence
, MonoArray
*args
)
1284 MonoAssembly
*assembly
;
1289 MonoReflectionAssembly
*refass
;
1291 MONO_ARCH_SAVE_REGS
;
1293 filename
= mono_string_to_utf8 (file
);
1294 assembly
= mono_assembly_open (filename
, NULL
);
1298 mono_raise_exception (mono_get_exception_file_not_found2 (NULL
, file
));
1300 image
= assembly
->image
;
1302 method
= mono_get_method (image
, mono_image_get_entry_point (image
), NULL
);
1305 g_error ("No entry point method found in %s", image
->name
);
1308 args
= (MonoArray
*) mono_array_new (ad
->data
, mono_defaults
.string_class
, 0);
1310 refass
= mono_assembly_get_object (ad
->data
, assembly
);
1311 MONO_OBJECT_SETREF (refass
, evidence
, evidence
);
1313 res
= mono_runtime_exec_main (method
, (MonoArray
*)args
, NULL
);
1319 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain
* ad
)
1321 MONO_ARCH_SAVE_REGS
;
1323 return ad
->data
->domain_id
;
1327 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain
*ad
)
1329 MonoDomain
*old_domain
= mono_domain_get();
1331 MONO_ARCH_SAVE_REGS
;
1333 if (!mono_domain_set (ad
->data
, FALSE
))
1334 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1336 return old_domain
->domain
;
1340 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid
)
1342 MonoDomain
*current_domain
= mono_domain_get ();
1343 MonoDomain
*domain
= mono_domain_get_by_id (domainid
);
1345 MONO_ARCH_SAVE_REGS
;
1347 if (!domain
|| !mono_domain_set (domain
, FALSE
))
1348 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1350 return current_domain
->domain
;
1354 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain
*ad
)
1356 MONO_ARCH_SAVE_REGS
;
1358 mono_thread_push_appdomain_ref (ad
->data
);
1362 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id
)
1364 MonoDomain
*domain
= mono_domain_get_by_id (domain_id
);
1366 MONO_ARCH_SAVE_REGS
;
1370 * Raise an exception to prevent the managed code from executing a pop
1373 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1375 mono_thread_push_appdomain_ref (domain
);
1379 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1381 MONO_ARCH_SAVE_REGS
;
1383 mono_thread_pop_appdomain_ref ();
1387 ves_icall_System_AppDomain_InternalGetContext ()
1389 MONO_ARCH_SAVE_REGS
;
1391 return mono_context_get ();
1395 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1397 MONO_ARCH_SAVE_REGS
;
1399 return mono_domain_get ()->default_context
;
1403 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext
*mc
)
1405 MonoAppContext
*old_context
= mono_context_get ();
1407 MONO_ARCH_SAVE_REGS
;
1409 mono_context_set (mc
);
1415 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString
* newguid
)
1417 MonoDomain
* mono_root_domain
= mono_get_root_domain ();
1418 mono_domain_lock (mono_root_domain
);
1419 if (process_guid_set
) {
1420 mono_domain_unlock (mono_root_domain
);
1421 return mono_string_new_utf16 (mono_domain_get (), process_guid
, sizeof(process_guid
)/2);
1423 memcpy (process_guid
, mono_string_chars(newguid
), sizeof(process_guid
));
1424 process_guid_set
= TRUE
;
1425 mono_domain_unlock (mono_root_domain
);
1430 mono_domain_is_unloading (MonoDomain
*domain
)
1432 if (domain
->state
== MONO_APPDOMAIN_UNLOADING
|| domain
->state
== MONO_APPDOMAIN_UNLOADED
)
1439 clear_cached_vtable (gpointer key
, gpointer value
, gpointer user_data
)
1441 MonoClass
*klass
= (MonoClass
*)key
;
1442 MonoDomain
*domain
= (MonoDomain
*)user_data
;
1443 MonoClassRuntimeInfo
*runtime_info
;
1445 runtime_info
= klass
->runtime_info
;
1446 if (runtime_info
&& runtime_info
->max_domain
>= domain
->domain_id
)
1447 runtime_info
->domain_vtables
[domain
->domain_id
] = NULL
;
1450 typedef struct unload_data
{
1452 char *failure_reason
;
1455 static guint32 WINAPI
1456 unload_thread_main (void *arg
)
1458 unload_data
*data
= (unload_data
*)arg
;
1459 MonoDomain
*domain
= data
->domain
;
1462 * FIXME: Abort our parent thread last, so we can return a failure
1463 * indication if aborting times out.
1465 if (!mono_threads_abort_appdomain_threads (domain
, 10000)) {
1466 data
->failure_reason
= g_strdup_printf ("Aborting of threads in domain %s timed out.", domain
->friendly_name
);
1470 /* Finalize all finalizable objects in the doomed appdomain */
1471 if (!mono_domain_finalize (domain
, 10000)) {
1472 data
->failure_reason
= g_strdup_printf ("Finalization of domain %s timed out.", domain
->friendly_name
);
1476 /* Clear references to our vtables in class->runtime_info.
1477 * We also hold the loader lock because we're going to change
1478 * class->runtime_info.
1480 mono_domain_lock (domain
);
1481 mono_loader_lock ();
1482 g_hash_table_foreach (domain
->class_vtable_hash
, clear_cached_vtable
, domain
);
1483 mono_loader_unlock ();
1484 mono_domain_unlock (domain
);
1486 mono_threads_clear_cached_culture (domain
);
1488 domain
->state
= MONO_APPDOMAIN_UNLOADED
;
1490 /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1492 /* remove from the handle table the items related to this domain */
1493 mono_gchandle_free_domain (domain
);
1495 mono_domain_free (domain
, FALSE
);
1497 mono_gc_collect (mono_gc_max_generation ());
1503 * mono_domain_unload:
1504 * @domain: The domain to unload
1506 * Unloads an appdomain. Follows the process outlined in:
1507 * http://blogs.gotdotnet.com/cbrumme
1509 * If doing things the 'right' way is too hard or complex, we do it the
1510 * 'simple' way, which means do everything needed to avoid crashes and
1511 * memory leaks, but not much else.
1514 mono_domain_unload (MonoDomain
*domain
)
1516 HANDLE thread_handle
;
1519 MonoAppDomainState prev_state
;
1522 unload_data thread_data
;
1523 MonoDomain
*caller_domain
= mono_domain_get ();
1525 /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
1527 /* Atomically change our state to UNLOADING */
1528 prev_state
= InterlockedCompareExchange ((gint32
*)&domain
->state
,
1529 MONO_APPDOMAIN_UNLOADING
,
1530 MONO_APPDOMAIN_CREATED
);
1531 if (prev_state
!= MONO_APPDOMAIN_CREATED
) {
1532 if (prev_state
== MONO_APPDOMAIN_UNLOADING
)
1533 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1535 if (prev_state
== MONO_APPDOMAIN_UNLOADED
)
1536 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1538 g_assert_not_reached ();
1541 mono_domain_set (domain
, FALSE
);
1542 /* Notify OnDomainUnload listeners */
1543 method
= mono_class_get_method_from_name (domain
->domain
->mbr
.obj
.vtable
->klass
, "DoDomainUnload", -1);
1547 mono_runtime_invoke (method
, domain
->domain
, NULL
, &exc
);
1549 /* Roll back the state change */
1550 domain
->state
= MONO_APPDOMAIN_CREATED
;
1551 mono_domain_set (caller_domain
, FALSE
);
1552 mono_raise_exception ((MonoException
*)exc
);
1555 thread_data
.domain
= domain
;
1556 thread_data
.failure_reason
= NULL
;
1559 * First we create a separate thread for unloading, since
1560 * we might have to abort some threads, including the current one.
1563 * If we create a non-suspended thread, the runtime will hang.
1565 * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1568 thread_handle
= CreateThread (NULL
, 0, unload_thread_main
, &thread_data
, 0, &tid
);
1570 thread_handle
= CreateThread (NULL
, 0, (LPTHREAD_START_ROUTINE
)unload_thread_main
, &thread_data
, CREATE_SUSPENDED
, &tid
);
1571 if (thread_handle
== NULL
) {
1574 ResumeThread (thread_handle
);
1577 /* Wait for the thread */
1578 while ((res
= WaitForSingleObjectEx (thread_handle
, INFINITE
, TRUE
) == WAIT_IO_COMPLETION
)) {
1579 if (mono_thread_has_appdomain_ref (mono_thread_current (), domain
) && (mono_thread_interruption_requested ()))
1580 /* The unload thread tries to abort us */
1581 /* The icall wrapper will execute the abort */
1582 CloseHandle (thread_handle
);
1585 CloseHandle (thread_handle
);
1587 mono_domain_set (caller_domain
, FALSE
);
1589 if (thread_data
.failure_reason
) {
1592 /* Roll back the state change */
1593 domain
->state
= MONO_APPDOMAIN_CREATED
;
1595 g_warning (thread_data
.failure_reason
);
1597 ex
= mono_get_exception_cannot_unload_appdomain (thread_data
.failure_reason
);
1599 g_free (thread_data
.failure_reason
);
1600 thread_data
.failure_reason
= NULL
;
1602 mono_raise_exception (ex
);