// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.ExceptionServices; using System.Runtime.Loader; using System.Runtime.Versioning; using System.Threading; namespace System { public static partial class AppContext { private static Dictionary? s_dataStore; private static Dictionary? 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 (string?)GetData("APP_CONTEXT_BASE_DIRECTORY") ?? (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()?.FrameworkName; public static object? GetData(string name) { if (name == null) throw new ArgumentNullException(nameof(name)); if (s_dataStore == null) return null; object? data; lock (s_dataStore) { s_dataStore.TryGetValue(name, out data); } return data; } public static void SetData(string name, object? data) { if (name == null) throw new ArgumentNullException(nameof(name)); if (s_dataStore == null) { Interlocked.CompareExchange(ref s_dataStore, new Dictionary(), null); } lock (s_dataStore) { s_dataStore[name] = data; } } #pragma warning disable CS0067 // events raised by the VM public static event UnhandledExceptionEventHandler? UnhandledException; public static event System.EventHandler? FirstChanceException; #pragma warning restore CS0067 public static event System.EventHandler? ProcessExit; internal static void OnProcessExit() { AssemblyLoadContext.OnProcessExit(); ProcessExit?.Invoke(AppDomain.CurrentDomain, EventArgs.Empty); } /// /// Try to get the value of the switch. /// /// The name of the switch /// A variable where to place the value of the switch /// A return value of true represents that the switch was set and contains the value of the switch public static bool TryGetSwitch(string switchName, out bool isEnabled) { if (switchName == null) throw new ArgumentNullException(nameof(switchName)); if (switchName.Length == 0) throw new ArgumentException(SR.Argument_EmptyName, nameof(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; } /// /// Assign a switch a value /// /// The name of the switch /// The value to assign public static void SetSwitch(string switchName, bool isEnabled) { if (switchName == null) throw new ArgumentNullException(nameof(switchName)); if (switchName.Length == 0) throw new ArgumentException(SR.Argument_EmptyName, nameof(switchName)); if (s_switches == null) { // Compatibility switches are rarely used. Initialize the Dictionary lazily Interlocked.CompareExchange(ref s_switches, new Dictionary(), null); } lock (s_switches) { s_switches[switchName] = isEnabled; } } #if !CORERT internal static unsafe void Setup(char** pNames, char** pValues, int count) { Debug.Assert(s_dataStore == null, "s_dataStore is not expected to be inited before Setup is called"); s_dataStore = new Dictionary(count); for (int i = 0; i < count; i++) { s_dataStore.Add(new string(pNames[i]), new string(pValues[i])); } } private static string GetBaseDirectoryCore() { // Fallback path for hosts that do not set APP_CONTEXT_BASE_DIRECTORY explicitly string? directory = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); if (directory != null && !Path.EndsInDirectorySeparator(directory)) directory += PathInternal.DirectorySeparatorCharAsString; return directory ?? string.Empty; } #endif } }