1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 /*=============================================================================
9 ** Purpose: Domains represent an application within the runtime. Objects can
10 ** not be shared between domains and each domain can be configured
14 =============================================================================*/
19 using System.Reflection;
21 using System.Runtime.CompilerServices;
22 using System.Security;
23 using System.Collections;
24 using System.Collections.Generic;
25 using System.Threading;
26 using System.Runtime.InteropServices;
27 using System.Reflection.Emit;
28 using CultureInfo = System.Globalization.CultureInfo;
30 using AssemblyHashAlgorithm = System.Configuration.Assemblies.AssemblyHashAlgorithm;
32 using System.Runtime.ConstrainedExecution;
33 using System.Runtime.Versioning;
34 using System.Diagnostics;
35 using System.Diagnostics.Contracts;
36 using System.Runtime.ExceptionServices;
38 internal sealed class AppDomain
40 // Domain security information
41 // These fields initialized from the other side only. (NOTE: order
42 // of these fields cannot be changed without changing the layout in
43 // the EE- AppDomainBaseObject in this case)
45 private AppDomainManager _domainManager;
46 private Dictionary<String, Object> _LocalStore;
47 private AppDomainSetup _FusionStore;
48 public event AssemblyLoadEventHandler AssemblyLoad;
50 private ResolveEventHandler _TypeResolve;
52 public event ResolveEventHandler TypeResolve
58 _TypeResolve += value;
66 _TypeResolve -= value;
71 private ResolveEventHandler _ResourceResolve;
73 public event ResolveEventHandler ResourceResolve
79 _ResourceResolve += value;
87 _ResourceResolve -= value;
92 private ResolveEventHandler _AssemblyResolve;
94 public event ResolveEventHandler AssemblyResolve
100 _AssemblyResolve += value;
108 _AssemblyResolve -= value;
114 private EventHandler _processExit;
116 private EventHandler _domainUnload;
118 private UnhandledExceptionEventHandler _unhandledException;
120 // The compat flags are set at domain creation time to indicate that the given breaking
121 // changes (named in the strings) should not be used in this domain. We only use the
122 // keys, the vhe values are ignored.
123 private Dictionary<String, object> _compatFlags;
125 // Delegate that will hold references to FirstChance exception notifications
126 private EventHandler<FirstChanceExceptionEventArgs> _firstChanceException;
128 private IntPtr _pDomain; // this is an unmanaged pointer (AppDomain * m_pDomain)` used from the VM.
130 private bool _compatFlagsInitialized;
132 internal const String TargetFrameworkNameAppCompatSetting = "TargetFrameworkName";
135 private static APPX_FLAGS s_flags;
138 // Keep in async with vm\appdomainnative.cpp
141 private enum APPX_FLAGS
143 APPX_FLAGS_INITIALIZED = 0x01,
145 APPX_FLAGS_APPX_MODEL = 0x02,
146 APPX_FLAGS_APPX_DESIGN_MODE = 0x04,
147 APPX_FLAGS_APPX_MASK = APPX_FLAGS_APPX_MODEL |
148 APPX_FLAGS_APPX_DESIGN_MODE,
151 private static APPX_FLAGS Flags
156 s_flags = nGetAppXFlags();
158 Debug.Assert(s_flags != 0);
162 #endif // FEATURE_APPX
165 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
166 [return: MarshalAs(UnmanagedType.I4)]
167 private static extern APPX_FLAGS nGetAppXFlags();
171 /// Get a handle used to make a call into the VM pointing to this domain
173 internal AppDomainHandle GetNativeHandle()
175 // This should never happen under normal circumstances. However, there ar ways to create an
176 // uninitialized object through remoting, etc.
177 if (_pDomain.IsNull())
179 throw new InvalidOperationException(SR.Argument_InvalidHandle);
182 return new AppDomainHandle(_pDomain);
186 /// If this AppDomain is configured to have an AppDomain manager then create the instance of it.
187 /// This method is also called from the VM to create the domain manager in the default domain.
189 private void CreateAppDomainManager()
191 Debug.Assert(_domainManager == null, "_domainManager == null");
193 AppDomainSetup adSetup = FusionStore;
194 String trustedPlatformAssemblies = (String)(GetData("TRUSTED_PLATFORM_ASSEMBLIES"));
195 if (trustedPlatformAssemblies != null)
197 String platformResourceRoots = (String)(GetData("PLATFORM_RESOURCE_ROOTS"));
198 if (platformResourceRoots == null)
200 platformResourceRoots = String.Empty;
203 String appPaths = (String)(GetData("APP_PATHS"));
204 if (appPaths == null)
206 appPaths = String.Empty;
209 String appNiPaths = (String)(GetData("APP_NI_PATHS"));
210 if (appNiPaths == null)
212 appNiPaths = String.Empty;
215 String appLocalWinMD = (String)(GetData("APP_LOCAL_WINMETADATA"));
216 if (appLocalWinMD == null)
218 appLocalWinMD = String.Empty;
220 SetupBindingPaths(trustedPlatformAssemblies, platformResourceRoots, appPaths, appNiPaths, appLocalWinMD);
223 InitializeCompatibilityFlags();
227 /// Initialize the compatibility flags to non-NULL values.
228 /// This method is also called from the VM when the default domain doesn't have a domain manager.
230 private void InitializeCompatibilityFlags()
232 AppDomainSetup adSetup = FusionStore;
234 // set up shim flags regardless of whether we create a DomainManager in this method.
235 if (adSetup.GetCompatibilityFlags() != null)
237 _compatFlags = new Dictionary<String, object>(adSetup.GetCompatibilityFlags(), StringComparer.OrdinalIgnoreCase);
240 // for perf, we don't intialize the _compatFlags dictionary when we don't need to. However, we do need to make a
241 // note that we've run this method, because IsCompatibilityFlagsSet needs to return different values for the
242 // case where the compat flags have been setup.
243 Debug.Assert(!_compatFlagsInitialized);
244 _compatFlagsInitialized = true;
248 /// Returns whether the current AppDomain follows the AppX rules.
251 internal static bool IsAppXModel()
254 return (Flags & APPX_FLAGS.APPX_FLAGS_APPX_MODEL) != 0;
261 /// Returns the setting of the AppXDevMode config switch.
264 internal static bool IsAppXDesignMode()
267 return (Flags & APPX_FLAGS.APPX_FLAGS_APPX_MASK) == (APPX_FLAGS.APPX_FLAGS_APPX_MODEL | APPX_FLAGS.APPX_FLAGS_APPX_DESIGN_MODE);
274 /// Checks (and throws on failure) if the domain supports Assembly.LoadFrom.
277 internal static void CheckLoadFromSupported()
281 throw new NotSupportedException(SR.Format(SR.NotSupported_AppX, "Assembly.LoadFrom"));
286 /// Checks (and throws on failure) if the domain supports Assembly.LoadFile.
289 internal static void CheckLoadFileSupported()
293 throw new NotSupportedException(SR.Format(SR.NotSupported_AppX, "Assembly.LoadFile"));
298 /// Checks (and throws on failure) if the domain supports Assembly.Load(byte[] ...).
301 internal static void CheckLoadByteArraySupported()
305 throw new NotSupportedException(SR.Format(SR.NotSupported_AppX, "Assembly.Load(byte[], ...)"));
309 public AppDomainManager DomainManager
313 return _domainManager;
317 public static AppDomain CurrentDomain
321 return Thread.GetDomain();
325 public String BaseDirectory
329 return FusionStore.ApplicationBase;
333 public override String ToString()
335 StringBuilder sb = StringBuilderCache.Acquire();
337 String fn = nGetFriendlyName();
340 sb.Append(SR.Loader_Name + fn);
341 sb.Append(Environment.NewLine);
344 return StringBuilderCache.GetStringAndRelease(sb);
347 [MethodImpl(MethodImplOptions.InternalCall)]
348 private extern Assembly[] nGetAssemblies(bool forIntrospection);
350 internal Assembly[] GetAssemblies(bool forIntrospection)
352 return nGetAssemblies(forIntrospection);
355 // this is true when we've removed the handles etc so really can't do anything
356 [MethodImplAttribute(MethodImplOptions.InternalCall)]
357 internal extern bool IsUnloadingForcedFinalize();
359 // this is true when we've just started going through the finalizers and are forcing objects to finalize
360 // so must be aware that certain infrastructure may have gone away
361 [MethodImplAttribute(MethodImplOptions.InternalCall)]
362 public extern bool IsFinalizingForUnload();
364 [MethodImplAttribute(MethodImplOptions.InternalCall)]
365 internal static extern void PublishAnonymouslyHostedDynamicMethodsAssembly(RuntimeAssembly assemblyHandle);
367 public void SetData(string name, object data)
370 throw new ArgumentNullException(nameof(name));
372 lock (((ICollection)LocalStore).SyncRoot)
374 LocalStore[name] = data;
379 public Object GetData(string name)
382 throw new ArgumentNullException(nameof(name));
385 lock (((ICollection)LocalStore).SyncRoot)
387 LocalStore.TryGetValue(name, out data);
394 [Obsolete("AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the ManagedThreadId property on Thread. http://go.microsoft.com/fwlink/?linkid=14202", false)]
395 [DllImport(Interop.Libraries.Kernel32)]
396 public static extern int GetCurrentThreadId();
400 throw new NotSupportedException(SR.NotSupported_Constructor);
403 [MethodImplAttribute(MethodImplOptions.InternalCall)]
404 internal extern void nCreateContext();
406 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
407 private static extern void nSetupBindingPaths(String trustedPlatformAssemblies, String platformResourceRoots, String appPath, String appNiPaths, String appLocalWinMD);
409 internal void SetupBindingPaths(String trustedPlatformAssemblies, String platformResourceRoots, String appPath, String appNiPaths, String appLocalWinMD)
411 nSetupBindingPaths(trustedPlatformAssemblies, platformResourceRoots, appPath, appNiPaths, appLocalWinMD);
414 [MethodImplAttribute(MethodImplOptions.InternalCall)]
415 private extern String nGetFriendlyName();
417 // support reliability for certain event handlers, if the target
418 // methods also participate in this discipline. If caller passes
419 // an existing MulticastDelegate, then we could use a MDA to indicate
420 // that reliability is not guaranteed. But if it is a single cast
421 // scenario, we can make it work.
423 public event EventHandler ProcessExit
429 RuntimeHelpers.PrepareContractedDelegate(value);
431 _processExit += value;
437 _processExit -= value;
442 public event EventHandler DomainUnload
448 RuntimeHelpers.PrepareContractedDelegate(value);
450 _domainUnload += value;
456 _domainUnload -= value;
461 public event UnhandledExceptionEventHandler UnhandledException
467 RuntimeHelpers.PrepareContractedDelegate(value);
469 _unhandledException += value;
475 _unhandledException -= value;
479 // This is the event managed code can wireup against to be notified
480 // about first chance exceptions.
482 // To register/unregister the callback, the code must be SecurityCritical.
483 public event EventHandler<FirstChanceExceptionEventArgs> FirstChanceException
489 RuntimeHelpers.PrepareContractedDelegate(value);
491 _firstChanceException += value;
497 _firstChanceException -= value;
501 private void OnAssemblyLoadEvent(RuntimeAssembly LoadedAssembly)
503 AssemblyLoadEventHandler eventHandler = AssemblyLoad;
504 if (eventHandler != null)
506 AssemblyLoadEventArgs ea = new AssemblyLoadEventArgs(LoadedAssembly);
507 eventHandler(this, ea);
511 // This method is called by the VM.
512 private RuntimeAssembly OnResourceResolveEvent(RuntimeAssembly assembly, String resourceName)
514 ResolveEventHandler eventHandler = _ResourceResolve;
515 if (eventHandler == null)
518 Delegate[] ds = eventHandler.GetInvocationList();
520 for (int i = 0; i < len; i++)
522 Assembly asm = ((ResolveEventHandler)ds[i])(this, new ResolveEventArgs(resourceName, assembly));
523 RuntimeAssembly ret = GetRuntimeAssembly(asm);
531 // This method is called by the VM
532 private RuntimeAssembly OnTypeResolveEvent(RuntimeAssembly assembly, String typeName)
534 ResolveEventHandler eventHandler = _TypeResolve;
535 if (eventHandler == null)
538 Delegate[] ds = eventHandler.GetInvocationList();
540 for (int i = 0; i < len; i++)
542 Assembly asm = ((ResolveEventHandler)ds[i])(this, new ResolveEventArgs(typeName, assembly));
543 RuntimeAssembly ret = GetRuntimeAssembly(asm);
551 // This method is called by the VM.
552 private RuntimeAssembly OnAssemblyResolveEvent(RuntimeAssembly assembly, String assemblyFullName)
554 ResolveEventHandler eventHandler = _AssemblyResolve;
556 if (eventHandler == null)
561 Delegate[] ds = eventHandler.GetInvocationList();
563 for (int i = 0; i < len; i++)
565 Assembly asm = ((ResolveEventHandler)ds[i])(this, new ResolveEventArgs(assemblyFullName, assembly));
566 RuntimeAssembly ret = GetRuntimeAssembly(asm);
574 #if FEATURE_COMINTEROP
575 // Called by VM - code:CLRPrivTypeCacheWinRT::RaiseDesignerNamespaceResolveEvent
576 private string[] OnDesignerNamespaceResolveEvent(string namespaceName)
578 return System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMetadata.OnDesignerNamespaceResolveEvent(this, namespaceName);
580 #endif // FEATURE_COMINTEROP
582 internal AppDomainSetup FusionStore
586 Debug.Assert(_FusionStore != null,
587 "Fusion store has not been correctly setup in this domain");
592 internal static RuntimeAssembly GetRuntimeAssembly(Assembly asm)
597 RuntimeAssembly rtAssembly = asm as RuntimeAssembly;
598 if (rtAssembly != null)
601 AssemblyBuilder ab = asm as AssemblyBuilder;
603 return ab.InternalAssembly;
608 private Dictionary<String, Object> LocalStore
612 if (_LocalStore != null)
616 _LocalStore = new Dictionary<String, Object>();
622 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
623 private static extern void nSetNativeDllSearchDirectories(string paths);
625 private void SetupFusionStore(AppDomainSetup info, AppDomainSetup oldInfo)
627 Debug.Assert(info != null);
629 if (info.ApplicationBase == null)
631 info.SetupDefaults(RuntimeEnvironment.GetModuleFileName(), imageLocationAlreadyNormalized: true);
636 // This must be the last action taken
640 // Used to switch into other AppDomain and call SetupRemoteDomain.
641 // We cannot simply call through the proxy, because if there
642 // are any remoting sinks registered, they can add non-mscorlib
643 // objects to the message (causing an assembly load exception when
644 // we try to deserialize it on the other side)
645 private static object PrepareDataForSetup(String friendlyName,
646 AppDomainSetup setup,
647 string[] propertyNames,
648 string[] propertyValues)
650 AppDomainSetup newSetup = new AppDomainSetup(setup, false);
652 // Remove the special AppDomainCompatSwitch entries from the set of name value pairs
653 // And add them to the AppDomainSetup
655 // This is only supported on CoreCLR through ICLRRuntimeHost2.CreateAppDomainWithManager
656 // Desktop code should use System.AppDomain.CreateDomain() or
657 // System.AppDomainManager.CreateDomain() and add the flags to the AppDomainSetup
658 List<String> compatList = new List<String>();
660 if (propertyNames != null && propertyValues != null)
662 for (int i = 0; i < propertyNames.Length; i++)
664 if (String.Compare(propertyNames[i], "AppDomainCompatSwitch", StringComparison.OrdinalIgnoreCase) == 0)
666 compatList.Add(propertyValues[i]);
667 propertyNames[i] = null;
668 propertyValues[i] = null;
672 if (compatList.Count > 0)
674 newSetup.SetCompatibilitySwitches(compatList);
685 } // PrepareDataForSetup
687 private static Object Setup(Object arg)
689 Object[] args = (Object[])arg;
690 String friendlyName = (String)args[0];
691 AppDomainSetup setup = (AppDomainSetup)args[1];
692 string[] propertyNames = (string[])args[2]; // can contain null elements
693 string[] propertyValues = (string[])args[3]; // can contain null elements
695 AppDomain ad = AppDomain.CurrentDomain;
696 AppDomainSetup newSetup = new AppDomainSetup(setup, false);
698 if (propertyNames != null && propertyValues != null)
700 for (int i = 0; i < propertyNames.Length; i++)
702 // We want to set native dll probing directories before any P/Invokes have a
703 // chance to fire. The Path class, for one, has P/Invokes.
704 if (propertyNames[i] == "NATIVE_DLL_SEARCH_DIRECTORIES")
706 if (propertyValues[i] == null)
707 throw new ArgumentNullException("NATIVE_DLL_SEARCH_DIRECTORIES");
709 string paths = propertyValues[i];
710 if (paths.Length == 0)
713 nSetNativeDllSearchDirectories(paths);
717 for (int i = 0; i < propertyNames.Length; i++)
719 if (propertyNames[i] == "APPBASE") // make sure in sync with Fusion
721 if (propertyValues[i] == null)
722 throw new ArgumentNullException("APPBASE");
724 if (PathInternal.IsPartiallyQualified(propertyValues[i]))
725 throw new ArgumentException(SR.Argument_AbsolutePathRequired);
727 newSetup.ApplicationBase = NormalizePath(propertyValues[i], fullCheck: true);
729 else if (propertyNames[i] == "TRUSTED_PLATFORM_ASSEMBLIES" ||
730 propertyNames[i] == "PLATFORM_RESOURCE_ROOTS" ||
731 propertyNames[i] == "APP_PATHS" ||
732 propertyNames[i] == "APP_NI_PATHS")
734 string values = propertyValues[i];
736 throw new ArgumentNullException(propertyNames[i]);
738 ad.SetData(propertyNames[i], NormalizeAppPaths(values));
740 else if (propertyNames[i] != null)
742 ad.SetData(propertyNames[i], propertyValues[i]); // just propagate
747 ad.SetupFusionStore(newSetup, null); // makes FusionStore a ref to newSetup
749 // technically, we don't need this, newSetup refers to the same object as FusionStore
750 // but it's confusing since it isn't immediately obvious whether we have a ref or a copy
751 AppDomainSetup adSetup = ad.FusionStore;
753 // set up the friendly name
754 ad.nSetupFriendlyName(friendlyName);
756 ad.CreateAppDomainManager(); // could modify FusionStore's object
761 private static string NormalizeAppPaths(string values)
763 int estimatedLength = values.Length + 1; // +1 for extra separator temporarily added at end
764 StringBuilder sb = StringBuilderCache.Acquire(estimatedLength);
766 for (int pos = 0; pos < values.Length; pos++)
770 int nextPos = values.IndexOf(Path.PathSeparator, pos);
773 path = values.Substring(pos);
774 pos = values.Length - 1;
778 path = values.Substring(pos, nextPos - pos);
782 // Skip empty directories
783 if (path.Length == 0)
786 if (PathInternal.IsPartiallyQualified(path))
787 throw new ArgumentException(SR.Argument_AbsolutePathRequired);
789 string appPath = NormalizePath(path, fullCheck: true);
791 sb.Append(Path.PathSeparator);
794 // Strip the last separator
797 sb.Remove(sb.Length - 1, 1);
800 return StringBuilderCache.GetStringAndRelease(sb);
803 internal static string NormalizePath(string path, bool fullCheck)
805 return Path.GetFullPath(path);
808 // This routine is called from unmanaged code to
809 // set the default fusion context.
810 private void SetupDomain(bool allowRedirects, String path, String configFile, String[] propertyNames, String[] propertyValues)
812 // It is possible that we could have multiple threads initializing
813 // the default domain. We will just take the winner of these two.
814 // (eg. one thread doing a com call and another doing attach for IJW)
817 if (_FusionStore == null)
819 AppDomainSetup setup = new AppDomainSetup();
821 // always use internet permission set
822 SetupFusionStore(setup, null);
827 [MethodImplAttribute(MethodImplOptions.InternalCall)]
828 private extern void nSetupFriendlyName(string friendlyName);
830 public AppDomainSetup SetupInformation
834 return new AppDomainSetup(FusionStore, true);
838 [MethodImplAttribute(MethodImplOptions.InternalCall)]
839 internal extern String IsStringInterned(String str);
841 [MethodImplAttribute(MethodImplOptions.InternalCall)]
842 internal extern String GetOrInternString(String str);
844 public bool IsFullyTrusted
860 [MethodImplAttribute(MethodImplOptions.InternalCall)]
861 internal extern Int32 GetId();
865 /// Handle used to marshal an AppDomain to the VM (eg QCall). When marshaled via a QCall, the target
866 /// method in the VM will receive a QCall::AppDomainHandle parameter.
868 internal struct AppDomainHandle
870 private IntPtr m_appDomainHandle;
872 // Note: generall an AppDomainHandle should not be directly constructed, instead the
873 // code:System.AppDomain.GetNativeHandle method should be called to get the handle for a specific
875 internal AppDomainHandle(IntPtr domainHandle)
877 m_appDomainHandle = domainHandle;