3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 using System.Collections.Generic;
13 // The AppContext class consists of a collection of APIs that applications can use to reason about the context
14 // in which they are running.
16 // One of the APIs that are available to the application (and the framework) are the ones related to the
17 // configuration switches (SetSwitch, TryGetSwitch). Those APIs are used to set and retrieve the value of a switch.
19 // Inside the framework we use those APIs to provide backward compatibility for our in-place updates. In order for
20 // that to work in as many cases as possible we have intentionally coded the APIs to use very low level concepts.
21 // For instance, we are directly P/Invoking into the Registry calls and so that we don't initialize the infrastructure
22 // for accessing registry. Doing that would allow us to use AppContext switches inside the Registry code.
24 public static class AppContext
27 private enum SwitchValueState
31 HasLookedForOverride = 0x4,
32 UnknownValue = 0x8 // Has no default and could not find an override
35 // The Dictionary is intentionally left without specifying the comparer. StringComparer.Ordinal for instance will
36 // end up pulling in Globalization since all StringComparers are initialized in the static constructor (including the
37 // ones that use CultureInfo).
38 // Not specifying a comparer means that the dictionary will end up with a GenericEqualityComparer<string> comparer
39 private static readonly Dictionary<string, SwitchValueState> s_switchMap = new Dictionary<string, SwitchValueState>();
40 private static volatile bool s_defaultsInitialized = false;
42 public static string BaseDirectory
45 [System.Security.SecuritySafeCritical]
49 // The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type.
50 // Otherwise the caller will get invalid cast exception
51 return (string) AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY") ?? AppDomain.CurrentDomain.BaseDirectory;
55 public static string TargetFrameworkName
59 // Forward the value that is set on the current domain.
60 return AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
65 [System.Security.SecuritySafeCritical]
67 public static object GetData(string name)
69 return AppDomain.CurrentDomain.GetData(name);
74 private static void InitializeDefaultSwitchValues()
76 // To save a method call into this method, we are first checking
77 // the value of s_defaultsInitialized in the caller.
80 if (s_defaultsInitialized == false)
83 // populate the AppContext with the default set of values
84 AppContextDefaultValues.PopulateDefaultValues();
86 s_defaultsInitialized = true;
92 /// Try to get the value of the switch.
94 /// <param name="switchName">The name of the switch</param>
95 /// <param name="isEnabled">A variable where to place the value of the switch</param>
96 /// <returns>A return value of true represents that the switch was set and <paramref name="isEnabled"/> contains the value of the switch</returns>
97 public static bool TryGetSwitch(string switchName, out bool isEnabled)
99 if (switchName == null)
100 throw new ArgumentNullException("switchName");
101 if (switchName.Length == 0)
102 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName");
104 if (s_defaultsInitialized == false)
106 InitializeDefaultSwitchValues();
109 BCLDebug.Assert(s_defaultsInitialized == true, "AppContext defaults should have been initialized.");
112 // By default, the switch is not enabled.
115 SwitchValueState switchValue;
118 if (s_switchMap.TryGetValue(switchName, out switchValue))
120 // The value is in the dictionary.
121 // There are 3 cases here:
122 // 1. The value of the switch is 'unknown'. This means that the switch name is not known to the system (either via defaults or checking overrides).
123 // Example: This is the case when, during a servicing event, a switch is added to System.Xml which ships before mscorlib. The value of the switch
124 // Will be unknown to mscorlib.dll and we want to prevent checking the overrides every time we check this switch
125 // 2. The switch has a valid value AND we have read the overrides for it
126 // Example: TryGetSwitch is called for a switch set via SetSwitch
127 // 3. The switch has the default value and we need to check for overrides
128 // Example: TryGetSwitch is called for the first time for a switch that has a default value
130 // 1. The value is unknown
131 if (switchValue == SwitchValueState.UnknownValue)
137 // We get the value of isEnabled from the value that we stored in the dictionary
138 isEnabled = (switchValue & SwitchValueState.HasTrueValue) == SwitchValueState.HasTrueValue;
140 // 2. The switch has a valid value AND we have checked for overrides
141 if ((switchValue & SwitchValueState.HasLookedForOverride) == SwitchValueState.HasLookedForOverride)
146 // 3. The switch has a valid value, but we need to check for overrides.
147 // Regardless of whether or not the switch has an override, we need to update the value to reflect
148 // the fact that we checked for overrides.
150 if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue))
152 // we found an override!
153 isEnabled = overrideValue;
156 // Update the switch in the dictionary to mark it as 'checked for override'
157 s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
158 | SwitchValueState.HasLookedForOverride;
164 // The value is NOT in the dictionary
165 // In this case we need to see if we have an override defined for the value.
166 // There are 2 cases:
167 // 1. The value has an override specified. In this case we need to add the value to the dictionary
168 // and mark it as checked for overrides
169 // Example: In a servicing event, System.Xml introduces a switch and an override is specified.
170 // The value is not found in mscorlib (as System.Xml ships independent of mscorlib)
171 // 2. The value does not have an override specified
172 // In this case, we want to capture the fact that we looked for a value and found nothing by adding
173 // an entry in the dictionary with the 'sentinel' value of 'SwitchValueState.UnknownValue'.
174 // Example: This will prevent us from trying to find overrides for values that we don't have in the dictionary
176 // 1. The value has an override specified.
178 if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue))
180 isEnabled = overrideValue;
182 // Update the switch in the dictionary to mark it as 'checked for override'
183 s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
184 | SwitchValueState.HasLookedForOverride;
189 // 2. The value does not have an override.
190 s_switchMap[switchName] = SwitchValueState.UnknownValue;
193 return false; // we did not find a value for the switch
197 /// Assign a switch a value
199 /// <param name="switchName">The name of the switch</param>
200 /// <param name="isEnabled">The value to assign</param>
201 public static void SetSwitch(string switchName, bool isEnabled)
203 if (switchName == null)
204 throw new ArgumentNullException("switchName");
205 if (switchName.Length == 0)
206 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName");
208 if (s_defaultsInitialized == false)
210 InitializeDefaultSwitchValues();
213 BCLDebug.Assert(s_defaultsInitialized == true, "AppContext defaults should have been initialized.");
216 SwitchValueState switchValue = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
217 | SwitchValueState.HasLookedForOverride;
220 // Store the new value and the fact that we checked in the dictionary
221 s_switchMap[switchName] = switchValue;
226 /// This method is going to be called from the AppContextDefaultValues class when setting up the
227 /// default values for the switches.
229 internal static void DefineSwitchDefault(string switchName, bool isEnabled)
232 BCLDebug.Assert(System.Threading.Monitor.IsEntered(s_switchMap), "Expected the method to be called within a lock");
235 s_switchMap[switchName] = isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue;
239 /// This method is going to be called from the AppContextDefaultValues class when setting up the
240 /// override default values for the switches.
242 internal static void DefineSwitchOverride(string switchName, bool isEnabled)
245 BCLDebug.Assert(System.Threading.Monitor.IsEntered(s_switchMap), "Expected the method to be called within a lock");
248 s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
249 | SwitchValueState.HasLookedForOverride;