We need you! We're working hard on the next version of Developer Fusion - Let us know what you think we should be up to!

The Labs \ Source Viewer \ SSCLI \ System.Resources \ ResourceManager

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  ResourceManager
  18. **
  19. **
  20. ** Purpose: Default way to access String resources from
  21. ** an assembly.
  22. **
  23. **
  24. ===========================================================*/
  25. namespace System.Resources
  26. {
  27.     using System;
  28.     using System.IO;
  29.     using System.Globalization;
  30.     using System.Collections;
  31.     using System.Text;
  32.     using System.Reflection;
  33.     using System.Runtime.Remoting.Activation;
  34.     using System.Runtime.Serialization;
  35.     using System.Security.Permissions;
  36.     using System.Threading;
  37.     using System.Runtime.InteropServices;
  38.     using Microsoft.Win32;
  39.     using System.Collections.Generic;
  40.     using System.Runtime.Versioning;
  41.    
  42.     // Resource Manager exposes an assembly's resources to an application for
  43.     // the correct CultureInfo. An example would be localizing text for a
  44.     // user-visible message. Create a set of resource files listing a name
  45.     // for a message and its value, compile them using ResGen, put them in
  46.     // an appropriate place (your assembly manifest(?)), then create a Resource
  47.     // Manager and query for the name of the message you want. The Resource
  48.     // Manager will use CultureInfo.GetCurrentUICulture() to look
  49.     // up a resource for your user's locale settings.
  50.     //
  51.     // Users should ideally create a resource file for every culture, or
  52.     // at least a meaningful subset. The filenames will follow the naming
  53.     // scheme:
  54.     //
  55.     // basename.culture name.resources
  56.     //
  57.     // The base name can be the name of your application, or depending on
  58.     // the granularity desired, possibly the name of each class. The culture
  59.     // name is determined from CultureInfo's Name property.
  60.     // An example file name may be MyApp.en-US.resources for
  61.     // MyApp's US English resources.
  62.     //
  63.     [Serializable()]
  64.     [System.Runtime.InteropServices.ComVisible(true)]
  65.     public class ResourceManager
  66.     {
  67.         protected string BaseNameField;
  68.         // Sets is a many-to-one table of CultureInfos mapped to ResourceSets.
  69.         // Don't synchronize ResourceSets - too fine-grained a lock to be effective
  70.         protected Hashtable ResourceSets;
  71.         private string moduleDir;
  72.         // For assembly-ignorant directory location
  73.         protected Assembly MainAssembly;
  74.         // Need the assembly manifest sometimes.
  75.         private Type _locationInfo;
  76.         // For Assembly or type-based directory layout
  77.         private Type _userResourceSet;
  78.         // Which ResourceSet instance to create
  79.         private CultureInfo _neutralResourcesCulture;
  80.         // For perf optimizations.
  81.         private bool _ignoreCase;
  82.         // Whether case matters in GetString & GetObject
  83.         // When we create a separate FileBasedResourceManager subclass,
  84.         // UseManifest can be replaced with "false".
  85.         private bool UseManifest;
  86.         // Use Assembly manifest, or grovel disk.
  87.         // When we create a separate FileBasedResourceManager subclass,
  88.         // UseSatelliteAssem can be replaced with "true".
  89.         private bool UseSatelliteAssem;
  90.         // Are all the .resources files in the
  91.         // main assembly, or in satellite assemblies for each culture?
  92.         private static Hashtable _installedSatelliteInfo;
  93.         // Give the user the option
  94.         // to prevent certain satellite assembly probes via a config file.
  95.         // Note that config files are per-appdomain, not per-assembly nor process
  96.         private static bool _checkedConfigFile;
  97.         // Did we read the app's config file?
  98.         // Whether to fall back to the main assembly or a particular
  99.         // satellite for the neutral resources.
  100.         [OptionalField()]
  101.         private UltimateResourceFallbackLocation _fallbackLoc;
  102.         // Version number of satellite assemblies to look for. May be null.
  103.         [OptionalField()]
  104.         private Version _satelliteContractVersion;
  105.         [OptionalField()]
  106.         private bool _lookedForSatelliteContractVersion;
  107.        
  108.         private Assembly _callingAssembly;
  109.         // Assembly who created the ResMgr.
  110.         public static readonly int MagicNumber = unchecked((int)3203386062u);
  111.         // If only hex had a K...
  112.         // Version number so ResMgr can get the ideal set of classes for you.
  113.         // ResMgr header is:
  114.         // 1) MagicNumber (little endian Int32)
  115.         // 2) HeaderVersionNumber (little endian Int32)
  116.         // 3) Num Bytes to skip past ResMgr header (little endian Int32)
  117.         // 4) IResourceReader type name for this file (bytelength-prefixed UTF-8 String)
  118.         // 5) ResourceSet type name for this file (bytelength-prefixed UTF8 String)
  119.         public static readonly int HeaderVersionNumber = 1;
  120.        
  121.         //
  122.         //It would be better if we could use _neutralCulture instead of calling
  123.         //CultureInfo.InvariantCulture everywhere, but we run into problems with the .cctor. CultureInfo
  124.         //initializes assembly, which initializes ResourceManager, which tries to get a CultureInfo which isn't
  125.         //there yet because CultureInfo's class initializer hasn't finished. If we move SystemResMgr off of
  126.         //Assembly (or at least make it an internal property) we should be able to circumvent this problem.
  127.         //
  128.         // private static CultureInfo _neutralCulture = null;
  129.        
  130.         // This is our min required ResourceSet type.
  131.         private static readonly Type _minResourceSet = typeof(ResourceSet);
  132.         // These Strings are used to avoid using Reflection in CreateResourceSet.
  133.         // The first set are used by ResourceWriter. The second are used by
  134.         // InternalResGen.
  135.         static internal readonly string ResReaderTypeName = typeof(ResourceReader).FullName;
  136.         static internal readonly string ResSetTypeName = typeof(RuntimeResourceSet).FullName;
  137.         static internal readonly string MscorlibName = typeof(ResourceReader).Assembly.FullName;
  138.         internal const string ResFileExtension = ".resources";
  139.         internal const int ResFileExtensionLength = 10;
  140.        
  141.         // My private debugging aid. Set to 5 or 6 for verbose output. Set to 3
  142.         // for summary level information.
  143.         static internal readonly int DEBUG = 0;
  144.        
  145.         protected ResourceManager()
  146.         {
  147.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  148.             _callingAssembly = Assembly.nGetExecutingAssembly(ref stackMark);
  149.         }
  150.        
  151.         // Constructs a Resource Manager for files beginning with
  152.         // baseName in the directory specified by resourceDir
  153.         // or in the current directory. This Assembly-ignorant constructor is
  154.         // mostly useful for testing your own ResourceSet implementation.
  155.         //
  156.         // A good example of a baseName might be "Strings". BaseName
  157.         // should not end in ".resources".
  158.         //
  159.         // Note: System.Windows.Forms uses this method at design time.
  160.         //
  161.         [ResourceExposure(ResourceScope.Machine)]
  162.         [ResourceConsumption(ResourceScope.Machine)]
  163.         private ResourceManager(string baseName, string resourceDir, Type usingResourceSet)
  164.         {
  165.            
  166.             //
  167.             // __FileBasedResourceManager: We should move all the file-based functionality into a
  168.             // (potentially internal) subclass of ResourceManager. This will simplify
  169.             // InternalGetResourceSet a lot. We can also make several fields like UseManifest
  170.             // private.
  171.             //
  172.            
  173.            
  174.             if (null == baseName)
  175.                 throw new ArgumentNullException("baseName");
  176.             if (null == resourceDir)
  177.                 throw new ArgumentNullException("resourceDir");
  178.            
  179.             BaseNameField = baseName;
  180.            
  181.             moduleDir = resourceDir;
  182.             _userResourceSet = usingResourceSet;
  183.             ResourceSets = new Hashtable();
  184.             UseManifest = false;
  185.            
  186.             #if _DEBUG
  187.             if (DEBUG >= 3) {
  188.                 // Detect missing neutral locale resources.
  189.                 CultureInfo culture = CultureInfo.InvariantCulture;
  190.                 string defaultResPath = FindResourceFile(culture);
  191.                 if (defaultResPath == null || !File.Exists(defaultResPath)) {
  192.                     string defaultResName = GetResourceFileName(culture);
  193.                     string dir = moduleDir;
  194.                     if (defaultResPath != null)
  195.                         dir = Path.GetDirectoryName(defaultResPath);
  196.                     BCLDebug.Log("While constructing a ResourceManager, your application's neutral " + ResFileExtension + " file couldn't be found. Expected to find a " + ResFileExtension + " file called \"" + defaultResName + "\" in the directory \"" + dir + "\". However, this is optional, and if you're extremely careful, you can ignore this.");
  197.                 }
  198.                 else
  199.                     BCLDebug.Log("ResourceManager found default culture's " + ResFileExtension + " file, " + defaultResPath);
  200.             }
  201.             #endif
  202.         }
  203.        
  204.        
  205.         public ResourceManager(string baseName, Assembly assembly)
  206.         {
  207.             if (null == baseName)
  208.                 throw new ArgumentNullException("baseName");
  209.             if (null == assembly)
  210.                 throw new ArgumentNullException("assembly");
  211.            
  212.             MainAssembly = assembly;
  213.             _locationInfo = null;
  214.             BaseNameField = baseName;
  215.            
  216.             CommonSatelliteAssemblyInit();
  217.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  218.             _callingAssembly = Assembly.nGetExecutingAssembly(ref stackMark);
  219.             if (assembly == typeof(object).Assembly && _callingAssembly != assembly)
  220.                 _callingAssembly = null;
  221.         }
  222.        
  223.         public ResourceManager(string baseName, Assembly assembly, Type usingResourceSet)
  224.         {
  225.             if (null == baseName)
  226.                 throw new ArgumentNullException("baseName");
  227.             if (null == assembly)
  228.                 throw new ArgumentNullException("assembly");
  229.            
  230.             MainAssembly = assembly;
  231.             _locationInfo = null;
  232.             BaseNameField = baseName;
  233.            
  234.             if (usingResourceSet != null && (usingResourceSet != _minResourceSet) && !(usingResourceSet.IsSubclassOf(_minResourceSet)))
  235.                 throw new ArgumentException(Environment.GetResourceString("Arg_ResMgrNotResSet"), "usingResourceSet");
  236.             _userResourceSet = usingResourceSet;
  237.            
  238.             CommonSatelliteAssemblyInit();
  239.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  240.             _callingAssembly = Assembly.nGetExecutingAssembly(ref stackMark);
  241.             if (assembly == typeof(object).Assembly && _callingAssembly != assembly)
  242.                 _callingAssembly = null;
  243.         }
  244.        
  245.         public ResourceManager(Type resourceSource)
  246.         {
  247.             if (null == resourceSource)
  248.                 throw new ArgumentNullException("resourceSource");
  249.            
  250.             _locationInfo = resourceSource;
  251.             MainAssembly = _locationInfo.Assembly;
  252.             BaseNameField = resourceSource.Name;
  253.            
  254.             CommonSatelliteAssemblyInit();
  255.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  256.             _callingAssembly = Assembly.nGetExecutingAssembly(ref stackMark);
  257.             // Special case for mscorlib - protect mscorlib's private resources.
  258.             if (MainAssembly == typeof(object).Assembly && _callingAssembly != MainAssembly)
  259.                 _callingAssembly = null;
  260.         }
  261.        
  262.         // Trying to unify code as much as possible, even though having to do a
  263.         // security check in each constructor prevents it.
  264.         private void CommonSatelliteAssemblyInit()
  265.         {
  266.             UseManifest = true;
  267.             UseSatelliteAssem = true;
  268.            
  269.             ResourceSets = new Hashtable();
  270.            
  271.             _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly;
  272.            
  273.             #if _DEBUG
  274.             if (DEBUG >= 3) {
  275.                 // Detect missing neutral locale resources.
  276.                 string defaultResName = GetResourceFileName(CultureInfo.InvariantCulture);
  277.                 string outputResName = defaultResName;
  278.                 if (_locationInfo != null && _locationInfo.Namespace != null)
  279.                     outputResName = _locationInfo.Namespace + Type.Delimiter + defaultResName;
  280.                 if (!MainAssemblyContainsResourceBlob(defaultResName)) {
  281.                     BCLDebug.Log("Your main assembly really should contain the neutral culture's resources. Expected to find a resource blob in your assembly manifest called \"" + outputResName + "\". Check capitalization in your assembly manifest. If you're extremely careful though, this is only optional, not a requirement.");
  282.                 }
  283.                 else {
  284.                     BCLDebug.Log("ResourceManager found default culture's " + ResFileExtension + " file, " + defaultResName);
  285.                 }
  286.             }
  287.             #endif
  288.         }
  289.        
  290.         // Gets the base name for the ResourceManager.
  291.         public virtual string BaseName {
  292.             get { return BaseNameField; }
  293.         }
  294.        
  295.         // Whether we should ignore the capitalization of resources when calling
  296.         // GetString or GetObject.
  297.         public virtual bool IgnoreCase {
  298.             get { return _ignoreCase; }
  299.             set { _ignoreCase = value; }
  300.         }
  301.        
  302.         // Returns the Type of the ResourceSet the ResourceManager uses
  303.         // to construct ResourceSets.
  304.         public virtual Type ResourceSetType {
  305.             get { return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; }
  306.         }
  307.        
  308.         protected UltimateResourceFallbackLocation FallbackLocation {
  309.             get { return _fallbackLoc; }
  310.             set { _fallbackLoc = value; }
  311.         }
  312.        
  313.         // Tells the ResourceManager to call Close on all ResourceSets and
  314.         // release all resources. This will shrink your working set by
  315.         // potentially a substantial amount in a running application. Any
  316.         // future resource lookups on this ResourceManager will be as
  317.         // expensive as the very first lookup, since it will need to search
  318.         // for files and load resources again.
  319.         //
  320.         // This may be useful in some complex threading scenarios, where
  321.         // creating a new ResourceManager isn't quite the correct behavior.
  322.         public virtual void ReleaseAllResources()
  323.         {
  324.             #if _DEBUG
  325.             if (DEBUG >= 1)
  326.                 BCLDebug.Log("Calling ResourceManager::ReleaseAllResources");
  327.             #endif
  328.             IDictionaryEnumerator setEnum = ResourceSets.GetEnumerator();
  329.             // If any calls to Close throw, at least leave ourselves in a
  330.             // consistent state.
  331.             ResourceSets = new Hashtable();
  332.             while (setEnum.MoveNext()) {
  333.                 ((ResourceSet)setEnum.Value).Close();
  334.             }
  335.         }
  336.        
  337.         // Returns whether or not the main assembly contains a particular resource
  338.         // blob in it's assembly manifest. Used to verify that the neutral
  339.         // Culture's .resources file is present in the main assembly
  340.         #if _DEBUG
  341.         private bool MainAssemblyContainsResourceBlob(string defaultResName)
  342.         {
  343.             string resName = defaultResName;
  344.             if (_locationInfo != null && _locationInfo.Namespace != null)
  345.                 resName = _locationInfo.Namespace + Type.Delimiter + defaultResName;
  346.             string[] blobs = MainAssembly.GetManifestResourceNames();
  347.             foreach (string s in blobs)
  348.                 if (s.Equals(resName))
  349.                     return true;
  350.             return false;
  351.         }
  352.         #endif
  353.        
  354.         [ResourceExposure(ResourceScope.Machine)]
  355.         [ResourceConsumption(ResourceScope.Machine)]
  356.         public static ResourceManager CreateFileBasedResourceManager(string baseName, string resourceDir, Type usingResourceSet)
  357.         {
  358.             return new ResourceManager(baseName, resourceDir, usingResourceSet);
  359.         }
  360.        
  361.         // Given a CultureInfo, it generates the path &; file name for
  362.         // the .resources file for that CultureInfo. This method will grovel
  363.         // the disk looking for the correct file name & path. Uses CultureInfo's
  364.         // Name property. If the module directory was set in the ResourceManager
  365.         // constructor, we'll look there first. If it couldn't be found in the module
  366.         // diretory or the module dir wasn't provided, look in the current
  367.         // directory.
  368.         [ResourceExposure(ResourceScope.Machine)]
  369.         [ResourceConsumption(ResourceScope.Machine)]
  370.         private string FindResourceFile(CultureInfo culture)
  371.         {
  372.            
  373.             // __FileBasedResourceManager: When we create a __FileBasedResourceManager, move this method
  374.             // to that class. This is part of the assembly-ignorant implementation
  375.             // that really doesn't belong on ResourceManager.
  376.            
  377.             string fileName = GetResourceFileName(culture);
  378.            
  379.             // If we have a moduleDir, check there first. Get module fully
  380.             // qualified name, append path to that.
  381.             if (moduleDir != null) {
  382.                 #if _DEBUG
  383.                 if (DEBUG >= 3)
  384.                     BCLDebug.Log("FindResourceFile: checking module dir: \"" + moduleDir + '"');
  385.                 #endif
  386.                
  387.                 string path = Path.Combine(moduleDir, fileName);
  388.                 if (File.Exists(path)) {
  389.                     #if _DEBUG
  390.                     if (DEBUG >= 3)
  391.                         BCLDebug.Log("Found resource file in module dir! " + path);
  392.                     #endif
  393.                     return path;
  394.                 }
  395.             }
  396.            
  397.             #if _DEBUG
  398.             if (DEBUG >= 3)
  399.                 BCLDebug.Log("Couldn't find resource file in module dir, checking .\\" + fileName);
  400.             #endif
  401.            
  402.             // look in .
  403.             if (File.Exists(fileName))
  404.                 return fileName;
  405.            
  406.             return null;
  407.             // give up.
  408.         }
  409.        
  410.         // Given a CultureInfo, GetResourceFileName generates the name for
  411.         // the binary file for the given CultureInfo. This method uses
  412.         // CultureInfo's Name property as part of the file name for all cultures
  413.         // other than the invariant culture. This method does not touch the disk,
  414.         // and is used only to construct what a resource file name (suitable for
  415.         // passing to the ResourceReader constructor) or a manifest resource blob
  416.         // name should look like.
  417.         //
  418.         // This method can be overriden to look for a different extension,
  419.         // such as ".ResX", or a completely different format for naming files.
  420.         protected virtual string GetResourceFileName(CultureInfo culture)
  421.         {
  422.             StringBuilder sb = new StringBuilder(255);
  423.             sb.Append(BaseNameField);
  424.             // If this is the neutral culture, don't append culture name.
  425.             if (!culture.Equals(CultureInfo.InvariantCulture)) {
  426.                 CultureInfo.VerifyCultureName(culture, true);
  427.                 sb.Append('.');
  428.                 sb.Append(culture.Name);
  429.             }
  430.             sb.Append(ResFileExtension);
  431.             return sb.ToString();
  432.         }
  433.        
  434.         // Looks up a set of resources for a particular CultureInfo. This is
  435.         // not useful for most users of the ResourceManager - call
  436.         // GetString() or GetObject() instead.
  437.         //
  438.         // The parameters let you control whether the ResourceSet is created
  439.         // if it hasn't yet been loaded and if parent CultureInfos should be
  440.         // loaded as well for resource inheritance.
  441.         //
  442.         public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
  443.         {
  444.             if (null == culture)
  445.                 throw new ArgumentNullException("culture");
  446.            
  447.            
  448.             Hashtable localResourceSets = ResourceSets;
  449.             ResourceSet rs;
  450.             if (localResourceSets != null) {
  451.                 rs = (ResourceSet)localResourceSets[culture];
  452.                 if (null != rs)
  453.                     return rs;
  454.             }
  455.            
  456.             if (UseManifest && culture.Equals(CultureInfo.InvariantCulture)) {
  457.                 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  458.                 string fileName = GetResourceFileName(culture);
  459.                 Stream stream = MainAssembly.GetManifestResourceStream(_locationInfo, fileName, _callingAssembly == MainAssembly, ref stackMark);
  460.                 if (createIfNotExists && stream != null) {
  461.                     rs = CreateResourceSet(stream, MainAssembly);
  462.                     lock (localResourceSets) {
  463.                         localResourceSets.Add(culture, rs);
  464.                     }
  465.                     return rs;
  466.                 }
  467.             }
  468.            
  469.             return InternalGetResourceSet(culture, createIfNotExists, tryParents);
  470.         }
  471.        
  472.         // InternalGetResourceSet is a non-threadsafe method where all the logic
  473.         // for getting a resource set lives. Access to it is controlled by
  474.         // threadsafe methods such as GetResourceSet, GetString, & GetObject.
  475.         // This will take a minimal number of locks.
  476.         [ResourceExposure(ResourceScope.None)]
  477.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  478.         protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
  479.         {
  480.             BCLDebug.Assert(culture != null, "culture != null");
  481.            
  482.             #if _DEBUG
  483.             if (DEBUG >= 3)
  484.                 BCLDebug.Log("GetResourceSet(" + culture.Name + " [" + culture.LCID + "], " + createIfNotExists + ", " + tryParents + ")");
  485.             #endif
  486.            
  487.             Hashtable localResourceSets = ResourceSets;
  488.             ResourceSet rs = (ResourceSet)localResourceSets[culture];
  489.             if (null != rs)
  490.                 return rs;
  491.            
  492.             // InternalGetResourceSet will never be threadsafe. However, it must
  493.             // be protected against reentrancy from the SAME THREAD. (ie, calling
  494.             // GetSatelliteAssembly may send some window messages or trigger the
  495.             // Assembly load event, which could fail then call back into the
  496.             // ResourceManager). It's happened.
  497.            
  498.             Stream stream = null;
  499.             string fileName = null;
  500.             Assembly satellite = null;
  501.             // Which assembly we loaded from
  502.             if (UseManifest) {
  503.                 fileName = GetResourceFileName(culture);
  504.                 // Ask assembly for resource stream named fileName.
  505.                 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  506.                 if (UseSatelliteAssem) {
  507.                     CultureInfo lookForCulture = culture;
  508.                     if (_neutralResourcesCulture == null)
  509.                         _neutralResourcesCulture = GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
  510.                     // If our neutral resources were written in this culture
  511.                     // AND we know the main assembly does NOT contain neutral
  512.                     // resources, don't probe for this satellite.
  513.                     if (culture.Equals(_neutralResourcesCulture) && FallbackLocation == UltimateResourceFallbackLocation.MainAssembly) {
  514.                         #if _DEBUG
  515.                         if (DEBUG >= 4)
  516.                             BCLDebug.Log("ResMgr::InternalGetResourceSet - Your neutral resources are sufficient for this culture. Skipping satellites & using neutral resources for: " + culture.Name);
  517.                         #endif
  518.                         // Update internal state.
  519.                         lookForCulture = CultureInfo.InvariantCulture;
  520.                         fileName = GetResourceFileName(lookForCulture);
  521.                     }
  522.                    
  523.                     // For neutral locale, look in the main assembly
  524.                     // if and only if our fallback location is MainAssembly.
  525.                     if (lookForCulture.Equals(CultureInfo.InvariantCulture)) {
  526.                         if (FallbackLocation == UltimateResourceFallbackLocation.Satellite) {
  527.                             satellite = GetSatelliteAssembly(_neutralResourcesCulture);
  528.                             // If your ultimate fallback satellite couldn't be
  529.                             // loaded, it's a fatal error. We'll give
  530.                             // you a nice error message.
  531.                             if (satellite == null) {
  532.                                 string satAssemName = MainAssembly.nGetSimpleName() + ".resources.dll";
  533.                                 if (_satelliteContractVersion != null)
  534.                                     satAssemName += ", Version=" + _satelliteContractVersion.ToString();
  535.                                
  536.                                 AssemblyName an = new AssemblyName();
  537.                                 an.SetPublicKey(MainAssembly.nGetPublicKey());
  538.                                 byte[] token = an.GetPublicKeyToken();
  539.                                
  540.                                 int iLen = token.Length;
  541.                                 StringBuilder publicKeyTok = new StringBuilder(iLen * 2);
  542.                                 for (int i = 0; i < iLen; i++)
  543.                                     publicKeyTok.Append(token[i].ToString("x", CultureInfo.InvariantCulture));
  544.                                 satAssemName += ", PublicKeyToken=" + publicKeyTok;
  545.                                 string cultureName = _neutralResourcesCulture.Name;
  546.                                 if (cultureName.Length == 0)
  547.                                     cultureName = "<invariant>";
  548.                                 throw new MissingSatelliteAssemblyException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("MissingSatelliteAssembly_Culture_Name"), _neutralResourcesCulture, satAssemName), cultureName);
  549.                             }
  550.                             // Correct fileName for this satellite.
  551.                             fileName = GetResourceFileName(_neutralResourcesCulture);
  552.                         }
  553.                         else
  554.                             satellite = MainAssembly;
  555.                     }
  556.                     else if (!TryLookingForSatellite(lookForCulture))
  557.                         satellite = null;
  558.                     else
  559.                         // If our config file says the satellite isn't here,
  560.                         // don't ask for it.
  561.                         satellite = GetSatelliteAssembly(lookForCulture);
  562.                    
  563.                    
  564.                     // Look for a possibly-NameSpace-qualified resource file, ie,
  565.                     // Microsoft.Name.myApp.en-US.resources.
  566.                     // (A null _locationInfo is legal)
  567.                     if (satellite != null) {
  568.                         // Handle case in here where someone added a callback
  569.                         // for assembly load events. While no other threads
  570.                         // have called into GetResourceSet, our own thread can!
  571.                         // At that point, we could already have an RS in our
  572.                         // hash table, and we don't want to add it twice.
  573.                         rs = (ResourceSet)localResourceSets[lookForCulture];
  574.                         if (null != rs) {
  575.                             #if _DEBUG
  576.                             if (DEBUG >= 1)
  577.                                 BCLDebug.Log("ResMgr::GetResourceSet - After loading a satellite assembly, we already have the resource set for culture \"" + lookForCulture.Name + "\" [" + lookForCulture.LCID + "]. You must have had an assembly load callback that called into this instance of the ResourceManager! Eeew.");
  578.                             #endif
  579.                             return rs;
  580.                         }
  581.                        
  582.                         // If we're looking in the main assembly AND if the main
  583.                         // assembly was the person who created the ResourceManager,
  584.                         // skip a security check for private manifest resources.
  585.                         bool canSkipSecurityCheck = MainAssembly == satellite && _callingAssembly == MainAssembly;
  586.                         stream = satellite.GetManifestResourceStream(_locationInfo, fileName, canSkipSecurityCheck, ref stackMark);
  587.                         if (stream == null)
  588.                             stream = CaseInsensitiveManifestResourceStreamLookup(satellite, fileName);
  589.                     }
  590.                    
  591.                     #if _DEBUG
  592.                     if (DEBUG >= 3)
  593.                         BCLDebug.Log("ResMgr manifest resource lookup in satellite assembly for culture " + (lookForCulture.Equals(CultureInfo.InvariantCulture) ? "[neutral]" : lookForCulture.Name) + (stream == null ? " failed." : " succeeded.") + " assembly name: " + (satellite == null ? "<null>" : satellite.nGetSimpleName() + " assembly culture: " + satellite.GetLocale().Name + " [" + satellite.GetLocale().LCID + "]"));
  594.                     #endif
  595.                 }
  596.                 else {
  597.                     // if !UseSatelliteAssembly
  598.                     satellite = MainAssembly;
  599.                     stream = MainAssembly.GetManifestResourceStream(_locationInfo, fileName, _callingAssembly == MainAssembly, ref stackMark);
  600.                 }
  601.                
  602.                 if (stream == null && tryParents) {
  603.                     // If we've hit top of the Culture tree, return.
  604.                     if (culture.Equals(CultureInfo.InvariantCulture)) {
  605.                         // Keep people from bothering me about resources problems
  606.                         if (MainAssembly == typeof(object).Assembly && BaseName.Equals("mscorlib")) {
  607.                             // This would break CultureInfo & all our exceptions.
  608.                             BCLDebug.Assert(false, "Couldn't get mscorlib" + ResFileExtension + " from mscorlib's assembly" + Environment.NewLine + Environment.NewLine + "Are you building the runtime on your machine? Chances are the BCL directory didn't build correctly. Type 'build -c' in the BCL directory. If you get build errors, look at buildd.log. If you then can't figure out what's wrong (and you aren't changing the assembly-related metadata code), ask a BCL dev.\n\nIf you did NOT build the runtime, you shouldn't be seeing this and you've found a bug.");
  609.                             throw new ExecutionEngineException("mscorlib" + ResFileExtension + " couldn't be found! Large parts of the BCL won't work!");
  610.                         }
  611.                         // We really don't think this should happen - we always
  612.                         // expect the neutral locale's resources to be present.
  613.                         string resName = String.Empty;
  614.                         if (_locationInfo != null && _locationInfo.Namespace != null)
  615.                             resName = _locationInfo.Namespace + Type.Delimiter;
  616.                         resName += fileName;
  617.                         throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoNeutralAsm", resName, MainAssembly.nGetSimpleName()));
  618.                     }
  619.                    
  620.                     CultureInfo parent = culture.Parent;
  621.                    
  622.                     // Recurse now.
  623.                     rs = InternalGetResourceSet(parent, createIfNotExists, tryParents);
  624.                     if (rs != null) {
  625.                         AddResourceSet(localResourceSets, culture, ref rs);
  626.                     }
  627.                     return rs;
  628.                 }
  629.             }
  630.             //UseManifest
  631.             else {
  632.                 // Don't use Assembly manifest, but grovel on disk for a file.
  633.                 new System.Security.Permissions.FileIOPermission(System.Security.Permissions.PermissionState.Unrestricted).Assert();
  634.                
  635.                 // Create new ResourceSet, if a file exists on disk for it.
  636.                 fileName = FindResourceFile(culture);
  637.                 if (fileName == null) {
  638.                     if (tryParents) {
  639.                         // If we've hit top of the Culture tree, return.
  640.                         if (culture.Equals(CultureInfo.InvariantCulture)) {
  641.                             // We really don't think this should happen - we always
  642.                             // expect the neutral locale's resources to be present.
  643.                            
  644.                             throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoNeutralDisk") + Environment.NewLine + "baseName: " + BaseNameField + " locationInfo: " + (_locationInfo == null ? "<null>" : _locationInfo.FullName) + " fileName: " + GetResourceFileName(culture));
  645.                         }
  646.                        
  647.                         CultureInfo parent = culture.Parent;
  648.                        
  649.                         // Recurse now.
  650.                         rs = InternalGetResourceSet(parent, createIfNotExists, tryParents);
  651.                         if (rs != null) {
  652.                             AddResourceSet(localResourceSets, culture, ref rs);
  653.                         }
  654.                         return rs;
  655.                     }
  656.                 }
  657.                 else {
  658.                     rs = CreateResourceSet(fileName);
  659.                    
  660.                     // To speed up ResourceSet lookups in the future, store this
  661.                     // culture with its parent culture's ResourceSet.
  662.                     if (rs != null) {
  663.                         AddResourceSet(localResourceSets, culture, ref rs);
  664.                     }
  665.                     return rs;
  666.                 }
  667.             }
  668.            
  669.             if (createIfNotExists && stream != null && rs == null) {
  670.                 #if _DEBUG
  671.                 if (DEBUG >= 1)
  672.                     BCLDebug.Log("Creating new ResourceSet for culture " + ((culture == CultureInfo.InvariantCulture) ? "[neutral]" : culture.ToString()) + " from file " + fileName);
  673.                 #endif
  674.                 rs = CreateResourceSet(stream, satellite);
  675.                 AddResourceSet(localResourceSets, culture, ref rs);
  676.             }
  677.             #if _DEBUG
  678.             else {
  679.                 // Just for debugging - include debug spew saying we won't do anything.
  680.                 if (!createIfNotExists && stream != null && rs == null) {
  681.                    
  682.                     if (DEBUG >= 1)
  683.                         BCLDebug.Log("NOT creating new ResourceSet because createIfNotExists was false! For culture " + ((culture == CultureInfo.InvariantCulture) ? "[neutral]" : culture.ToString()) + " from file " + fileName);
  684.                 }
  685.             }
  686.             #endif
  687.             return rs;
  688.         }
  689.        
  690.         // Simple helper to ease maintenance and improve readability.
  691.         private static void AddResourceSet(Hashtable localResourceSets, CultureInfo culture, ref ResourceSet rs)
  692.         {
  693.             // InternalGetResourceSet is both recursive and reentrant -
  694.             // assembly load callbacks in particular are a way we can call
  695.             // back into the ResourceManager in unexpectedly on the same thread.
  696.             lock (localResourceSets) {
  697.                 // If another thread added this culture, return that.
  698.                 ResourceSet lostRace = (ResourceSet)localResourceSets[culture];
  699.                 if (lostRace != null) {
  700.                     if (!Object.Equals(lostRace, rs)) {
  701.                         rs.Dispose();
  702.                         rs = lostRace;
  703.                     }
  704.                 }
  705.                 else {
  706.                     localResourceSets.Add(culture, rs);
  707.                 }
  708.             }
  709.         }
  710.        
  711.         // Looks up a .resources file in the assembly manifest using
  712.         // case-insensitive lookup rules. Yes, this is slow. The metadata
  713.         // dev lead refuses to make all manifest blob lookups case-insensitive,
  714.         // even optionally case-insensitive.
  715.         private Stream CaseInsensitiveManifestResourceStreamLookup(Assembly satellite, string name)
  716.         {
  717.             StringBuilder sb = new StringBuilder();
  718.             if (_locationInfo != null) {
  719.                 string nameSpace = _locationInfo.Namespace;
  720.                 if (nameSpace != null) {
  721.                     sb.Append(nameSpace);
  722.                     if (name != null)
  723.                         sb.Append(Type.Delimiter);
  724.                 }
  725.             }
  726.             sb.Append(name);
  727.            
  728.             string givenName = sb.ToString();
  729.             CompareInfo comparer = CultureInfo.InvariantCulture.CompareInfo;
  730.             string canonicalName = null;
  731.             foreach (string existingName in satellite.GetManifestResourceNames()) {
  732.                 if (comparer.Compare(existingName, givenName, CompareOptions.IgnoreCase) == 0) {
  733.                     if (canonicalName == null)
  734.                         canonicalName = existingName;
  735.                     else
  736.                         throw new MissingManifestResourceException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("MissingManifestResource_MultipleBlobs"), givenName, satellite.ToString()));
  737.                 }
  738.             }
  739.            
  740.             #if _DEBUG
  741.             if (DEBUG >= 4) {
  742.                 if