AppContext.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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.
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Reflection;
  8. using System.Runtime.ExceptionServices;
  9. using System.Runtime.Loader;
  10. using System.Runtime.Versioning;
  11. using System.Threading;
  12. namespace System
  13. {
  14. public static partial class AppContext
  15. {
  16. private static Dictionary<string, object?>? s_dataStore;
  17. private static Dictionary<string, bool>? s_switches;
  18. private static string? s_defaultBaseDirectory;
  19. public static string BaseDirectory =>
  20. // The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type.
  21. // Otherwise the caller will get invalid cast exception
  22. (string?)GetData("APP_CONTEXT_BASE_DIRECTORY") ??
  23. (s_defaultBaseDirectory ??= GetBaseDirectoryCore());
  24. public static string? TargetFrameworkName =>
  25. // The Target framework is not the framework that the process is actually running on.
  26. // It is the value read from the TargetFrameworkAttribute on the .exe that started the process.
  27. Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
  28. public static object? GetData(string name)
  29. {
  30. if (name == null)
  31. throw new ArgumentNullException(nameof(name));
  32. if (s_dataStore == null)
  33. return null;
  34. object? data;
  35. lock (s_dataStore)
  36. {
  37. s_dataStore.TryGetValue(name, out data);
  38. }
  39. return data;
  40. }
  41. public static void SetData(string name, object? data)
  42. {
  43. if (name == null)
  44. throw new ArgumentNullException(nameof(name));
  45. if (s_dataStore == null)
  46. {
  47. Interlocked.CompareExchange(ref s_dataStore, new Dictionary<string, object?>(), null);
  48. }
  49. lock (s_dataStore)
  50. {
  51. s_dataStore[name] = data;
  52. }
  53. }
  54. #pragma warning disable CS0067 // events raised by the VM
  55. public static event UnhandledExceptionEventHandler? UnhandledException;
  56. public static event System.EventHandler<FirstChanceExceptionEventArgs>? FirstChanceException;
  57. #pragma warning restore CS0067
  58. public static event System.EventHandler? ProcessExit;
  59. internal static void OnProcessExit()
  60. {
  61. AssemblyLoadContext.OnProcessExit();
  62. ProcessExit?.Invoke(AppDomain.CurrentDomain, EventArgs.Empty);
  63. }
  64. /// <summary>
  65. /// Try to get the value of the switch.
  66. /// </summary>
  67. /// <param name="switchName">The name of the switch</param>
  68. /// <param name="isEnabled">A variable where to place the value of the switch</param>
  69. /// <returns>A return value of true represents that the switch was set and <paramref name="isEnabled"/> contains the value of the switch</returns>
  70. public static bool TryGetSwitch(string switchName, out bool isEnabled)
  71. {
  72. if (switchName == null)
  73. throw new ArgumentNullException(nameof(switchName));
  74. if (switchName.Length == 0)
  75. throw new ArgumentException(SR.Argument_EmptyName, nameof(switchName));
  76. if (s_switches != null)
  77. {
  78. lock (s_switches)
  79. {
  80. if (s_switches.TryGetValue(switchName, out isEnabled))
  81. return true;
  82. }
  83. }
  84. if (GetData(switchName) is string value && bool.TryParse(value, out isEnabled))
  85. {
  86. return true;
  87. }
  88. isEnabled = false;
  89. return false;
  90. }
  91. /// <summary>
  92. /// Assign a switch a value
  93. /// </summary>
  94. /// <param name="switchName">The name of the switch</param>
  95. /// <param name="isEnabled">The value to assign</param>
  96. public static void SetSwitch(string switchName, bool isEnabled)
  97. {
  98. if (switchName == null)
  99. throw new ArgumentNullException(nameof(switchName));
  100. if (switchName.Length == 0)
  101. throw new ArgumentException(SR.Argument_EmptyName, nameof(switchName));
  102. if (s_switches == null)
  103. {
  104. // Compatibility switches are rarely used. Initialize the Dictionary lazily
  105. Interlocked.CompareExchange(ref s_switches, new Dictionary<string, bool>(), null);
  106. }
  107. lock (s_switches)
  108. {
  109. s_switches[switchName] = isEnabled;
  110. }
  111. }
  112. #if !CORERT
  113. internal static unsafe void Setup(char** pNames, char** pValues, int count)
  114. {
  115. Debug.Assert(s_dataStore == null, "s_dataStore is not expected to be inited before Setup is called");
  116. s_dataStore = new Dictionary<string, object?>(count);
  117. for (int i = 0; i < count; i++)
  118. {
  119. s_dataStore.Add(new string(pNames[i]), new string(pValues[i]));
  120. }
  121. }
  122. private static string GetBaseDirectoryCore()
  123. {
  124. // Fallback path for hosts that do not set APP_CONTEXT_BASE_DIRECTORY explicitly
  125. string? directory = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
  126. if (directory != null && !Path.EndsInDirectorySeparator(directory))
  127. directory += PathInternal.DirectorySeparatorCharAsString;
  128. return directory ?? string.Empty;
  129. }
  130. #endif
  131. }
  132. }