ResourceManager.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  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.IO;
  5. using System.Globalization;
  6. using System.Reflection;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. namespace System.Resources
  10. {
  11. // Resource Manager exposes an assembly's resources to an application for
  12. // the correct CultureInfo. An example would be localizing text for a
  13. // user-visible message. Create a set of resource files listing a name
  14. // for a message and its value, compile them using ResGen, put them in
  15. // an appropriate place (your assembly manifest(?)), then create a Resource
  16. // Manager and query for the name of the message you want. The Resource
  17. // Manager will use CultureInfo.GetCurrentUICulture() to look
  18. // up a resource for your user's locale settings.
  19. //
  20. // Users should ideally create a resource file for every culture, or
  21. // at least a meaningful subset. The filenames will follow the naming
  22. // scheme:
  23. //
  24. // basename.culture name.resources
  25. //
  26. // The base name can be the name of your application, or depending on
  27. // the granularity desired, possibly the name of each class. The culture
  28. // name is determined from CultureInfo's Name property.
  29. // An example file name may be MyApp.en-US.resources for
  30. // MyApp's US English resources.
  31. //
  32. // -----------------
  33. // Refactoring Notes
  34. // -----------------
  35. // In Feb 08, began first step of refactoring ResourceManager to improve
  36. // maintainability (sd changelist 3012100). This resulted in breaking
  37. // apart the InternalGetResourceSet "big loop" so that the file-based
  38. // and manifest-based lookup was located in separate methods.
  39. // In Apr 08, continued refactoring so that file-based and manifest-based
  40. // concerns are encapsulated by separate classes. At construction, the
  41. // ResourceManager creates one of these classes based on whether the
  42. // RM will need to use file-based or manifest-based resources, and
  43. // afterwards refers to this through the interface IResourceGroveler.
  44. //
  45. // Serialization Compat: Ideally, we could have refactored further but
  46. // this would have broken serialization compat. For example, the
  47. // ResourceManager member UseManifest and UseSatelliteAssem are no
  48. // longer relevant on ResourceManager. Similarly, other members could
  49. // ideally be moved to the file-based or manifest-based classes
  50. // because they are only relevant for those types of lookup.
  51. //
  52. // Solution now / in the future:
  53. // For now, we simply use a mediator class so that we can keep these
  54. // members on ResourceManager but allow the file-based and manifest-
  55. // based classes to access/set these members in a uniform way. See
  56. // ResourceManagerMediator.
  57. // We encapsulate fallback logic in a fallback iterator class, so that
  58. // this logic isn't duplicated in several methods.
  59. //
  60. // In the future, we can also look into further factoring and better
  61. // design of IResourceGroveler interface to accommodate unused parameters
  62. // that don't make sense for either file-based or manifest-based lookup paths.
  63. //
  64. // Benefits of this refactoring:
  65. // - Makes it possible to understand what the ResourceManager does,
  66. // which is key for maintainability.
  67. // - Makes the ResourceManager more extensible by identifying and
  68. // encapsulating what varies
  69. // - Unearthed a bug that's been lurking a while in file-based
  70. // lookup paths for InternalGetResourceSet if createIfNotExists is
  71. // false.
  72. // - Reuses logic, e.g. by breaking apart the culture fallback into
  73. // the fallback iterator class, we don't have to repeat the
  74. // sometimes confusing fallback logic across multiple methods
  75. // - Fxcop violations reduced to 1/5th of original count. Most
  76. // importantly, code complexity violations disappeared.
  77. // - Finally, it got rid of dead code paths. Because the big loop was
  78. // so confusing, it masked unused chunks of code. Also, dividing
  79. // between file-based and manifest-based allowed functionaliy
  80. // unused in silverlight to fall out.
  81. //
  82. // Note: this type is integral to the construction of exception objects,
  83. // and sometimes this has to be done in low memory situtations (OOM) or
  84. // to create TypeInitializationExceptions due to failure of a static class
  85. // constructor. This type needs to be extremely careful and assume that
  86. // any type it references may have previously failed to construct, so statics
  87. // belonging to that type may not be initialized. FrameworkEventSource.Log
  88. // is one such example.
  89. //
  90. public partial class ResourceManager
  91. {
  92. internal class CultureNameResourceSetPair
  93. {
  94. public string lastCultureName;
  95. public ResourceSet lastResourceSet;
  96. }
  97. protected string BaseNameField;
  98. protected Assembly MainAssembly; // Need the assembly manifest sometimes.
  99. private Dictionary<string, ResourceSet> _resourceSets;
  100. private string _moduleDir; // For assembly-ignorant directory location
  101. private Type _locationInfo; // For Assembly or type-based directory layout
  102. private Type _userResourceSet; // Which ResourceSet instance to create
  103. private CultureInfo _neutralResourcesCulture; // For perf optimizations.
  104. private CultureNameResourceSetPair _lastUsedResourceCache;
  105. private bool _ignoreCase; // Whether case matters in GetString & GetObject
  106. private bool _useManifest; // Use Assembly manifest, or grovel disk.
  107. // Whether to fall back to the main assembly or a particular
  108. // satellite for the neutral resources.
  109. private UltimateResourceFallbackLocation _fallbackLoc;
  110. // Version number of satellite assemblies to look for. May be null.
  111. private Version _satelliteContractVersion;
  112. private bool _lookedForSatelliteContractVersion;
  113. private IResourceGroveler _resourceGroveler;
  114. public static readonly int MagicNumber = unchecked((int)0xBEEFCACE); // If only hex had a K...
  115. // Version number so ResMgr can get the ideal set of classes for you.
  116. // ResMgr header is:
  117. // 1) MagicNumber (little endian Int32)
  118. // 2) HeaderVersionNumber (little endian Int32)
  119. // 3) Num Bytes to skip past ResMgr header (little endian Int32)
  120. // 4) IResourceReader type name for this file (bytelength-prefixed UTF-8 String)
  121. // 5) ResourceSet type name for this file (bytelength-prefixed UTF8 String)
  122. public static readonly int HeaderVersionNumber = 1;
  123. //
  124. //It would be better if we could use _neutralCulture instead of calling
  125. //CultureInfo.InvariantCulture everywhere, but we run into problems with the .cctor. CultureInfo
  126. //initializes assembly, which initializes ResourceManager, which tries to get a CultureInfo which isn't
  127. //there yet because CultureInfo's class initializer hasn't finished. If we move SystemResMgr off of
  128. //Assembly (or at least make it an internal property) we should be able to circumvent this problem.
  129. //
  130. // private static CultureInfo _neutralCulture = null;
  131. // This is our min required ResourceSet type.
  132. private static readonly Type s_minResourceSet = typeof(ResourceSet);
  133. // These Strings are used to avoid using Reflection in CreateResourceSet.
  134. internal const string ResReaderTypeName = "System.Resources.ResourceReader";
  135. internal const string ResSetTypeName = "System.Resources.RuntimeResourceSet";
  136. internal const string ResFileExtension = ".resources";
  137. internal const int ResFileExtensionLength = 10;
  138. protected ResourceManager()
  139. {
  140. _lastUsedResourceCache = new CultureNameResourceSetPair();
  141. ResourceManagerMediator mediator = new ResourceManagerMediator(this);
  142. _resourceGroveler = new ManifestBasedResourceGroveler(mediator);
  143. }
  144. // Constructs a Resource Manager for files beginning with
  145. // baseName in the directory specified by resourceDir
  146. // or in the current directory. This Assembly-ignorant constructor is
  147. // mostly useful for testing your own ResourceSet implementation.
  148. //
  149. // A good example of a baseName might be "Strings". BaseName
  150. // should not end in ".resources".
  151. //
  152. // Note: System.Windows.Forms uses this method at design time.
  153. //
  154. private ResourceManager(string baseName, string resourceDir, Type usingResourceSet)
  155. {
  156. if (null == baseName)
  157. throw new ArgumentNullException(nameof(baseName));
  158. if (null == resourceDir)
  159. throw new ArgumentNullException(nameof(resourceDir));
  160. BaseNameField = baseName;
  161. _moduleDir = resourceDir;
  162. _userResourceSet = usingResourceSet;
  163. _resourceSets = new Dictionary<string, ResourceSet>();
  164. _lastUsedResourceCache = new CultureNameResourceSetPair();
  165. _useManifest = false;
  166. ResourceManagerMediator mediator = new ResourceManagerMediator(this);
  167. _resourceGroveler = new FileBasedResourceGroveler(mediator);
  168. }
  169. public ResourceManager(string baseName, Assembly assembly)
  170. {
  171. if (null == baseName)
  172. throw new ArgumentNullException(nameof(baseName));
  173. if (null == assembly)
  174. throw new ArgumentNullException(nameof(assembly));
  175. if (!assembly.IsRuntimeImplemented())
  176. throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
  177. MainAssembly = assembly;
  178. BaseNameField = baseName;
  179. CommonAssemblyInit();
  180. }
  181. public ResourceManager(string baseName, Assembly assembly, Type usingResourceSet)
  182. {
  183. if (null == baseName)
  184. throw new ArgumentNullException(nameof(baseName));
  185. if (null == assembly)
  186. throw new ArgumentNullException(nameof(assembly));
  187. if (!assembly.IsRuntimeImplemented())
  188. throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
  189. MainAssembly = assembly;
  190. BaseNameField = baseName;
  191. if (usingResourceSet != null && (usingResourceSet != s_minResourceSet) && !(usingResourceSet.IsSubclassOf(s_minResourceSet)))
  192. throw new ArgumentException(SR.Arg_ResMgrNotResSet, nameof(usingResourceSet));
  193. _userResourceSet = usingResourceSet;
  194. CommonAssemblyInit();
  195. }
  196. public ResourceManager(Type resourceSource)
  197. {
  198. if (null == resourceSource)
  199. throw new ArgumentNullException(nameof(resourceSource));
  200. if (!resourceSource.IsRuntimeImplemented())
  201. throw new ArgumentException(SR.Argument_MustBeRuntimeType);
  202. _locationInfo = resourceSource;
  203. MainAssembly = _locationInfo.Assembly;
  204. BaseNameField = resourceSource.Name;
  205. CommonAssemblyInit();
  206. }
  207. // Trying to unify code as much as possible, even though having to do a
  208. // security check in each constructor prevents it.
  209. private void CommonAssemblyInit()
  210. {
  211. #if FEATURE_APPX || ENABLE_WINRT
  212. SetUapConfiguration();
  213. #endif
  214. // Now we can use the managed resources even when using PRI's to support the APIs GetObject, GetStream...etc.
  215. _useManifest = true;
  216. _resourceSets = new Dictionary<string, ResourceSet>();
  217. _lastUsedResourceCache = new CultureNameResourceSetPair();
  218. ResourceManagerMediator mediator = new ResourceManagerMediator(this);
  219. _resourceGroveler = new ManifestBasedResourceGroveler(mediator);
  220. _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, out _fallbackLoc);
  221. }
  222. // Gets the base name for the ResourceManager.
  223. public virtual string BaseName
  224. {
  225. get { return BaseNameField; }
  226. }
  227. // Whether we should ignore the capitalization of resources when calling
  228. // GetString or GetObject.
  229. public virtual bool IgnoreCase
  230. {
  231. get { return _ignoreCase; }
  232. set { _ignoreCase = value; }
  233. }
  234. // Returns the Type of the ResourceSet the ResourceManager uses
  235. // to construct ResourceSets.
  236. public virtual Type ResourceSetType
  237. {
  238. get { return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; }
  239. }
  240. protected UltimateResourceFallbackLocation FallbackLocation
  241. {
  242. get { return _fallbackLoc; }
  243. set { _fallbackLoc = value; }
  244. }
  245. // Tells the ResourceManager to call Close on all ResourceSets and
  246. // release all resources. This will shrink your working set by
  247. // potentially a substantial amount in a running application. Any
  248. // future resource lookups on this ResourceManager will be as
  249. // expensive as the very first lookup, since it will need to search
  250. // for files and load resources again.
  251. //
  252. // This may be useful in some complex threading scenarios, where
  253. // creating a new ResourceManager isn't quite the correct behavior.
  254. public virtual void ReleaseAllResources()
  255. {
  256. Dictionary<string, ResourceSet> localResourceSets = _resourceSets;
  257. // If any calls to Close throw, at least leave ourselves in a
  258. // consistent state.
  259. _resourceSets = new Dictionary<string, ResourceSet>();
  260. _lastUsedResourceCache = new CultureNameResourceSetPair();
  261. lock (localResourceSets)
  262. {
  263. foreach ((_, ResourceSet resourceSet) in localResourceSets)
  264. {
  265. resourceSet.Close();
  266. }
  267. }
  268. }
  269. public static ResourceManager CreateFileBasedResourceManager(string baseName, string resourceDir, Type usingResourceSet)
  270. {
  271. return new ResourceManager(baseName, resourceDir, usingResourceSet);
  272. }
  273. // Given a CultureInfo, GetResourceFileName generates the name for
  274. // the binary file for the given CultureInfo. This method uses
  275. // CultureInfo's Name property as part of the file name for all cultures
  276. // other than the invariant culture. This method does not touch the disk,
  277. // and is used only to construct what a resource file name (suitable for
  278. // passing to the ResourceReader constructor) or a manifest resource file
  279. // name should look like.
  280. //
  281. // This method can be overriden to look for a different extension,
  282. // such as ".ResX", or a completely different format for naming files.
  283. protected virtual string GetResourceFileName(CultureInfo culture)
  284. {
  285. // If this is the neutral culture, don't include the culture name.
  286. if (culture.HasInvariantCultureName)
  287. {
  288. return BaseNameField + ResFileExtension;
  289. }
  290. else
  291. {
  292. CultureInfo.VerifyCultureName(culture.Name, throwException: true);
  293. return BaseNameField + "." + culture.Name + ResFileExtension;
  294. }
  295. }
  296. // WARNING: This function must be kept in sync with ResourceFallbackManager.GetEnumerator()
  297. // Return the first ResourceSet, based on the first culture ResourceFallbackManager would return
  298. internal ResourceSet GetFirstResourceSet(CultureInfo culture)
  299. {
  300. // Logic from ResourceFallbackManager.GetEnumerator()
  301. if (_neutralResourcesCulture != null && culture.Name == _neutralResourcesCulture.Name)
  302. {
  303. culture = CultureInfo.InvariantCulture;
  304. }
  305. if (_lastUsedResourceCache != null)
  306. {
  307. lock (_lastUsedResourceCache)
  308. {
  309. if (culture.Name == _lastUsedResourceCache.lastCultureName)
  310. return _lastUsedResourceCache.lastResourceSet;
  311. }
  312. }
  313. // Look in the ResourceSet table
  314. Dictionary<string, ResourceSet> localResourceSets = _resourceSets;
  315. ResourceSet rs = null;
  316. if (localResourceSets != null)
  317. {
  318. lock (localResourceSets)
  319. {
  320. localResourceSets.TryGetValue(culture.Name, out rs);
  321. }
  322. }
  323. if (rs != null)
  324. {
  325. // update the cache with the most recent ResourceSet
  326. if (_lastUsedResourceCache != null)
  327. {
  328. lock (_lastUsedResourceCache)
  329. {
  330. _lastUsedResourceCache.lastCultureName = culture.Name;
  331. _lastUsedResourceCache.lastResourceSet = rs;
  332. }
  333. }
  334. return rs;
  335. }
  336. return null;
  337. }
  338. // Looks up a set of resources for a particular CultureInfo. This is
  339. // not useful for most users of the ResourceManager - call
  340. // GetString() or GetObject() instead.
  341. //
  342. // The parameters let you control whether the ResourceSet is created
  343. // if it hasn't yet been loaded and if parent CultureInfos should be
  344. // loaded as well for resource inheritance.
  345. //
  346. public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
  347. {
  348. if (null == culture)
  349. throw new ArgumentNullException(nameof(culture));
  350. Dictionary<string, ResourceSet> localResourceSets = _resourceSets;
  351. ResourceSet rs;
  352. if (localResourceSets != null)
  353. {
  354. lock (localResourceSets)
  355. {
  356. if (localResourceSets.TryGetValue(culture.Name, out rs))
  357. return rs;
  358. }
  359. }
  360. if (_useManifest && culture.HasInvariantCultureName)
  361. {
  362. string fileName = GetResourceFileName(culture);
  363. Stream stream = MainAssembly.GetManifestResourceStream(_locationInfo, fileName);
  364. if (createIfNotExists && stream != null)
  365. {
  366. rs = ((ManifestBasedResourceGroveler)_resourceGroveler).CreateResourceSet(stream, MainAssembly);
  367. AddResourceSet(localResourceSets, culture.Name, ref rs);
  368. return rs;
  369. }
  370. }
  371. return InternalGetResourceSet(culture, createIfNotExists, tryParents);
  372. }
  373. // InternalGetResourceSet is a non-threadsafe method where all the logic
  374. // for getting a resource set lives. Access to it is controlled by
  375. // threadsafe methods such as GetResourceSet, GetString, & GetObject.
  376. // This will take a minimal number of locks.
  377. protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
  378. {
  379. Debug.Assert(culture != null, "culture != null");
  380. Dictionary<string, ResourceSet> localResourceSets = _resourceSets;
  381. ResourceSet rs = null;
  382. CultureInfo foundCulture = null;
  383. lock (localResourceSets)
  384. {
  385. if (localResourceSets.TryGetValue(culture.Name, out rs))
  386. {
  387. return rs;
  388. }
  389. }
  390. ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, tryParents);
  391. foreach (CultureInfo currentCultureInfo in mgr)
  392. {
  393. lock (localResourceSets)
  394. {
  395. if (localResourceSets.TryGetValue(currentCultureInfo.Name, out rs))
  396. {
  397. // we need to update the cache if we fellback
  398. if (culture != currentCultureInfo) foundCulture = currentCultureInfo;
  399. break;
  400. }
  401. }
  402. // InternalGetResourceSet will never be threadsafe. However, it must
  403. // be protected against reentrancy from the SAME THREAD. (ie, calling
  404. // GetSatelliteAssembly may send some window messages or trigger the
  405. // Assembly load event, which could fail then call back into the
  406. // ResourceManager). It's happened.
  407. rs = _resourceGroveler.GrovelForResourceSet(currentCultureInfo, localResourceSets,
  408. tryParents, createIfNotExists);
  409. // found a ResourceSet; we're done
  410. if (rs != null)
  411. {
  412. foundCulture = currentCultureInfo;
  413. break;
  414. }
  415. }
  416. if (rs != null && foundCulture != null)
  417. {
  418. // add entries to the cache for the cultures we have gone through
  419. // currentCultureInfo now refers to the culture that had resources.
  420. // update cultures starting from requested culture up to the culture
  421. // that had resources.
  422. foreach (CultureInfo updateCultureInfo in mgr)
  423. {
  424. AddResourceSet(localResourceSets, updateCultureInfo.Name, ref rs);
  425. // stop when we've added current or reached invariant (top of chain)
  426. if (updateCultureInfo == foundCulture)
  427. {
  428. break;
  429. }
  430. }
  431. }
  432. return rs;
  433. }
  434. // Simple helper to ease maintenance and improve readability.
  435. private static void AddResourceSet(Dictionary<string, ResourceSet> localResourceSets, string cultureName, ref ResourceSet rs)
  436. {
  437. // InternalGetResourceSet is both recursive and reentrant -
  438. // assembly load callbacks in particular are a way we can call
  439. // back into the ResourceManager in unexpectedly on the same thread.
  440. lock (localResourceSets)
  441. {
  442. // If another thread added this culture, return that.
  443. ResourceSet lostRace;
  444. if (localResourceSets.TryGetValue(cultureName, out lostRace))
  445. {
  446. if (!object.ReferenceEquals(lostRace, rs))
  447. {
  448. // Note: In certain cases, we can be trying to add a ResourceSet for multiple
  449. // cultures on one thread, while a second thread added another ResourceSet for one
  450. // of those cultures. If there is a race condition we must make sure our ResourceSet
  451. // isn't in our dictionary before closing it.
  452. if (!localResourceSets.ContainsValue(rs))
  453. rs.Dispose();
  454. rs = lostRace;
  455. }
  456. }
  457. else
  458. {
  459. localResourceSets.Add(cultureName, rs);
  460. }
  461. }
  462. }
  463. protected static Version GetSatelliteContractVersion(Assembly a)
  464. {
  465. // Ensure that the assembly reference is not null
  466. if (a == null)
  467. {
  468. throw new ArgumentNullException(nameof(a), SR.ArgumentNull_Assembly);
  469. }
  470. string v = a.GetCustomAttribute<SatelliteContractVersionAttribute>()?.Version;
  471. if (v == null)
  472. {
  473. // Return null. The calling code will use the assembly version instead to avoid potential type
  474. // and library loads caused by CA lookup.
  475. return null;
  476. }
  477. if (!Version.TryParse(v, out Version version))
  478. {
  479. throw new ArgumentException(SR.Format(SR.Arg_InvalidSatelliteContract_Asm_Ver, a.ToString(), v));
  480. }
  481. return version;
  482. }
  483. protected static CultureInfo GetNeutralResourcesLanguage(Assembly a)
  484. {
  485. // This method should be obsolete - replace it with the one below.
  486. // Unfortunately, we made it protected.
  487. return ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(a, out _);
  488. }
  489. // IGNORES VERSION
  490. internal static bool IsDefaultType(string asmTypeName,
  491. string typeName)
  492. {
  493. Debug.Assert(asmTypeName != null, "asmTypeName was unexpectedly null");
  494. // First, compare type names
  495. int comma = asmTypeName.IndexOf(',');
  496. if (((comma == -1) ? asmTypeName.Length : comma) != typeName.Length)
  497. return false;
  498. // case sensitive
  499. if (string.Compare(asmTypeName, 0, typeName, 0, typeName.Length, StringComparison.Ordinal) != 0)
  500. return false;
  501. if (comma == -1)
  502. return true;
  503. // Now, compare assembly display names (IGNORES VERSION AND PROCESSORARCHITECTURE)
  504. // also, for mscorlib ignores everything, since that's what the binder is going to do
  505. while (char.IsWhiteSpace(asmTypeName[++comma])) ;
  506. // case insensitive
  507. AssemblyName an = new AssemblyName(asmTypeName.Substring(comma));
  508. // to match IsMscorlib() in VM
  509. return string.Equals(an.Name, "mscorlib", StringComparison.OrdinalIgnoreCase);
  510. }
  511. // Looks up a resource value for a particular name. Looks in the
  512. // current thread's CultureInfo, and if not found, all parent CultureInfos.
  513. // Returns null if the resource wasn't found.
  514. //
  515. public virtual string GetString(string name)
  516. {
  517. return GetString(name, (CultureInfo)null);
  518. }
  519. // Looks up a resource value for a particular name. Looks in the
  520. // specified CultureInfo, and if not found, all parent CultureInfos.
  521. // Returns null if the resource wasn't found.
  522. //
  523. public virtual string GetString(string name, CultureInfo culture)
  524. {
  525. if (null == name)
  526. throw new ArgumentNullException(nameof(name));
  527. #if FEATURE_APPX || ENABLE_WINRT
  528. if (_useUapResourceManagement)
  529. {
  530. // Throws WinRT hresults.
  531. return GetStringFromPRI(name, culture, _neutralResourcesCulture.Name);
  532. }
  533. #endif
  534. if (culture == null)
  535. {
  536. culture = CultureInfo.CurrentUICulture;
  537. }
  538. ResourceSet last = GetFirstResourceSet(culture);
  539. if (last != null)
  540. {
  541. string value = last.GetString(name, _ignoreCase);
  542. if (value != null)
  543. return value;
  544. }
  545. // This is the CultureInfo hierarchy traversal code for resource
  546. // lookups, similar but necessarily orthogonal to the ResourceSet
  547. // lookup logic.
  548. ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
  549. foreach (CultureInfo currentCultureInfo in mgr)
  550. {
  551. ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
  552. if (rs == null)
  553. break;
  554. if (rs != last)
  555. {
  556. string value = rs.GetString(name, _ignoreCase);
  557. if (value != null)
  558. {
  559. // update last used ResourceSet
  560. if (_lastUsedResourceCache != null)
  561. {
  562. lock (_lastUsedResourceCache)
  563. {
  564. _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
  565. _lastUsedResourceCache.lastResourceSet = rs;
  566. }
  567. }
  568. return value;
  569. }
  570. last = rs;
  571. }
  572. }
  573. return null;
  574. }
  575. // Looks up a resource value for a particular name. Looks in the
  576. // current thread's CultureInfo, and if not found, all parent CultureInfos.
  577. // Returns null if the resource wasn't found.
  578. //
  579. public virtual object GetObject(string name)
  580. {
  581. return GetObject(name, (CultureInfo)null, true);
  582. }
  583. // Looks up a resource value for a particular name. Looks in the
  584. // specified CultureInfo, and if not found, all parent CultureInfos.
  585. // Returns null if the resource wasn't found.
  586. public virtual object GetObject(string name, CultureInfo culture)
  587. {
  588. return GetObject(name, culture, true);
  589. }
  590. private object GetObject(string name, CultureInfo culture, bool wrapUnmanagedMemStream)
  591. {
  592. if (null == name)
  593. throw new ArgumentNullException(nameof(name));
  594. if (null == culture)
  595. {
  596. culture = CultureInfo.CurrentUICulture;
  597. }
  598. ResourceSet last = GetFirstResourceSet(culture);
  599. if (last != null)
  600. {
  601. object value = last.GetObject(name, _ignoreCase);
  602. if (value != null)
  603. {
  604. if (value is UnmanagedMemoryStream stream && wrapUnmanagedMemStream)
  605. return new UnmanagedMemoryStreamWrapper(stream);
  606. else
  607. return value;
  608. }
  609. }
  610. // This is the CultureInfo hierarchy traversal code for resource
  611. // lookups, similar but necessarily orthogonal to the ResourceSet
  612. // lookup logic.
  613. ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
  614. foreach (CultureInfo currentCultureInfo in mgr)
  615. {
  616. ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
  617. if (rs == null)
  618. break;
  619. if (rs != last)
  620. {
  621. object value = rs.GetObject(name, _ignoreCase);
  622. if (value != null)
  623. {
  624. // update the last used ResourceSet
  625. if (_lastUsedResourceCache != null)
  626. {
  627. lock (_lastUsedResourceCache)
  628. {
  629. _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
  630. _lastUsedResourceCache.lastResourceSet = rs;
  631. }
  632. }
  633. if (value is UnmanagedMemoryStream stream && wrapUnmanagedMemStream)
  634. return new UnmanagedMemoryStreamWrapper(stream);
  635. else
  636. return value;
  637. }
  638. last = rs;
  639. }
  640. }
  641. return null;
  642. }
  643. public UnmanagedMemoryStream GetStream(string name)
  644. {
  645. return GetStream(name, (CultureInfo)null);
  646. }
  647. public UnmanagedMemoryStream GetStream(string name, CultureInfo culture)
  648. {
  649. object obj = GetObject(name, culture, false);
  650. UnmanagedMemoryStream ums = obj as UnmanagedMemoryStream;
  651. if (ums == null && obj != null)
  652. throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ResourceNotStream_Name, name));
  653. return ums;
  654. }
  655. internal class ResourceManagerMediator
  656. {
  657. private ResourceManager _rm;
  658. internal ResourceManagerMediator(ResourceManager rm)
  659. {
  660. if (rm == null)
  661. {
  662. throw new ArgumentNullException(nameof(rm));
  663. }
  664. _rm = rm;
  665. }
  666. // NEEDED ONLY BY FILE-BASED
  667. internal string ModuleDir
  668. {
  669. get { return _rm._moduleDir; }
  670. }
  671. // NEEDED BOTH BY FILE-BASED AND ASSEMBLY-BASED
  672. internal Type LocationInfo
  673. {
  674. get { return _rm._locationInfo; }
  675. }
  676. internal Type UserResourceSet
  677. {
  678. get { return _rm._userResourceSet; }
  679. }
  680. internal string BaseNameField
  681. {
  682. get { return _rm.BaseNameField; }
  683. }
  684. internal CultureInfo NeutralResourcesCulture
  685. {
  686. get { return _rm._neutralResourcesCulture; }
  687. set { _rm._neutralResourcesCulture = value; }
  688. }
  689. internal string GetResourceFileName(CultureInfo culture)
  690. {
  691. return _rm.GetResourceFileName(culture);
  692. }
  693. // NEEDED ONLY BY ASSEMBLY-BASED
  694. internal bool LookedForSatelliteContractVersion
  695. {
  696. get { return _rm._lookedForSatelliteContractVersion; }
  697. set { _rm._lookedForSatelliteContractVersion = value; }
  698. }
  699. internal Version SatelliteContractVersion
  700. {
  701. get { return _rm._satelliteContractVersion; }
  702. set { _rm._satelliteContractVersion = value; }
  703. }
  704. internal Version ObtainSatelliteContractVersion(Assembly a)
  705. {
  706. return ResourceManager.GetSatelliteContractVersion(a);
  707. }
  708. internal UltimateResourceFallbackLocation FallbackLoc
  709. {
  710. get { return _rm.FallbackLocation; }
  711. set { _rm._fallbackLoc = value; }
  712. }
  713. internal Assembly MainAssembly
  714. {
  715. get { return _rm.MainAssembly; }
  716. }
  717. // this is weird because we have BaseNameField accessor above, but we're sticking
  718. // with it for compat.
  719. internal string BaseName
  720. {
  721. get { return _rm.BaseName; }
  722. }
  723. }
  724. }
  725. }