ResourceManager.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  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. _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly;
  219. ResourceManagerMediator mediator = new ResourceManagerMediator(this);
  220. _resourceGroveler = new ManifestBasedResourceGroveler(mediator);
  221. _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
  222. }
  223. // Gets the base name for the ResourceManager.
  224. public virtual string BaseName
  225. {
  226. get { return BaseNameField; }
  227. }
  228. // Whether we should ignore the capitalization of resources when calling
  229. // GetString or GetObject.
  230. public virtual bool IgnoreCase
  231. {
  232. get { return _ignoreCase; }
  233. set { _ignoreCase = value; }
  234. }
  235. // Returns the Type of the ResourceSet the ResourceManager uses
  236. // to construct ResourceSets.
  237. public virtual Type ResourceSetType
  238. {
  239. get { return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; }
  240. }
  241. protected UltimateResourceFallbackLocation FallbackLocation
  242. {
  243. get { return _fallbackLoc; }
  244. set { _fallbackLoc = value; }
  245. }
  246. // Tells the ResourceManager to call Close on all ResourceSets and
  247. // release all resources. This will shrink your working set by
  248. // potentially a substantial amount in a running application. Any
  249. // future resource lookups on this ResourceManager will be as
  250. // expensive as the very first lookup, since it will need to search
  251. // for files and load resources again.
  252. //
  253. // This may be useful in some complex threading scenarios, where
  254. // creating a new ResourceManager isn't quite the correct behavior.
  255. public virtual void ReleaseAllResources()
  256. {
  257. Dictionary<string, ResourceSet> localResourceSets = _resourceSets;
  258. // If any calls to Close throw, at least leave ourselves in a
  259. // consistent state.
  260. _resourceSets = new Dictionary<string, ResourceSet>();
  261. _lastUsedResourceCache = new CultureNameResourceSetPair();
  262. lock (localResourceSets)
  263. {
  264. foreach ((_, ResourceSet resourceSet) in localResourceSets)
  265. {
  266. resourceSet.Close();
  267. }
  268. }
  269. }
  270. public static ResourceManager CreateFileBasedResourceManager(string baseName, string resourceDir, Type usingResourceSet)
  271. {
  272. return new ResourceManager(baseName, resourceDir, usingResourceSet);
  273. }
  274. // Given a CultureInfo, GetResourceFileName generates the name for
  275. // the binary file for the given CultureInfo. This method uses
  276. // CultureInfo's Name property as part of the file name for all cultures
  277. // other than the invariant culture. This method does not touch the disk,
  278. // and is used only to construct what a resource file name (suitable for
  279. // passing to the ResourceReader constructor) or a manifest resource file
  280. // name should look like.
  281. //
  282. // This method can be overriden to look for a different extension,
  283. // such as ".ResX", or a completely different format for naming files.
  284. protected virtual string GetResourceFileName(CultureInfo culture)
  285. {
  286. // If this is the neutral culture, don't include the culture name.
  287. if (culture.HasInvariantCultureName)
  288. {
  289. return BaseNameField + ResFileExtension;
  290. }
  291. else
  292. {
  293. CultureInfo.VerifyCultureName(culture.Name, throwException: true);
  294. return BaseNameField + "." + culture.Name + ResFileExtension;
  295. }
  296. }
  297. // WARNING: This function must be kept in sync with ResourceFallbackManager.GetEnumerator()
  298. // Return the first ResourceSet, based on the first culture ResourceFallbackManager would return
  299. internal ResourceSet GetFirstResourceSet(CultureInfo culture)
  300. {
  301. // Logic from ResourceFallbackManager.GetEnumerator()
  302. if (_neutralResourcesCulture != null && culture.Name == _neutralResourcesCulture.Name)
  303. {
  304. culture = CultureInfo.InvariantCulture;
  305. }
  306. if (_lastUsedResourceCache != null)
  307. {
  308. lock (_lastUsedResourceCache)
  309. {
  310. if (culture.Name == _lastUsedResourceCache.lastCultureName)
  311. return _lastUsedResourceCache.lastResourceSet;
  312. }
  313. }
  314. // Look in the ResourceSet table
  315. Dictionary<string, ResourceSet> localResourceSets = _resourceSets;
  316. ResourceSet rs = null;
  317. if (localResourceSets != null)
  318. {
  319. lock (localResourceSets)
  320. {
  321. localResourceSets.TryGetValue(culture.Name, out rs);
  322. }
  323. }
  324. if (rs != null)
  325. {
  326. // update the cache with the most recent ResourceSet
  327. if (_lastUsedResourceCache != null)
  328. {
  329. lock (_lastUsedResourceCache)
  330. {
  331. _lastUsedResourceCache.lastCultureName = culture.Name;
  332. _lastUsedResourceCache.lastResourceSet = rs;
  333. }
  334. }
  335. return rs;
  336. }
  337. return null;
  338. }
  339. // Looks up a set of resources for a particular CultureInfo. This is
  340. // not useful for most users of the ResourceManager - call
  341. // GetString() or GetObject() instead.
  342. //
  343. // The parameters let you control whether the ResourceSet is created
  344. // if it hasn't yet been loaded and if parent CultureInfos should be
  345. // loaded as well for resource inheritance.
  346. //
  347. public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
  348. {
  349. if (null == culture)
  350. throw new ArgumentNullException(nameof(culture));
  351. Dictionary<string, ResourceSet> localResourceSets = _resourceSets;
  352. ResourceSet rs;
  353. if (localResourceSets != null)
  354. {
  355. lock (localResourceSets)
  356. {
  357. if (localResourceSets.TryGetValue(culture.Name, out rs))
  358. return rs;
  359. }
  360. }
  361. if (_useManifest && culture.HasInvariantCultureName)
  362. {
  363. string fileName = GetResourceFileName(culture);
  364. Stream stream = MainAssembly.GetManifestResourceStream(_locationInfo, fileName);
  365. if (createIfNotExists && stream != null)
  366. {
  367. rs = ((ManifestBasedResourceGroveler)_resourceGroveler).CreateResourceSet(stream, MainAssembly);
  368. AddResourceSet(localResourceSets, culture.Name, ref rs);
  369. return rs;
  370. }
  371. }
  372. return InternalGetResourceSet(culture, createIfNotExists, tryParents);
  373. }
  374. // InternalGetResourceSet is a non-threadsafe method where all the logic
  375. // for getting a resource set lives. Access to it is controlled by
  376. // threadsafe methods such as GetResourceSet, GetString, & GetObject.
  377. // This will take a minimal number of locks.
  378. protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
  379. {
  380. Debug.Assert(culture != null, "culture != null");
  381. Dictionary<string, ResourceSet> localResourceSets = _resourceSets;
  382. ResourceSet rs = null;
  383. CultureInfo foundCulture = null;
  384. lock (localResourceSets)
  385. {
  386. if (localResourceSets.TryGetValue(culture.Name, out rs))
  387. {
  388. return rs;
  389. }
  390. }
  391. ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, tryParents);
  392. foreach (CultureInfo currentCultureInfo in mgr)
  393. {
  394. lock (localResourceSets)
  395. {
  396. if (localResourceSets.TryGetValue(currentCultureInfo.Name, out rs))
  397. {
  398. // we need to update the cache if we fellback
  399. if (culture != currentCultureInfo) foundCulture = currentCultureInfo;
  400. break;
  401. }
  402. }
  403. // InternalGetResourceSet will never be threadsafe. However, it must
  404. // be protected against reentrancy from the SAME THREAD. (ie, calling
  405. // GetSatelliteAssembly may send some window messages or trigger the
  406. // Assembly load event, which could fail then call back into the
  407. // ResourceManager). It's happened.
  408. rs = _resourceGroveler.GrovelForResourceSet(currentCultureInfo, localResourceSets,
  409. tryParents, createIfNotExists);
  410. // found a ResourceSet; we're done
  411. if (rs != null)
  412. {
  413. foundCulture = currentCultureInfo;
  414. break;
  415. }
  416. }
  417. if (rs != null && foundCulture != null)
  418. {
  419. // add entries to the cache for the cultures we have gone through
  420. // currentCultureInfo now refers to the culture that had resources.
  421. // update cultures starting from requested culture up to the culture
  422. // that had resources.
  423. foreach (CultureInfo updateCultureInfo in mgr)
  424. {
  425. AddResourceSet(localResourceSets, updateCultureInfo.Name, ref rs);
  426. // stop when we've added current or reached invariant (top of chain)
  427. if (updateCultureInfo == foundCulture)
  428. {
  429. break;
  430. }
  431. }
  432. }
  433. return rs;
  434. }
  435. // Simple helper to ease maintenance and improve readability.
  436. private static void AddResourceSet(Dictionary<string, ResourceSet> localResourceSets, string cultureName, ref ResourceSet rs)
  437. {
  438. // InternalGetResourceSet is both recursive and reentrant -
  439. // assembly load callbacks in particular are a way we can call
  440. // back into the ResourceManager in unexpectedly on the same thread.
  441. lock (localResourceSets)
  442. {
  443. // If another thread added this culture, return that.
  444. ResourceSet lostRace;
  445. if (localResourceSets.TryGetValue(cultureName, out lostRace))
  446. {
  447. if (!object.ReferenceEquals(lostRace, rs))
  448. {
  449. // Note: In certain cases, we can be trying to add a ResourceSet for multiple
  450. // cultures on one thread, while a second thread added another ResourceSet for one
  451. // of those cultures. If there is a race condition we must make sure our ResourceSet
  452. // isn't in our dictionary before closing it.
  453. if (!localResourceSets.ContainsValue(rs))
  454. rs.Dispose();
  455. rs = lostRace;
  456. }
  457. }
  458. else
  459. {
  460. localResourceSets.Add(cultureName, rs);
  461. }
  462. }
  463. }
  464. protected static Version GetSatelliteContractVersion(Assembly a)
  465. {
  466. // Ensure that the assembly reference is not null
  467. if (a == null)
  468. {
  469. throw new ArgumentNullException(nameof(a), SR.ArgumentNull_Assembly);
  470. }
  471. string v = a.GetCustomAttribute<SatelliteContractVersionAttribute>()?.Version;
  472. if (v == null)
  473. {
  474. // Return null. The calling code will use the assembly version instead to avoid potential type
  475. // and library loads caused by CA lookup.
  476. return null;
  477. }
  478. if (!Version.TryParse(v, out Version version))
  479. {
  480. throw new ArgumentException(SR.Format(SR.Arg_InvalidSatelliteContract_Asm_Ver, a.ToString(), v));
  481. }
  482. return version;
  483. }
  484. protected static CultureInfo GetNeutralResourcesLanguage(Assembly a)
  485. {
  486. // This method should be obsolete - replace it with the one below.
  487. // Unfortunately, we made it protected.
  488. UltimateResourceFallbackLocation ignoringUsefulData = UltimateResourceFallbackLocation.MainAssembly;
  489. CultureInfo culture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(a, ref ignoringUsefulData);
  490. return culture;
  491. }
  492. // IGNORES VERSION
  493. internal static bool IsDefaultType(string asmTypeName,
  494. string typeName)
  495. {
  496. Debug.Assert(asmTypeName != null, "asmTypeName was unexpectedly null");
  497. // First, compare type names
  498. int comma = asmTypeName.IndexOf(',');
  499. if (((comma == -1) ? asmTypeName.Length : comma) != typeName.Length)
  500. return false;
  501. // case sensitive
  502. if (string.Compare(asmTypeName, 0, typeName, 0, typeName.Length, StringComparison.Ordinal) != 0)
  503. return false;
  504. if (comma == -1)
  505. return true;
  506. // Now, compare assembly display names (IGNORES VERSION AND PROCESSORARCHITECTURE)
  507. // also, for mscorlib ignores everything, since that's what the binder is going to do
  508. while (char.IsWhiteSpace(asmTypeName[++comma])) ;
  509. // case insensitive
  510. AssemblyName an = new AssemblyName(asmTypeName.Substring(comma));
  511. // to match IsMscorlib() in VM
  512. return string.Equals(an.Name, "mscorlib", StringComparison.OrdinalIgnoreCase);
  513. }
  514. // Looks up a resource value for a particular name. Looks in the
  515. // current thread's CultureInfo, and if not found, all parent CultureInfos.
  516. // Returns null if the resource wasn't found.
  517. //
  518. public virtual string GetString(string name)
  519. {
  520. return GetString(name, (CultureInfo)null);
  521. }
  522. // Looks up a resource value for a particular name. Looks in the
  523. // specified CultureInfo, and if not found, all parent CultureInfos.
  524. // Returns null if the resource wasn't found.
  525. //
  526. public virtual string GetString(string name, CultureInfo culture)
  527. {
  528. if (null == name)
  529. throw new ArgumentNullException(nameof(name));
  530. #if FEATURE_APPX || ENABLE_WINRT
  531. if (_useUapResourceManagement)
  532. {
  533. // Throws WinRT hresults.
  534. return GetStringFromPRI(name, culture, _neutralResourcesCulture.Name);
  535. }
  536. #endif
  537. if (culture == null)
  538. {
  539. culture = CultureInfo.CurrentUICulture;
  540. }
  541. ResourceSet last = GetFirstResourceSet(culture);
  542. if (last != null)
  543. {
  544. string value = last.GetString(name, _ignoreCase);
  545. if (value != null)
  546. return value;
  547. }
  548. // This is the CultureInfo hierarchy traversal code for resource
  549. // lookups, similar but necessarily orthogonal to the ResourceSet
  550. // lookup logic.
  551. ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
  552. foreach (CultureInfo currentCultureInfo in mgr)
  553. {
  554. ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
  555. if (rs == null)
  556. break;
  557. if (rs != last)
  558. {
  559. string value = rs.GetString(name, _ignoreCase);
  560. if (value != null)
  561. {
  562. // update last used ResourceSet
  563. if (_lastUsedResourceCache != null)
  564. {
  565. lock (_lastUsedResourceCache)
  566. {
  567. _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
  568. _lastUsedResourceCache.lastResourceSet = rs;
  569. }
  570. }
  571. return value;
  572. }
  573. last = rs;
  574. }
  575. }
  576. return null;
  577. }
  578. // Looks up a resource value for a particular name. Looks in the
  579. // current thread's CultureInfo, and if not found, all parent CultureInfos.
  580. // Returns null if the resource wasn't found.
  581. //
  582. public virtual object GetObject(string name)
  583. {
  584. return GetObject(name, (CultureInfo)null, true);
  585. }
  586. // Looks up a resource value for a particular name. Looks in the
  587. // specified CultureInfo, and if not found, all parent CultureInfos.
  588. // Returns null if the resource wasn't found.
  589. public virtual object GetObject(string name, CultureInfo culture)
  590. {
  591. return GetObject(name, culture, true);
  592. }
  593. private object GetObject(string name, CultureInfo culture, bool wrapUnmanagedMemStream)
  594. {
  595. if (null == name)
  596. throw new ArgumentNullException(nameof(name));
  597. if (null == culture)
  598. {
  599. culture = CultureInfo.CurrentUICulture;
  600. }
  601. ResourceSet last = GetFirstResourceSet(culture);
  602. if (last != null)
  603. {
  604. object value = last.GetObject(name, _ignoreCase);
  605. if (value != null)
  606. {
  607. if (value is UnmanagedMemoryStream stream && wrapUnmanagedMemStream)
  608. return new UnmanagedMemoryStreamWrapper(stream);
  609. else
  610. return value;
  611. }
  612. }
  613. // This is the CultureInfo hierarchy traversal code for resource
  614. // lookups, similar but necessarily orthogonal to the ResourceSet
  615. // lookup logic.
  616. ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
  617. foreach (CultureInfo currentCultureInfo in mgr)
  618. {
  619. ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
  620. if (rs == null)
  621. break;
  622. if (rs != last)
  623. {
  624. object value = rs.GetObject(name, _ignoreCase);
  625. if (value != null)
  626. {
  627. // update the last used ResourceSet
  628. if (_lastUsedResourceCache != null)
  629. {
  630. lock (_lastUsedResourceCache)
  631. {
  632. _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
  633. _lastUsedResourceCache.lastResourceSet = rs;
  634. }
  635. }
  636. if (value is UnmanagedMemoryStream stream && wrapUnmanagedMemStream)
  637. return new UnmanagedMemoryStreamWrapper(stream);
  638. else
  639. return value;
  640. }
  641. last = rs;
  642. }
  643. }
  644. return null;
  645. }
  646. public UnmanagedMemoryStream GetStream(string name)
  647. {
  648. return GetStream(name, (CultureInfo)null);
  649. }
  650. public UnmanagedMemoryStream GetStream(string name, CultureInfo culture)
  651. {
  652. object obj = GetObject(name, culture, false);
  653. UnmanagedMemoryStream ums = obj as UnmanagedMemoryStream;
  654. if (ums == null && obj != null)
  655. throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ResourceNotStream_Name, name));
  656. return ums;
  657. }
  658. internal class ResourceManagerMediator
  659. {
  660. private ResourceManager _rm;
  661. internal ResourceManagerMediator(ResourceManager rm)
  662. {
  663. if (rm == null)
  664. {
  665. throw new ArgumentNullException(nameof(rm));
  666. }
  667. _rm = rm;
  668. }
  669. // NEEDED ONLY BY FILE-BASED
  670. internal string ModuleDir
  671. {
  672. get { return _rm._moduleDir; }
  673. }
  674. // NEEDED BOTH BY FILE-BASED AND ASSEMBLY-BASED
  675. internal Type LocationInfo
  676. {
  677. get { return _rm._locationInfo; }
  678. }
  679. internal Type UserResourceSet
  680. {
  681. get { return _rm._userResourceSet; }
  682. }
  683. internal string BaseNameField
  684. {
  685. get { return _rm.BaseNameField; }
  686. }
  687. internal CultureInfo NeutralResourcesCulture
  688. {
  689. get { return _rm._neutralResourcesCulture; }
  690. set { _rm._neutralResourcesCulture = value; }
  691. }
  692. internal string GetResourceFileName(CultureInfo culture)
  693. {
  694. return _rm.GetResourceFileName(culture);
  695. }
  696. // NEEDED ONLY BY ASSEMBLY-BASED
  697. internal bool LookedForSatelliteContractVersion
  698. {
  699. get { return _rm._lookedForSatelliteContractVersion; }
  700. set { _rm._lookedForSatelliteContractVersion = value; }
  701. }
  702. internal Version SatelliteContractVersion
  703. {
  704. get { return _rm._satelliteContractVersion; }
  705. set { _rm._satelliteContractVersion = value; }
  706. }
  707. internal Version ObtainSatelliteContractVersion(Assembly a)
  708. {
  709. return ResourceManager.GetSatelliteContractVersion(a);
  710. }
  711. internal UltimateResourceFallbackLocation FallbackLoc
  712. {
  713. get { return _rm.FallbackLocation; }
  714. set { _rm._fallbackLoc = value; }
  715. }
  716. internal Assembly MainAssembly
  717. {
  718. get { return _rm.MainAssembly; }
  719. }
  720. // this is weird because we have BaseNameField accessor above, but we're sticking
  721. // with it for compat.
  722. internal string BaseName
  723. {
  724. get { return _rm.BaseName; }
  725. }
  726. }
  727. }
  728. }