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 //*****************************************************************************
9 // Unified method of accessing configuration values from environment variables,
10 // registry and config file. See file:../inc/CLRConfigValues.h for details on how to add config values.
12 //*****************************************************************************
15 #include "clrconfig.h"
22 // Initialize the EEConfig::GetConfiguration function pointer to NULL. If EEConfig isn't init'ed, this will
23 // stay NULL and CLRConfig will ignore config files.
25 CLRConfig::GetConfigValueFunction CLRConfig::s_GetConfigValueCallback = NULL;
28 // Creating structs using the macro table in CLRConfigValues.h
31 // These macros intialize ConfigDWORDInfo structs.
32 #define RETAIL_CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \
33 const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default};
34 #define RETAIL_CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \
35 const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions};
37 // These macros intialize ConfigStringInfo structs.
38 #define RETAIL_CONFIG_STRING_INFO(symbol, name, description) \
39 const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default};
40 #define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \
41 const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions};
43 // TEMPORARY macros that intialize strings for config value accesses that haven't been moved over to
44 // CLRConfig yet. Once all accesses have been moved, these macros (and corresponding instantiations in
45 // file:../utilcode/CLRConfig.h) should be removed.
46 #define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \
47 const LPCWSTR CLRConfig::symbol = name;
48 #define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \
49 const LPCWSTR CLRConfig::symbol = name;
51 // Debug versions of the macros
54 #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \
55 const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default};
56 #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \
57 const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions};
58 #define CONFIG_STRING_INFO(symbol, name, description) \
59 const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default};
60 #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \
61 const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions};
62 #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \
63 const LPCWSTR CLRConfig::symbol = name;
64 #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \
65 const LPCWSTR CLRConfig::symbol = name;
67 #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description)
68 #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions)
69 #define CONFIG_STRING_INFO(symbol, name, description)
70 #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions)
71 #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description)
72 #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description)
75 // Now that we have defined what what the macros in file:../inc/CLRConfigValues.h mean, include it to generate the code.
76 #include "clrconfigvalues.h"
78 #undef RETAIL_CONFIG_DWORD_INFO
79 #undef RETAIL_CONFIG_STRING_INFO
80 #undef RETAIL_CONFIG_DWORD_INFO_EX
81 #undef RETAIL_CONFIG_STRING_INFO_EX
82 #undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS
83 #undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS
84 #undef CONFIG_DWORD_INFO
85 #undef CONFIG_STRING_INFO
86 #undef CONFIG_DWORD_INFO_EX
87 #undef CONFIG_STRING_INFO_EX
88 #undef CONFIG_DWORD_INFO_DIRECT_ACCESS
89 #undef CONFIG_STRING_INFO_DIRECT_ACCESS
94 // Return if a quirk is a enabled.
95 // This will also return enabled as true when the quirk has a value set.
96 BOOL CLRConfig::IsConfigEnabled(const ConfigDWORDInfo & info)
106 DWORD result = info.defaultValue;
109 // Set up REGUTIL options.
111 REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
112 BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_);
115 // If we aren't favoring config files, we check REGUTIL here.
117 if(CheckLookupOption(info, FavorConfigFile) == FALSE)
119 REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPlus);
122 LPWSTR result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
123 if(result != NULL && result[0] != 0)
130 // Check config files through EEConfig.
132 if(CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
133 s_GetConfigValueCallback != NULL)// Check that GetConfigValueCallback function has been registered.
137 // EEConfig lookup options.
138 BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
139 BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
141 if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pvalue, systemOnly, applicationFirst)) && pvalue != NULL)
145 result = wcstoul(pvalue, &end, 0);
147 // errno is ERANGE if the number is out of range, and end is set to pvalue if
148 // no valid conversion exists.
149 if (errno == ERANGE || end == pvalue)
154 result = info.defaultValue;
163 // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
165 if(CheckLookupOption(info, FavorConfigFile) == TRUE)
167 REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPlus);
170 LPWSTR result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
171 if(result != NULL && result[0] != 0)
177 if(info.defaultValue>0)
184 // Look up a DWORD config value.
187 // * info - see file:../inc/CLRConfig.h for details.
189 // * useDefaultIfNotSet - if true, fall back to the default value if the value is not set.
191 // * acceptExplicitDefaultFromRegutil - if false, only accept a value returned by REGUTIL if it is
192 // different from the default value. This parameter is useful as a way to preserve existing
195 // * result - the result.
198 // * true for success, false otherwise.
201 DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplicitDefaultFromRegutil, /* [Out] */ bool *isDefault)
211 _ASSERTE (isDefault != nullptr);
215 // Set up REGUTIL options.
217 REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
218 BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_);
221 // If we aren't favoring config files, we check REGUTIL here.
223 if (CheckLookupOption(info, FavorConfigFile) == FALSE)
226 HRESULT hr = REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &resultMaybe, level, prependCOMPlus);
228 if (!acceptExplicitDefaultFromRegutil)
230 // Ignore the default value even if it's set explicitly.
231 if (resultMaybe != info.defaultValue)
239 // If we are willing to accept the default value when it's set explicitly,
240 // checking the HRESULT here is sufficient. E_FAIL is returned when the
251 // Check config files through EEConfig.
253 if (CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
254 s_GetConfigValueCallback != NULL)// Check that GetConfigValueCallback function has been registered.
258 // EEConfig lookup options.
259 BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
260 BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
262 if (SUCCEEDED(s_GetConfigValueCallback(info.name, &pvalue, systemOnly, applicationFirst)) && pvalue != NULL)
266 DWORD resultMaybe = wcstoul(pvalue, &end, 0);
268 // errno is ERANGE if the number is out of range, and end is set to pvalue if
269 // no valid conversion exists.
270 if (errno != ERANGE && end != pvalue)
277 // If an invalid value is defined we treat it as the default value.
278 // i.e. we don't look further.
280 return info.defaultValue;
286 // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
288 if (CheckLookupOption(info, FavorConfigFile) == TRUE)
291 HRESULT hr = REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &resultMaybe, level, prependCOMPlus);
293 if (!acceptExplicitDefaultFromRegutil)
295 // Ignore the default value even if it's set explicitly.
296 if (resultMaybe != info.defaultValue)
304 // If we are willing to accept the default value when it's set explicitly,
305 // checking the HRESULT here is sufficient. E_FAIL is returned when the
316 return info.defaultValue;
320 // Look up a DWORD config value.
323 // * info - see file:../inc/CLRConfig.h for details
326 DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info)
328 // We pass false for 'acceptExplicitDefaultFromRegutil' to maintain the existing behavior of this function.
329 // Callers who don't need that behavior should switch to the other version of this function and pass true.
331 return GetConfigValue(info, false /* acceptExplicitDefaultFromRegutil */, &unused);
335 // Look up a String config value.
338 // * info - see file:../inc/CLRConfig.h for details
341 // * Pointer to the string value, if found. You own the string that's returned. Returns NULL if the value
345 LPWSTR CLRConfig::GetConfigValue(const ConfigStringInfo & info)
355 LPWSTR result = NULL;
357 // TODO: We swallow OOM exception here. Is this OK?
360 // If this fails, result will stay NULL.
361 GetConfigValue(info, &result);
367 // Look up a string config value, passing it out through a pointer reference.
370 // * Reports out of memory errors (HRESULT E_OUTOFMEMORY).
373 // * info - see file:../inc/CLRConfig.h for details
374 // * outVal - Set to the result string. You own the string that's returned. Set to NULL if the value is
378 HRESULT CLRConfig::GetConfigValue(const ConfigStringInfo & info, __deref_out_z LPWSTR * outVal)
383 INJECT_FAULT (CONTRACT_RETURN E_OUTOFMEMORY);
384 POSTCONDITION(CheckPointer(outVal, NULL_OK)); // TODO: Should this check be *outVal instead of outVal?
387 LPWSTR result = NULL;
391 // Set up REGUTIL options.
393 REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
394 BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_);
397 // If we aren't favoring config files, we check REGUTIL here.
399 if(result == NULL && CheckLookupOption(info, FavorConfigFile) == FALSE)
401 result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
405 // Check config files through EEConfig.
407 if(result == NULL && // Check that we don't have a value from REGUTIL
408 CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
409 s_GetConfigValueCallback != NULL) // Check that GetConfigValueCallback function has been registered.
413 // EEConfig lookup options.
414 BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
415 BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
417 if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pResult, systemOnly, applicationFirst)) && pResult != NULL)
419 size_t len = wcslen(pResult) + 1;
420 result = new (nothrow) WCHAR[len];
423 RETURN E_OUTOFMEMORY;
425 wcscpy_s(result, len, pResult);
430 // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
433 CheckLookupOption(info, FavorConfigFile) == TRUE)
435 result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
438 if ((result != NULL) && CheckLookupOption(info, TrimWhiteSpaceFromStringValue))
440 // If this fails, result remains untouched, so we'll just return the untrimmed
442 LPWSTR wszTrimmedResult = NULL;
443 if (SUCCEEDED(TrimWhiteSpace(result, &wszTrimmedResult)) &&
444 (wszTrimmedResult != NULL))
446 // wszTrimmedResult should be the result we return. Delete the untrimmed
449 result = wszTrimmedResult;
458 // Check whether an option is specified (e.g. explicitly listed) in any location
461 // * name - the name field of the desired ConfigDWORDInfo/ConfigStringInfo
464 BOOL CLRConfig::IsConfigOptionSpecified(LPCWSTR name)
473 // Check config files
475 LPCWSTR result = NULL;
477 if (s_GetConfigValueCallback != NULL &&
478 SUCCEEDED(s_GetConfigValueCallback(name, &result, FALSE, FALSE)) &&
485 // Check REGUTIL, both with and without the COMPlus_ prefix
487 LPWSTR result = NULL;
489 result = REGUTIL::GetConfigString_DontUse_(name, TRUE);
492 FreeConfigString(result);
496 result = REGUTIL::GetConfigString_DontUse_(name, FALSE);
499 FreeConfigString(result);
508 //---------------------------------------------------------------------------------------
510 // Given an input string, returns a newly-allocated string equal to the input but with
511 // leading and trailing whitespace trimmed off. If input is already trimmed, or if
512 // trimming would result in an empty string, this function sets the output string to NULL
514 // Caller must free *pwszTrimmed if non-NULL
517 // * wszOrig - String to trim
518 // * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or
522 // HRESULT indicating success or failure.
524 HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed)
533 _ASSERTE(wszOrig != NULL);
534 _ASSERTE(pwszTrimmed != NULL);
536 // In case we return early, set [out] to NULL by default
539 // Get pointers into internal string that show where to do the trimming.
540 size_t cchOrig = wcslen(wszOrig);
541 if (!FitsIn<DWORD>(cchOrig))
542 return COR_E_OVERFLOW;
543 DWORD cchAfterTrim = (DWORD) cchOrig;
544 LPCWSTR wszAfterTrim = wszOrig;
545 ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim);
547 // Is input string already trimmed? If so, save an allocation and just return.
548 if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim))
550 // Yup, just return success
554 if (cchAfterTrim == 0)
556 // After trimming, there's nothing left, so just return NULL
560 // Create a new buffer to hold a copy of the trimmed string. Caller will be
561 // responsible for this buffer if we return it.
562 NewArrayHolder<WCHAR> wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]);
563 if (wszTrimmedCopy == NULL)
565 return E_OUTOFMEMORY;
568 errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim);
574 // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for
576 wszTrimmedCopy.SuppressRelease();
577 *pwszTrimmed = wszTrimmedCopy;
583 // Deallocation function for code:CLRConfig::FreeConfigString
585 void CLRConfig::FreeConfigString(__in_z LPWSTR str)
587 LIMITED_METHOD_CONTRACT;
593 // Register EEConfig's GetConfigValueCallback function so CLRConfig can look in config files.
596 void CLRConfig::RegisterGetConfigValueCallback(GetConfigValueFunction func)
598 LIMITED_METHOD_CONTRACT;
599 s_GetConfigValueCallback = func;
603 // Helper method to translate LookupOptions to REGUTIL::CORConfigLevel.
606 REGUTIL::CORConfigLevel CLRConfig::GetConfigLevel(LookupOptions options)
608 LIMITED_METHOD_CONTRACT;
610 REGUTIL::CORConfigLevel level = (REGUTIL::CORConfigLevel) 0;
612 if(CheckLookupOption(options, IgnoreEnv) == FALSE)
613 level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_ENV);
615 if(CheckLookupOption(options, IgnoreHKCU) == FALSE)
616 level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_USER);
618 if(CheckLookupOption(options, IgnoreHKLM) == FALSE)
619 level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_MACHINE);