| File: src\runtime\src\libraries\System.Private.CoreLib\src\System\AppContext.cs | Web Access |
| Project: System.Private.CoreLib.csproj (System.Private.CoreLib) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Tracing; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Runtime.Loader; using System.Runtime.Versioning; using System.Threading; namespace System { public static partial class AppContext { private static Dictionary<string, object?>? s_dataStore #if NATIVEAOT = InitializeDataStore() #endif ; private static Dictionary<string, bool>? s_switches; private static string? s_defaultBaseDirectory; public static string BaseDirectory => // The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type. // Otherwise the caller will get invalid cast exception GetData("APP_CONTEXT_BASE_DIRECTORY") as string ?? (s_defaultBaseDirectory ??= GetBaseDirectoryCore()); public static string? TargetFrameworkName => // The Target framework is not the framework that the process is actually running on. // It is the value read from the TargetFrameworkAttribute on the .exe that started the process. Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName; public static object? GetData(string name) { ArgumentNullException.ThrowIfNull(name); if (s_dataStore == null) return null; lock (s_dataStore) { if (s_dataStore.TryGetValue(name, out object? data)) return data; } #if !MONO && !NATIVEAOT if (IsKnownHostProperty(name)) { string? value = null; if (TryGetHostPropertyValue(name, new StringHandleOnStack(ref value))) { lock (s_dataStore) { if (s_dataStore.TryGetValue(name, out object? existing)) { Debug.Assert(existing is string existingValue && existingValue == value); return existing; } s_dataStore[name] = value; return value; } } } #endif return null; } /// <summary> /// Sets the value of the named data element assigned to the current application domain. /// </summary> /// <param name="name">The name of the data element</param> /// <param name="data">The value of <paramref name="name"/></param> /// <exception cref="ArgumentNullException">If <paramref name="name"/> is <see langword="null"/></exception> public static void SetData(string name, object? data) { ArgumentNullException.ThrowIfNull(name); if (s_dataStore == null) { Interlocked.CompareExchange(ref s_dataStore, new Dictionary<string, object?>(), null); } lock (s_dataStore) { s_dataStore[name] = data; } } #pragma warning disable CS0067 // events raised by the VM #if MONO [field: DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(UnhandledExceptionEventArgs))] #endif internal static event UnhandledExceptionEventHandler? UnhandledException; #if MONO [field: DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(FirstChanceExceptionEventArgs))] #endif internal static event EventHandler<FirstChanceExceptionEventArgs>? FirstChanceException; #pragma warning restore CS0067 #if NATIVEAOT [System.Runtime.RuntimeExport("OnUnhandledException")] #endif internal static void OnUnhandledException(object e) { #if NATIVEAOT RuntimeExceptionHelpers.SerializeCrashInfo(System.Runtime.RhFailFastReason.UnhandledException, (e as Exception)?.Message, e as Exception); #endif if (UnhandledException is UnhandledExceptionEventHandler handlers) { UnhandledExceptionEventArgs args = new(e, isTerminating: true); foreach (UnhandledExceptionEventHandler handler in Delegate.EnumerateInvocationList(handlers)) { try { handler(/* AppDomain */ null!, args); } catch { } } } } [ThreadStatic] private static bool t_deliveringFirstChanceNotification; private static void OnFirstChanceException(Exception e, object? sender) { if (FirstChanceException is EventHandler<FirstChanceExceptionEventArgs> handlers) { // Guard against reentrancy. Allocating the event args below or running a // handler may itself throw (e.g. OutOfMemoryException in a low-memory // situation). That exception would trigger another first-chance // notification on this same thread, allocate again, throw again, and // recurse until the stack overflows. Skip nested notifications to break // the recursion. if (t_deliveringFirstChanceNotification) { return; } t_deliveringFirstChanceNotification = true; try { FirstChanceExceptionEventArgs args = new(e); foreach (EventHandler<FirstChanceExceptionEventArgs> handler in Delegate.EnumerateInvocationList(handlers)) { try { handler(sender, args); } catch { } } } finally { t_deliveringFirstChanceNotification = false; } } } internal static void OnProcessExit() { AssemblyLoadContext.OnProcessExit(); if (EventSource.IsSupported) { EventListener.DisposeOnShutdown(); } AppDomain.OnProcessExit(); } /// <summary> /// Try to get the value of the switch. /// </summary> /// <param name="switchName">The name of the switch</param> /// <param name="isEnabled">A variable where to place the value of the switch</param> /// <returns>A return value of true represents that the switch was set and <paramref name="isEnabled"/> contains the value of the switch</returns> public static bool TryGetSwitch(string switchName, out bool isEnabled) { ArgumentException.ThrowIfNullOrEmpty(switchName); if (s_switches != null) { lock (s_switches) { if (s_switches.TryGetValue(switchName, out isEnabled)) return true; } } if (GetData(switchName) is string value && bool.TryParse(value, out isEnabled)) { return true; } isEnabled = false; return false; } /// <summary> /// Assign a switch a value /// </summary> /// <param name="switchName">The name of the switch</param> /// <param name="isEnabled">The value to assign</param> public static void SetSwitch(string switchName, bool isEnabled) { ArgumentException.ThrowIfNullOrEmpty(switchName); if (s_switches == null) { // Compatibility switches are rarely used. Initialize the Dictionary lazily Interlocked.CompareExchange(ref s_switches, new Dictionary<string, bool>(), null); } lock (s_switches) { s_switches[switchName] = isEnabled; } } #if MONO internal static unsafe void Setup(char** pNames, uint* pNameLengths, char** pValues, uint* pValueLengths, int count) { Debug.Assert(s_dataStore == null, "s_dataStore is not expected to be inited before Setup is called"); s_dataStore = new Dictionary<string, object?>(count); for (int i = 0; i < count; i++) { s_dataStore.Add(new string(pNames[i], 0, (int)pNameLengths[i]), new string(pValues[i], 0, (int)pValueLengths[i])); } } #elif !NATIVEAOT [UnmanagedCallersOnly] internal static unsafe void Setup(char** pNames, char** pValues, int count, Exception* pException) { try { Debug.Assert(s_dataStore == null, "s_dataStore is not expected to be inited before Setup is called"); s_dataStore = new Dictionary<string, object?>(count); for (int i = 0; i < count; i++) { string name = new string(pNames[i]); // Avoid retaining a managed string copy of known host properties. // They will be retrieved if explicitly requested. if (IsKnownHostProperty(name)) continue; s_dataStore.Add(name, new string(pValues[i])); } } catch (Exception ex) { *pException = ex; } } #endif } }