summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Resources
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/mscorlib/src/System/Resources
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/mscorlib/src/System/Resources')
-rw-r--r--src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs175
-rw-r--r--src/mscorlib/src/System/Resources/IResourceGroveler.cs32
-rw-r--r--src/mscorlib/src/System/Resources/IResourceReader.cs31
-rw-r--r--src/mscorlib/src/System/Resources/IResourceWriter.cs45
-rw-r--r--src/mscorlib/src/System/Resources/LooselyLinkedResourceReference.cs86
-rw-r--r--src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs677
-rw-r--r--src/mscorlib/src/System/Resources/MissingManifestResourceException.cs42
-rw-r--r--src/mscorlib/src/System/Resources/MissingSatelliteAssemblyException.cs56
-rw-r--r--src/mscorlib/src/System/Resources/NeutralResourcesLanguageAttribute.cs65
-rw-r--r--src/mscorlib/src/System/Resources/ResourceFallbackManager.cs278
-rw-r--r--src/mscorlib/src/System/Resources/ResourceManager.cs1679
-rw-r--r--src/mscorlib/src/System/Resources/ResourceReader.cs1424
-rw-r--r--src/mscorlib/src/System/Resources/ResourceSet.cs332
-rw-r--r--src/mscorlib/src/System/Resources/ResourceTypeCode.cs57
-rw-r--r--src/mscorlib/src/System/Resources/RuntimeResourceSet.cs435
-rw-r--r--src/mscorlib/src/System/Resources/SatelliteContractVersionAttribute.cs39
-rw-r--r--src/mscorlib/src/System/Resources/UltimateResourceFallbackLocation.cs29
-rw-r--r--src/mscorlib/src/System/Resources/__FastResourceComparer.cs141
-rw-r--r--src/mscorlib/src/System/Resources/__HResults.cs23
19 files changed, 5646 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs b/src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs
new file mode 100644
index 0000000000..efd949f565
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs
@@ -0,0 +1,175 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Searches for resources on disk, used for file-
+** based resource lookup.
+**
+**
+===========================================================*/
+namespace System.Resources {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Globalization;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.Versioning;
+ using System.Text;
+ using System.Threading;
+ using System.Diagnostics.Contracts;
+
+ internal class FileBasedResourceGroveler : IResourceGroveler
+ {
+ private ResourceManager.ResourceManagerMediator _mediator;
+
+ public FileBasedResourceGroveler(ResourceManager.ResourceManagerMediator mediator)
+ {
+ Contract.Assert(mediator != null, "mediator shouldn't be null; check caller");
+ _mediator = mediator;
+ }
+
+ // Consider modifying IResourceGroveler interface (hence this method signature) when we figure out
+ // serialization compat story for moving ResourceManager members to either file-based or
+ // manifest-based classes. Want to continue tightening the design to get rid of unused params.
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public ResourceSet GrovelForResourceSet(CultureInfo culture, Dictionary<String, ResourceSet> localResourceSets, bool tryParents, bool createIfNotExists, ref StackCrawlMark stackMark)
+ {
+ Contract.Assert(culture != null, "culture shouldn't be null; check caller");
+
+ String fileName = null;
+ ResourceSet rs = null;
+
+ // Don't use Assembly manifest, but grovel on disk for a file.
+ try
+ {
+ new System.Security.Permissions.FileIOPermission(System.Security.Permissions.PermissionState.Unrestricted).Assert();
+
+ // Create new ResourceSet, if a file exists on disk for it.
+ String tempFileName = _mediator.GetResourceFileName(culture);
+ fileName = FindResourceFile(culture, tempFileName);
+ if (fileName == null)
+ {
+ if (tryParents)
+ {
+ // If we've hit top of the Culture tree, return.
+ if (culture.HasInvariantCultureName)
+ {
+ // We really don't think this should happen - we always
+ // expect the neutral locale's resources to be present.
+ throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoNeutralDisk") + Environment.NewLine + "baseName: " + _mediator.BaseNameField + " locationInfo: " + (_mediator.LocationInfo == null ? "<null>" : _mediator.LocationInfo.FullName) + " fileName: " + _mediator.GetResourceFileName(culture));
+ }
+ }
+ }
+ else
+ {
+ rs = CreateResourceSet(fileName);
+ }
+ return rs;
+ }
+ finally
+ {
+ System.Security.CodeAccessPermission.RevertAssert();
+ }
+ }
+
+#if !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile event providers for coreclr
+ public bool HasNeutralResources(CultureInfo culture, String defaultResName)
+ {
+ // Detect missing neutral locale resources.
+ String defaultResPath = FindResourceFile(culture, defaultResName);
+ if (defaultResPath == null || !File.Exists(defaultResPath))
+ {
+ String dir = _mediator.ModuleDir;
+ if (defaultResPath != null)
+ {
+ dir = Path.GetDirectoryName(defaultResPath);
+ }
+ return false;
+ }
+ return true;
+ }
+#endif
+
+ // Given a CultureInfo, it generates the path &; file name for
+ // the .resources file for that CultureInfo. This method will grovel
+ // the disk looking for the correct file name & path. Uses CultureInfo's
+ // Name property. If the module directory was set in the ResourceManager
+ // constructor, we'll look there first. If it couldn't be found in the module
+ // diretory or the module dir wasn't provided, look in the current
+ // directory.
+
+ private String FindResourceFile(CultureInfo culture, String fileName)
+ {
+ Contract.Assert(culture != null, "culture shouldn't be null; check caller");
+ Contract.Assert(fileName != null, "fileName shouldn't be null; check caller");
+
+ // If we have a moduleDir, check there first. Get module fully
+ // qualified name, append path to that.
+ if (_mediator.ModuleDir != null)
+ {
+#if _DEBUG
+ if (ResourceManager.DEBUG >= 3)
+ BCLDebug.Log("FindResourceFile: checking module dir: \""+_mediator.ModuleDir+'\"');
+#endif
+
+ String path = Path.Combine(_mediator.ModuleDir, fileName);
+ if (File.Exists(path))
+ {
+#if _DEBUG
+ if (ResourceManager.DEBUG >= 3)
+ BCLDebug.Log("Found resource file in module dir! "+path);
+#endif
+ return path;
+ }
+ }
+
+#if _DEBUG
+ if (ResourceManager.DEBUG >= 3)
+ BCLDebug.Log("Couldn't find resource file in module dir, checking .\\"+fileName);
+#endif
+
+ // look in .
+ if (File.Exists(fileName))
+ return fileName;
+
+ return null; // give up.
+ }
+
+ // Constructs a new ResourceSet for a given file name. The logic in
+ // here avoids a ReflectionPermission check for our RuntimeResourceSet
+ // for perf and working set reasons.
+ [System.Security.SecurityCritical]
+ private ResourceSet CreateResourceSet(String file)
+ {
+ Contract.Assert(file != null, "file shouldn't be null; check caller");
+
+ if (_mediator.UserResourceSet == null)
+ {
+ // Explicitly avoid CreateInstance if possible, because it
+ // requires ReflectionPermission to call private & protected
+ // constructors.
+ return new RuntimeResourceSet(file);
+ }
+ else
+ {
+ Object[] args = new Object[1];
+ args[0] = file;
+ try
+ {
+ return (ResourceSet)Activator.CreateInstance(_mediator.UserResourceSet, args);
+ }
+ catch (MissingMethodException e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResMgrBadResSet_Type", _mediator.UserResourceSet.AssemblyQualifiedName), e);
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/IResourceGroveler.cs b/src/mscorlib/src/System/Resources/IResourceGroveler.cs
new file mode 100644
index 0000000000..983fd1204c
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/IResourceGroveler.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Interface for resource grovelers
+**
+**
+===========================================================*/
+namespace System.Resources {
+ using System;
+ using System.Globalization;
+ using System.Threading;
+ using System.Collections.Generic;
+ using System.Runtime.Versioning;
+
+ internal interface IResourceGroveler
+ {
+ ResourceSet GrovelForResourceSet(CultureInfo culture, Dictionary<String, ResourceSet> localResourceSets, bool tryParents,
+ bool createIfNotExists, ref StackCrawlMark stackMark);
+
+#if !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile event providers for coreclr
+
+ bool HasNeutralResources(CultureInfo culture, String defaultResName);
+#endif
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/IResourceReader.cs b/src/mscorlib/src/System/Resources/IResourceReader.cs
new file mode 100644
index 0000000000..7ab25ec1a0
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/IResourceReader.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Abstraction to read streams of resources.
+**
+**
+===========================================================*/
+namespace System.Resources {
+ using System;
+ using System.IO;
+ using System.Collections;
+
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IResourceReader : IEnumerable, IDisposable
+ {
+ // Interface does not need to be marked with the serializable attribute
+ // Closes the ResourceReader, releasing any resources associated with it.
+ // This could close a network connection, a file, or do nothing.
+ void Close();
+
+
+ new IDictionaryEnumerator GetEnumerator();
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/IResourceWriter.cs b/src/mscorlib/src/System/Resources/IResourceWriter.cs
new file mode 100644
index 0000000000..ae41b84cd7
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/IResourceWriter.cs
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Default way to write strings to a COM+ resource
+** file.
+**
+**
+===========================================================*/
+namespace System.Resources {
+ using System;
+ using System.IO;
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public interface IResourceWriter : IDisposable
+ {
+ // Interface does not need to be marked with the serializable attribute
+ // Adds a string resource to the list of resources to be written to a file.
+ // They aren't written until WriteFile() is called.
+ //
+ void AddResource(String name, String value);
+
+ // Adds a resource to the list of resources to be written to a file.
+ // They aren't written until WriteFile() is called.
+ //
+ void AddResource(String name, Object value);
+
+ // Adds a named byte array as a resource to the list of resources to
+ // be written to a file. They aren't written until WriteFile() is called.
+ //
+ void AddResource(String name, byte[] value);
+
+ // Closes the underlying resource file.
+ void Close();
+
+ // After calling AddResource, this writes all resources to the output
+ // stream. This does NOT close the output stream.
+ void Generate();
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/LooselyLinkedResourceReference.cs b/src/mscorlib/src/System/Resources/LooselyLinkedResourceReference.cs
new file mode 100644
index 0000000000..8235d608be
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/LooselyLinkedResourceReference.cs
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Provides a localizable way of retrieving a file
+** that is linked into your assembly and/or satellite assembly
+** while also leaving the file on disk for unmanaged tools.
+**
+**
+===========================================================*/
+
+// Removing LooselyLinkedResourceReference from Whidbey. We don't
+// yet have any strong customer need for it yet.
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+
+namespace System.Resources {
+ using System.Reflection;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Runtime.InteropServices;
+ using System.Globalization;
+
+ [Serializable]
+
+[System.Runtime.InteropServices.ComVisible(true)]
+ public struct LooselyLinkedResourceReference {
+ private String _manifestResourceName;
+ private String _typeName;
+
+ public LooselyLinkedResourceReference(String looselyLinkedResourceName, String typeName)
+ {
+ if (looselyLinkedResourceName == null)
+ throw new ArgumentNullException("looselyLinkedResourceName");
+ if (typeName == null)
+ throw new ArgumentNullException("typeName");
+ if (looselyLinkedResourceName.Length == 0)
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "looselyLinkedResourceName");
+ if (typeName.Length == 0)
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "typeName");
+ Contract.EndContractBlock();
+
+ _manifestResourceName = looselyLinkedResourceName;
+ _typeName = typeName;
+ }
+
+ public String LooselyLinkedResourceName {
+ get { return _manifestResourceName; }
+ }
+
+ public String TypeName {
+ get { return _typeName; }
+ }
+
+ public Object Resolve(Assembly assembly)
+ {
+ if (assembly == null)
+ throw new ArgumentNullException("assembly");
+ Contract.EndContractBlock();
+
+ Stream data = assembly.GetManifestResourceStream(_manifestResourceName);
+ if (data == null)
+ throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_LooselyLinked", _manifestResourceName, assembly.FullName));
+
+ Type type = Type.GetType(_typeName, true);
+
+ Object obj = Activator.CreateInstance(type, new Object[] { data });
+ return obj;
+ }
+
+ // For good debugging with tools like ResView
+ public override String ToString()
+ {
+ // This is for debugging only. Since we use the property names,
+ // this does not need to be localized.
+ return "LooselyLinkedResourceName = \""+ _manifestResourceName +"\", TypeName = \"" + _typeName + "\"";
+ }
+ }
+}
+
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
diff --git a/src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs b/src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs
new file mode 100644
index 0000000000..5fd0daad09
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs
@@ -0,0 +1,677 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Searches for resources in Assembly manifest, used
+** for assembly-based resource lookup.
+**
+**
+===========================================================*/
+namespace System.Resources {
+
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Reflection;
+ using System.Runtime.InteropServices;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.Versioning;
+ using System.Text;
+ using System.Threading;
+ using System.Diagnostics.Contracts;
+ using Microsoft.Win32;
+
+#if !FEATURE_CORECLR
+ using System.Diagnostics.Tracing;
+#endif
+
+ //
+ // Note: this type is integral to the construction of exception objects,
+ // and sometimes this has to be done in low memory situtations (OOM) or
+ // to create TypeInitializationExceptions due to failure of a static class
+ // constructor. This type needs to be extremely careful and assume that
+ // any type it references may have previously failed to construct, so statics
+ // belonging to that type may not be initialized. FrameworkEventSource.Log
+ // is one such example.
+ //
+ internal class ManifestBasedResourceGroveler : IResourceGroveler
+ {
+
+ private ResourceManager.ResourceManagerMediator _mediator;
+
+ public ManifestBasedResourceGroveler(ResourceManager.ResourceManagerMediator mediator)
+ {
+ // here and below: convert asserts to preconditions where appropriate when we get
+ // contracts story in place.
+ Contract.Requires(mediator != null, "mediator shouldn't be null; check caller");
+ _mediator = mediator;
+ }
+
+ [System.Security.SecuritySafeCritical]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public ResourceSet GrovelForResourceSet(CultureInfo culture, Dictionary<String, ResourceSet> localResourceSets, bool tryParents, bool createIfNotExists, ref StackCrawlMark stackMark)
+ {
+ Contract.Assert(culture != null, "culture shouldn't be null; check caller");
+ Contract.Assert(localResourceSets != null, "localResourceSets shouldn't be null; check caller");
+
+ ResourceSet rs = null;
+ Stream stream = null;
+ RuntimeAssembly satellite = null;
+
+ // 1. Fixups for ultimate fallbacks
+ CultureInfo lookForCulture = UltimateFallbackFixup(culture);
+
+ // 2. Look for satellite assembly or main assembly, as appropriate
+ if (lookForCulture.HasInvariantCultureName && _mediator.FallbackLoc == UltimateResourceFallbackLocation.MainAssembly)
+ {
+ // don't bother looking in satellites in this case
+ satellite = _mediator.MainAssembly;
+ }
+#if RESOURCE_SATELLITE_CONFIG
+ // If our config file says the satellite isn't here, don't ask for it.
+ else if (!lookForCulture.HasInvariantCultureName && !_mediator.TryLookingForSatellite(lookForCulture))
+ {
+ satellite = null;
+ }
+#endif
+ else
+ {
+ satellite = GetSatelliteAssembly(lookForCulture, ref stackMark);
+
+ if (satellite == null)
+ {
+ bool raiseException = (culture.HasInvariantCultureName && (_mediator.FallbackLoc == UltimateResourceFallbackLocation.Satellite));
+ // didn't find satellite, give error if necessary
+ if (raiseException)
+ {
+ HandleSatelliteMissing();
+ }
+ }
+ }
+
+ // get resource file name we'll search for. Note, be careful if you're moving this statement
+ // around because lookForCulture may be modified from originally requested culture above.
+ String fileName = _mediator.GetResourceFileName(lookForCulture);
+
+ // 3. If we identified an assembly to search; look in manifest resource stream for resource file
+ if (satellite != null)
+ {
+ // Handle case in here where someone added a callback for assembly load events.
+ // While no other threads have called into GetResourceSet, our own thread can!
+ // At that point, we could already have an RS in our hash table, and we don't
+ // want to add it twice.
+ lock (localResourceSets)
+ {
+ if (localResourceSets.TryGetValue(culture.Name, out rs))
+ {
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCacheUnexpected(_mediator.BaseName, _mediator.MainAssembly, culture.Name);
+ }
+#endif
+ }
+ }
+
+ stream = GetManifestResourceStream(satellite, fileName, ref stackMark);
+ }
+
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ if (stream != null)
+ {
+ FrameworkEventSource.Log.ResourceManagerStreamFound(_mediator.BaseName, _mediator.MainAssembly, culture.Name, satellite, fileName);
+ }
+ else
+ {
+ FrameworkEventSource.Log.ResourceManagerStreamNotFound(_mediator.BaseName, _mediator.MainAssembly, culture.Name, satellite, fileName);
+ }
+ }
+#endif
+
+ // 4a. Found a stream; create a ResourceSet if possible
+ if (createIfNotExists && stream != null && rs == null)
+ {
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerCreatingResourceSet(_mediator.BaseName, _mediator.MainAssembly, culture.Name, fileName);
+ }
+#endif
+ rs = CreateResourceSet(stream, satellite);
+ }
+ else if (stream == null && tryParents)
+ {
+ // 4b. Didn't find stream; give error if necessary
+ bool raiseException = culture.HasInvariantCultureName;
+ if (raiseException)
+ {
+ HandleResourceStreamMissing(fileName);
+ }
+ }
+
+#if !FEATURE_CORECLR
+ if (!createIfNotExists && stream != null && rs == null)
+ {
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerNotCreatingResourceSet(_mediator.BaseName, _mediator.MainAssembly, culture.Name);
+ }
+ }
+#endif
+
+ return rs;
+ }
+
+#if !FEATURE_CORECLR
+ // Returns whether or not the main assembly contains a particular resource
+ // file in it's assembly manifest. Used to verify that the neutral
+ // Culture's .resources file is present in the main assembly
+ public bool HasNeutralResources(CultureInfo culture, String defaultResName)
+ {
+ String resName = defaultResName;
+ if (_mediator.LocationInfo != null && _mediator.LocationInfo.Namespace != null)
+ resName = _mediator.LocationInfo.Namespace + Type.Delimiter + defaultResName;
+ String[] resourceFiles = _mediator.MainAssembly.GetManifestResourceNames();
+ foreach(String s in resourceFiles)
+ if (s.Equals(resName))
+ return true;
+ return false;
+ }
+#endif
+
+ private CultureInfo UltimateFallbackFixup(CultureInfo lookForCulture)
+ {
+
+ CultureInfo returnCulture = lookForCulture;
+
+ // If our neutral resources were written in this culture AND we know the main assembly
+ // does NOT contain neutral resources, don't probe for this satellite.
+ if (lookForCulture.Name == _mediator.NeutralResourcesCulture.Name &&
+ _mediator.FallbackLoc == UltimateResourceFallbackLocation.MainAssembly)
+ {
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerNeutralResourcesSufficient(_mediator.BaseName, _mediator.MainAssembly, lookForCulture.Name);
+ }
+#endif
+
+ returnCulture = CultureInfo.InvariantCulture;
+ }
+ else if (lookForCulture.HasInvariantCultureName && _mediator.FallbackLoc == UltimateResourceFallbackLocation.Satellite)
+ {
+ returnCulture = _mediator.NeutralResourcesCulture;
+ }
+
+ return returnCulture;
+
+ }
+
+ [System.Security.SecurityCritical]
+ internal static CultureInfo GetNeutralResourcesLanguage(Assembly a, ref UltimateResourceFallbackLocation fallbackLocation)
+ {
+ Contract.Assert(a != null, "assembly != null");
+ string cultureName = null;
+ short fallback = 0;
+ if (GetNeutralResourcesLanguageAttribute(((RuntimeAssembly)a).GetNativeHandle(),
+ JitHelpers.GetStringHandleOnStack(ref cultureName),
+ out fallback)) {
+ if ((UltimateResourceFallbackLocation)fallback < UltimateResourceFallbackLocation.MainAssembly || (UltimateResourceFallbackLocation)fallback > UltimateResourceFallbackLocation.Satellite) {
+ throw new ArgumentException(Environment.GetResourceString("Arg_InvalidNeutralResourcesLanguage_FallbackLoc", fallback));
+ }
+ fallbackLocation = (UltimateResourceFallbackLocation)fallback;
+ }
+ else {
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized) {
+ FrameworkEventSource.Log.ResourceManagerNeutralResourceAttributeMissing(a);
+ }
+#endif
+ fallbackLocation = UltimateResourceFallbackLocation.MainAssembly;
+ return CultureInfo.InvariantCulture;
+ }
+
+ try
+ {
+ CultureInfo c = CultureInfo.GetCultureInfo(cultureName);
+ return c;
+ }
+ catch (ArgumentException e)
+ { // we should catch ArgumentException only.
+ // Note we could go into infinite loops if mscorlib's
+ // NeutralResourcesLanguageAttribute is mangled. If this assert
+ // fires, please fix the build process for the BCL directory.
+ if (a == typeof(Object).Assembly)
+ {
+ Contract.Assert(false, System.CoreLib.Name+"'s NeutralResourcesLanguageAttribute is a malformed culture name! name: \"" + cultureName + "\" Exception: " + e);
+ return CultureInfo.InvariantCulture;
+ }
+
+ throw new ArgumentException(Environment.GetResourceString("Arg_InvalidNeutralResourcesLanguage_Asm_Culture", a.ToString(), cultureName), e);
+ }
+ }
+
+ // Constructs a new ResourceSet for a given file name. The logic in
+ // here avoids a ReflectionPermission check for our RuntimeResourceSet
+ // for perf and working set reasons.
+ // Use the assembly to resolve assembly manifest resource references.
+ // Note that is can be null, but probably shouldn't be.
+ // This method could use some refactoring. One thing at a time.
+ [System.Security.SecurityCritical]
+ internal ResourceSet CreateResourceSet(Stream store, Assembly assembly)
+ {
+ Contract.Assert(store != null, "I need a Stream!");
+ // Check to see if this is a Stream the ResourceManager understands,
+ // and check for the correct resource reader type.
+ if (store.CanSeek && store.Length > 4)
+ {
+ long startPos = store.Position;
+
+ // not disposing because we want to leave stream open
+ BinaryReader br = new BinaryReader(store);
+
+ // Look for our magic number as a little endian Int32.
+ int bytes = br.ReadInt32();
+ if (bytes == ResourceManager.MagicNumber)
+ {
+ int resMgrHeaderVersion = br.ReadInt32();
+ String readerTypeName = null, resSetTypeName = null;
+ if (resMgrHeaderVersion == ResourceManager.HeaderVersionNumber)
+ {
+ br.ReadInt32(); // We don't want the number of bytes to skip.
+ readerTypeName = System.CoreLib.FixupCoreLibName(br.ReadString());
+ resSetTypeName = System.CoreLib.FixupCoreLibName(br.ReadString());
+ }
+ else if (resMgrHeaderVersion > ResourceManager.HeaderVersionNumber)
+ {
+ // Assume that the future ResourceManager headers will
+ // have two strings for us - the reader type name and
+ // resource set type name. Read those, then use the num
+ // bytes to skip field to correct our position.
+ int numBytesToSkip = br.ReadInt32();
+ long endPosition = br.BaseStream.Position + numBytesToSkip;
+
+ readerTypeName = System.CoreLib.FixupCoreLibName(br.ReadString());
+ resSetTypeName = System.CoreLib.FixupCoreLibName(br.ReadString());
+
+ br.BaseStream.Seek(endPosition, SeekOrigin.Begin);
+ }
+ else
+ {
+ // resMgrHeaderVersion is older than this ResMgr version.
+ // We should add in backwards compatibility support here.
+
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ObsoleteResourcesFile", _mediator.MainAssembly.GetSimpleName()));
+ }
+
+ store.Position = startPos;
+ // Perf optimization - Don't use Reflection for our defaults.
+ // Note there are two different sets of strings here - the
+ // assembly qualified strings emitted by ResourceWriter, and
+ // the abbreviated ones emitted by InternalResGen.
+ if (CanUseDefaultResourceClasses(readerTypeName, resSetTypeName))
+ {
+ RuntimeResourceSet rs;
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ rs = new RuntimeResourceSet(store, assembly);
+#else
+ rs = new RuntimeResourceSet(store);
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+ return rs;
+ }
+ else
+ {
+ // we do not want to use partial binding here.
+ Type readerType = Type.GetType(readerTypeName, true);
+ Object[] args = new Object[1];
+ args[0] = store;
+ IResourceReader reader = (IResourceReader)Activator.CreateInstance(readerType, args);
+
+ Object[] resourceSetArgs =
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ new Object[2];
+#else
+ new Object[1];
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+ resourceSetArgs[0] = reader;
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ resourceSetArgs[1] = assembly;
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+ Type resSetType;
+ if (_mediator.UserResourceSet == null)
+ {
+ Contract.Assert(resSetTypeName != null, "We should have a ResourceSet type name from the custom resource file here.");
+ resSetType = Type.GetType(resSetTypeName, true, false);
+ }
+ else
+ resSetType = _mediator.UserResourceSet;
+ ResourceSet rs = (ResourceSet)Activator.CreateInstance(resSetType,
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance,
+ null,
+ resourceSetArgs,
+ null,
+ null);
+ return rs;
+ }
+ }
+ else
+ {
+ store.Position = startPos;
+ }
+
+ }
+
+ if (_mediator.UserResourceSet == null)
+ {
+ // Explicitly avoid CreateInstance if possible, because it
+ // requires ReflectionPermission to call private & protected
+ // constructors.
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ return new RuntimeResourceSet(store, assembly);
+#else
+ return new RuntimeResourceSet(store);
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+ }
+ else
+ {
+ Object[] args = new Object[2];
+ args[0] = store;
+ args[1] = assembly;
+ try
+ {
+ ResourceSet rs = null;
+ // Add in a check for a constructor taking in an assembly first.
+ try
+ {
+ rs = (ResourceSet)Activator.CreateInstance(_mediator.UserResourceSet, args);
+ return rs;
+ }
+ catch (MissingMethodException) { }
+
+ args = new Object[1];
+ args[0] = store;
+ rs = (ResourceSet)Activator.CreateInstance(_mediator.UserResourceSet, args);
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ rs.Assembly = assembly;
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+ return rs;
+ }
+ catch (MissingMethodException e)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResMgrBadResSet_Type", _mediator.UserResourceSet.AssemblyQualifiedName), e);
+ }
+ }
+ }
+
+ [System.Security.SecurityCritical]
+ private Stream GetManifestResourceStream(RuntimeAssembly satellite, String fileName, ref StackCrawlMark stackMark)
+ {
+ Contract.Requires(satellite != null, "satellite shouldn't be null; check caller");
+ Contract.Requires(fileName != null, "fileName shouldn't be null; check caller");
+
+ // If we're looking in the main assembly AND if the main assembly was the person who
+ // created the ResourceManager, skip a security check for private manifest resources.
+ bool canSkipSecurityCheck = (_mediator.MainAssembly == satellite)
+ && (_mediator.CallingAssembly == _mediator.MainAssembly);
+
+ Stream stream = satellite.GetManifestResourceStream(_mediator.LocationInfo, fileName, canSkipSecurityCheck, ref stackMark);
+ if (stream == null)
+ {
+ stream = CaseInsensitiveManifestResourceStreamLookup(satellite, fileName);
+ }
+
+ return stream;
+ }
+
+ // Looks up a .resources file in the assembly manifest using
+ // case-insensitive lookup rules. Yes, this is slow. The metadata
+ // dev lead refuses to make all assembly manifest resource lookups case-insensitive,
+ // even optionally case-insensitive.
+ [System.Security.SecurityCritical]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ private Stream CaseInsensitiveManifestResourceStreamLookup(RuntimeAssembly satellite, String name)
+ {
+ Contract.Requires(satellite != null, "satellite shouldn't be null; check caller");
+ Contract.Requires(name != null, "name shouldn't be null; check caller");
+
+ StringBuilder sb = new StringBuilder();
+ if (_mediator.LocationInfo != null)
+ {
+ String nameSpace = _mediator.LocationInfo.Namespace;
+ if (nameSpace != null)
+ {
+ sb.Append(nameSpace);
+ if (name != null)
+ sb.Append(Type.Delimiter);
+ }
+ }
+ sb.Append(name);
+
+ String givenName = sb.ToString();
+ CompareInfo comparer = CultureInfo.InvariantCulture.CompareInfo;
+ String canonicalName = null;
+ foreach (String existingName in satellite.GetManifestResourceNames())
+ {
+ if (comparer.Compare(existingName, givenName, CompareOptions.IgnoreCase) == 0)
+ {
+ if (canonicalName == null)
+ {
+ canonicalName = existingName;
+ }
+ else
+ {
+ throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_MultipleBlobs", givenName, satellite.ToString()));
+ }
+ }
+ }
+
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ if (canonicalName != null)
+ {
+ FrameworkEventSource.Log.ResourceManagerCaseInsensitiveResourceStreamLookupSucceeded(_mediator.BaseName, _mediator.MainAssembly, satellite.GetSimpleName(), givenName);
+ }
+ else
+ {
+ FrameworkEventSource.Log.ResourceManagerCaseInsensitiveResourceStreamLookupFailed(_mediator.BaseName, _mediator.MainAssembly, satellite.GetSimpleName(), givenName);
+ }
+ }
+#endif
+
+ if (canonicalName == null)
+ {
+ return null;
+ }
+ // If we're looking in the main assembly AND if the main
+ // assembly was the person who created the ResourceManager,
+ // skip a security check for private manifest resources.
+ bool canSkipSecurityCheck = _mediator.MainAssembly == satellite && _mediator.CallingAssembly == _mediator.MainAssembly;
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ Stream s = satellite.GetManifestResourceStream(canonicalName, ref stackMark, canSkipSecurityCheck);
+ // GetManifestResourceStream will return null if we don't have
+ // permission to read this stream from the assembly. For example,
+ // if the stream is private and we're trying to access it from another
+ // assembly (ie, ResMgr in mscorlib accessing anything else), we
+ // require Reflection TypeInformation permission to be able to read
+ // this.
+#if !FEATURE_CORECLR
+ if (s!=null) {
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerManifestResourceAccessDenied(_mediator.BaseName, _mediator.MainAssembly, satellite.GetSimpleName(), canonicalName);
+ }
+ }
+#endif
+ return s;
+ }
+
+ [System.Security.SecurityCritical]
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ private RuntimeAssembly GetSatelliteAssembly(CultureInfo lookForCulture, ref StackCrawlMark stackMark)
+ {
+ if (!_mediator.LookedForSatelliteContractVersion)
+ {
+ _mediator.SatelliteContractVersion = _mediator.ObtainSatelliteContractVersion(_mediator.MainAssembly);
+ _mediator.LookedForSatelliteContractVersion = true;
+ }
+
+ RuntimeAssembly satellite = null;
+ String satAssemblyName = GetSatelliteAssemblyName();
+
+ // Look up the satellite assembly, but don't let problems
+ // like a partially signed satellite assembly stop us from
+ // doing fallback and displaying something to the user.
+ // Yet also somehow log this error for a developer.
+ try
+ {
+ satellite = _mediator.MainAssembly.InternalGetSatelliteAssembly(satAssemblyName, lookForCulture, _mediator.SatelliteContractVersion, false, ref stackMark);
+ }
+
+ // Jun 08: for cases other than ACCESS_DENIED, we'll assert instead of throw to give release builds more opportunity to fallback.
+
+ catch (FileLoadException fle)
+ {
+ // Ignore cases where the loader gets an access
+ // denied back from the OS. This showed up for
+ // href-run exe's at one point.
+ int hr = fle._HResult;
+ if (hr != Win32Native.MakeHRFromErrorCode(Win32Native.ERROR_ACCESS_DENIED))
+ {
+ Contract.Assert(false, "[This assert catches satellite assembly build/deployment problems - report this message to your build lab & loc engineer]" + Environment.NewLine + "GetSatelliteAssembly failed for culture " + lookForCulture.Name + " and version " + (_mediator.SatelliteContractVersion == null ? _mediator.MainAssembly.GetVersion().ToString() : _mediator.SatelliteContractVersion.ToString()) + " of assembly " + _mediator.MainAssembly.GetSimpleName() + " with error code 0x" + hr.ToString("X", CultureInfo.InvariantCulture) + Environment.NewLine + "Exception: " + fle);
+ }
+ }
+
+ // Don't throw for zero-length satellite assemblies, for compat with v1
+ catch (BadImageFormatException bife)
+ {
+ Contract.Assert(false, "[This assert catches satellite assembly build/deployment problems - report this message to your build lab & loc engineer]" + Environment.NewLine + "GetSatelliteAssembly failed for culture " + lookForCulture.Name + " and version " + (_mediator.SatelliteContractVersion == null ? _mediator.MainAssembly.GetVersion().ToString() : _mediator.SatelliteContractVersion.ToString()) + " of assembly " + _mediator.MainAssembly.GetSimpleName() + Environment.NewLine + "Exception: " + bife);
+ }
+
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ if (satellite != null)
+ {
+ FrameworkEventSource.Log.ResourceManagerGetSatelliteAssemblySucceeded(_mediator.BaseName, _mediator.MainAssembly, lookForCulture.Name, satAssemblyName);
+ }
+ else
+ {
+ FrameworkEventSource.Log.ResourceManagerGetSatelliteAssemblyFailed(_mediator.BaseName, _mediator.MainAssembly, lookForCulture.Name, satAssemblyName);
+ }
+ }
+#endif
+
+ return satellite;
+ }
+
+ // Perf optimization - Don't use Reflection for most cases with
+ // our .resources files. This makes our code run faster and we can
+ // creating a ResourceReader via Reflection. This would incur
+ // a security check (since the link-time check on the constructor that
+ // takes a String is turned into a full demand with a stack walk)
+ // and causes partially trusted localized apps to fail.
+ private bool CanUseDefaultResourceClasses(String readerTypeName, String resSetTypeName)
+ {
+ Contract.Assert(readerTypeName != null, "readerTypeName shouldn't be null; check caller");
+ Contract.Assert(resSetTypeName != null, "resSetTypeName shouldn't be null; check caller");
+
+ if (_mediator.UserResourceSet != null)
+ return false;
+
+ // Ignore the actual version of the ResourceReader and
+ // RuntimeResourceSet classes. Let those classes deal with
+ // versioning themselves.
+ AssemblyName mscorlib = new AssemblyName(ResourceManager.MscorlibName);
+
+ if (readerTypeName != null)
+ {
+ if (!ResourceManager.CompareNames(readerTypeName, ResourceManager.ResReaderTypeName, mscorlib))
+ return false;
+ }
+
+ if (resSetTypeName != null)
+ {
+ if (!ResourceManager.CompareNames(resSetTypeName, ResourceManager.ResSetTypeName, mscorlib))
+ return false;
+ }
+
+ return true;
+ }
+
+ [System.Security.SecurityCritical]
+ private String GetSatelliteAssemblyName()
+ {
+ String satAssemblyName = _mediator.MainAssembly.GetSimpleName();
+ satAssemblyName += ".resources";
+ return satAssemblyName;
+ }
+
+ [System.Security.SecurityCritical]
+ private void HandleSatelliteMissing()
+ {
+ String satAssemName = _mediator.MainAssembly.GetSimpleName() + ".resources.dll";
+ if (_mediator.SatelliteContractVersion != null)
+ {
+ satAssemName += ", Version=" + _mediator.SatelliteContractVersion.ToString();
+ }
+
+ AssemblyName an = new AssemblyName();
+ an.SetPublicKey(_mediator.MainAssembly.GetPublicKey());
+ byte[] token = an.GetPublicKeyToken();
+
+ int iLen = token.Length;
+ StringBuilder publicKeyTok = new StringBuilder(iLen * 2);
+ for (int i = 0; i < iLen; i++)
+ {
+ publicKeyTok.Append(token[i].ToString("x", CultureInfo.InvariantCulture));
+ }
+ satAssemName += ", PublicKeyToken=" + publicKeyTok;
+
+ String missingCultureName = _mediator.NeutralResourcesCulture.Name;
+ if (missingCultureName.Length == 0)
+ {
+ missingCultureName = "<invariant>";
+ }
+ throw new MissingSatelliteAssemblyException(Environment.GetResourceString("MissingSatelliteAssembly_Culture_Name", _mediator.NeutralResourcesCulture, satAssemName), missingCultureName);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private void HandleResourceStreamMissing(String fileName)
+ {
+ // Keep people from bothering me about resources problems
+ if (_mediator.MainAssembly == typeof(Object).Assembly && _mediator.BaseName.Equals(System.CoreLib.Name))
+ {
+ // This would break CultureInfo & all our exceptions.
+ Contract.Assert(false, "Couldn't get " + System.CoreLib.Name+ResourceManager.ResFileExtension + " from "+System.CoreLib.Name+"'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.");
+
+ // We cannot continue further - simply FailFast.
+ string mesgFailFast = System.CoreLib.Name + ResourceManager.ResFileExtension + " couldn't be found! Large parts of the BCL won't work!";
+ System.Environment.FailFast(mesgFailFast);
+ }
+ // We really don't think this should happen - we always
+ // expect the neutral locale's resources to be present.
+ String resName = String.Empty;
+ if (_mediator.LocationInfo != null && _mediator.LocationInfo.Namespace != null)
+ resName = _mediator.LocationInfo.Namespace + Type.Delimiter;
+ resName += fileName;
+ throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoNeutralAsm", resName, _mediator.MainAssembly.GetSimpleName()));
+ }
+
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [System.Security.SecurityCritical] // Our security team doesn't yet allow safe-critical P/Invoke methods.
+ [System.Security.SuppressUnmanagedCodeSecurity]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool GetNeutralResourcesLanguageAttribute(RuntimeAssembly assemblyHandle, StringHandleOnStack cultureName, out short fallbackLocation);
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/MissingManifestResourceException.cs b/src/mscorlib/src/System/Resources/MissingManifestResourceException.cs
new file mode 100644
index 0000000000..e616bfe68b
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/MissingManifestResourceException.cs
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Exception for a missing assembly-level resource
+**
+**
+===========================================================*/
+
+using System;
+using System.Runtime.Serialization;
+
+namespace System.Resources {
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public class MissingManifestResourceException : SystemException
+ {
+ public MissingManifestResourceException()
+ : base(Environment.GetResourceString("Arg_MissingManifestResourceException")) {
+ SetErrorCode(System.__HResults.COR_E_MISSINGMANIFESTRESOURCE);
+ }
+
+ public MissingManifestResourceException(String message)
+ : base(message) {
+ SetErrorCode(System.__HResults.COR_E_MISSINGMANIFESTRESOURCE);
+ }
+
+ public MissingManifestResourceException(String message, Exception inner)
+ : base(message, inner) {
+ SetErrorCode(System.__HResults.COR_E_MISSINGMANIFESTRESOURCE);
+ }
+
+ protected MissingManifestResourceException(SerializationInfo info, StreamingContext context) : base (info, context) {
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/MissingSatelliteAssemblyException.cs b/src/mscorlib/src/System/Resources/MissingSatelliteAssemblyException.cs
new file mode 100644
index 0000000000..3d59f856b1
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/MissingSatelliteAssemblyException.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Exception for a missing satellite assembly needed
+** for ultimate resource fallback. This usually
+** indicates a setup and/or deployment problem.
+**
+**
+===========================================================*/
+
+using System;
+using System.Runtime.Serialization;
+
+namespace System.Resources {
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public class MissingSatelliteAssemblyException : SystemException
+ {
+ private String _cultureName;
+
+ public MissingSatelliteAssemblyException()
+ : base(Environment.GetResourceString("MissingSatelliteAssembly_Default")) {
+ SetErrorCode(System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY);
+ }
+
+ public MissingSatelliteAssemblyException(String message)
+ : base(message) {
+ SetErrorCode(System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY);
+ }
+
+ public MissingSatelliteAssemblyException(String message, String cultureName)
+ : base(message) {
+ SetErrorCode(System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY);
+ _cultureName = cultureName;
+ }
+
+ public MissingSatelliteAssemblyException(String message, Exception inner)
+ : base(message, inner) {
+ SetErrorCode(System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY);
+ }
+
+ protected MissingSatelliteAssemblyException(SerializationInfo info, StreamingContext context) : base (info, context) {
+ }
+
+ public String CultureName {
+ get { return _cultureName; }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/NeutralResourcesLanguageAttribute.cs b/src/mscorlib/src/System/Resources/NeutralResourcesLanguageAttribute.cs
new file mode 100644
index 0000000000..560cd5faa9
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/NeutralResourcesLanguageAttribute.cs
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Tells the ResourceManager what language your main
+** assembly's resources are written in. The
+** ResourceManager won't try loading a satellite
+** assembly for that culture, which helps perf.
+**
+**
+** NOTE:
+**
+** This custom attribute is no longer implemented in managed code. As part of a perf optimization,
+** it is now read in Module::GetNeutralResourcesLanguage, accessed from ManifestBasedResourceGroveler
+** through an internal runtime call.
+===========================================================*/
+
+namespace System.Resources {
+ using System;
+ using System.Diagnostics.Contracts;
+
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false)]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class NeutralResourcesLanguageAttribute : Attribute
+ {
+ private String _culture;
+ private UltimateResourceFallbackLocation _fallbackLoc;
+
+ public NeutralResourcesLanguageAttribute(String cultureName)
+ {
+ if (cultureName == null)
+ throw new ArgumentNullException("cultureName");
+ Contract.EndContractBlock();
+
+ _culture = cultureName;
+ _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly;
+ }
+
+ public NeutralResourcesLanguageAttribute(String cultureName, UltimateResourceFallbackLocation location)
+ {
+ if (cultureName == null)
+ throw new ArgumentNullException("cultureName");
+ if (!Enum.IsDefined(typeof(UltimateResourceFallbackLocation), location))
+ throw new ArgumentException(Environment.GetResourceString("Arg_InvalidNeutralResourcesLanguage_FallbackLoc", location));
+ Contract.EndContractBlock();
+
+ _culture = cultureName;
+ _fallbackLoc = location;
+ }
+
+ public String CultureName {
+ get { return _culture; }
+ }
+
+ public UltimateResourceFallbackLocation Location {
+ get { return _fallbackLoc; }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/ResourceFallbackManager.cs b/src/mscorlib/src/System/Resources/ResourceFallbackManager.cs
new file mode 100644
index 0000000000..def7836e05
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/ResourceFallbackManager.cs
@@ -0,0 +1,278 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Encapsulates CultureInfo fallback for resource
+** lookup
+**
+**
+===========================================================*/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+#if FEATURE_CORECLR
+using System.Diagnostics.Contracts;
+#endif
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
+namespace System.Resources
+{
+ internal class ResourceFallbackManager : IEnumerable<CultureInfo>
+ {
+ private CultureInfo m_startingCulture;
+ private CultureInfo m_neutralResourcesCulture;
+ private bool m_useParents;
+
+// Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
+#if FEATURE_CORECLR
+ // This is a cache of the thread, process, user, and OS-preferred fallback cultures.
+ // However, each thread may have a different value, and these may change during the
+ // lifetime of the process. So this cache must be verified each time we use it.
+ // Hence, we'll keep an array of strings for culture names & check it each time,
+ // but we'll really cache an array of CultureInfo's. Using thread-local statics
+ // as well to avoid differences across threads.
+ [ThreadStatic]
+ private static CultureInfo[] cachedOsFallbackArray;
+#endif // FEATURE_CORECLR
+
+ internal ResourceFallbackManager(CultureInfo startingCulture, CultureInfo neutralResourcesCulture, bool useParents)
+ {
+ if (startingCulture != null)
+ {
+ m_startingCulture = startingCulture;
+ }
+ else
+ {
+ m_startingCulture = CultureInfo.CurrentUICulture;
+ }
+
+ m_neutralResourcesCulture = neutralResourcesCulture;
+ m_useParents = useParents;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ // WARING: This function must be kept in sync with ResourceManager.GetFirstResourceSet()
+ public IEnumerator<CultureInfo> GetEnumerator()
+ {
+ bool reachedNeutralResourcesCulture = false;
+
+ // 1. starting culture chain, up to neutral
+ CultureInfo currentCulture = m_startingCulture;
+ do
+ {
+ if (m_neutralResourcesCulture != null && currentCulture.Name == m_neutralResourcesCulture.Name)
+ {
+ // Return the invariant culture all the time, even if the UltimateResourceFallbackLocation
+ // is a satellite assembly. This is fixed up later in ManifestBasedResourceGroveler::UltimateFallbackFixup.
+ yield return CultureInfo.InvariantCulture;
+ reachedNeutralResourcesCulture = true;
+ break;
+ }
+ yield return currentCulture;
+ currentCulture = currentCulture.Parent;
+ } while (m_useParents && !currentCulture.HasInvariantCultureName);
+
+ if (!m_useParents || m_startingCulture.HasInvariantCultureName)
+ {
+ yield break;
+ }
+
+// Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
+#if FEATURE_CORECLR
+ // 2. user preferred cultures, omitting starting culture if tried already
+ // Compat note: For console apps, this API will return cultures like Arabic
+ // or Hebrew that are displayed right-to-left. These don't work with today's
+ // CMD.exe. Since not all apps can short-circuit RTL languages to look at
+ // US English resources, we're exposing an appcompat flag for this, to make the
+ // osFallbackArray an empty array, mimicing our V2 behavior. Apps should instead
+ // be using CultureInfo.GetConsoleFallbackUICulture, and then test whether that
+ // culture's code page can be displayed on the console, and if not, they should
+ // set their culture to their neutral resources language.
+ // Note: the app compat switch will omit the OS Preferred fallback culture.
+ // Compat note 2: This feature breaks certain apps dependent on fallback to neutral
+ // resources. See extensive note in GetResourceFallbackArray.
+ CultureInfo[] osFallbackArray = LoadPreferredCultures();
+ if (osFallbackArray != null)
+ {
+ foreach (CultureInfo ci in osFallbackArray)
+ {
+ // only have to check starting culture and immediate parent for now.
+ // in Dev10, revisit this policy.
+ if (m_startingCulture.Name != ci.Name && m_startingCulture.Parent.Name != ci.Name)
+ {
+ yield return ci;
+ }
+ }
+ }
+#endif // FEATURE_CORECLR
+
+ // 3. invariant
+ // Don't return invariant twice though.
+ if (reachedNeutralResourcesCulture)
+ yield break;
+
+ yield return CultureInfo.InvariantCulture;
+ }
+
+// Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
+#if FEATURE_CORECLR
+ private static CultureInfo[] LoadPreferredCultures()
+ {
+ // The list of preferred cultures includes thread, process, user, and OS
+ // information and may theoretically change every time we call it.
+ // The caching does save us some allocations - this complexity saved about
+ // 7% of the wall clock time on a US English machine, and may save more on non-English
+ // boxes (since the fallback list may be longer).
+ String[] cultureNames = GetResourceFallbackArray();
+ if (cultureNames == null)
+ return null;
+
+ bool useCachedNames = (cachedOsFallbackArray != null && cultureNames.Length == cachedOsFallbackArray.Length);
+ if (useCachedNames)
+ {
+ for (int i = 0; i < cultureNames.Length; i++)
+ {
+ if (!String.Equals(cultureNames[i], cachedOsFallbackArray[i].Name))
+ {
+ useCachedNames = false;
+ break;
+ }
+ }
+ }
+ if (useCachedNames)
+ return cachedOsFallbackArray;
+
+ cachedOsFallbackArray = LoadCulturesFromNames(cultureNames);
+ return cachedOsFallbackArray;
+ }
+
+ private static CultureInfo[] LoadCulturesFromNames(String[] cultureNames)
+ {
+ if (cultureNames == null)
+ return null;
+
+ CultureInfo[] cultures = new CultureInfo[cultureNames.Length];
+ int culturesIndex = 0;
+ for (int i = 0; i < cultureNames.Length; i++)
+ {
+ // get cached, read-only cultures to avoid excess allocations
+ cultures[culturesIndex] = CultureInfo.GetCultureInfo(cultureNames[i]);
+ // Note GetCultureInfo can return null for a culture name that we don't support on the current OS.
+ // Don't leave a null in the middle of the array.
+ if (!Object.ReferenceEquals(cultures[culturesIndex], null))
+ culturesIndex++;
+ }
+
+ // If we couldn't create a culture, return an array of the right length.
+ if (culturesIndex != cultureNames.Length)
+ {
+ CultureInfo[] ret = new CultureInfo[culturesIndex];
+ Array.Copy(cultures, ret, culturesIndex);
+ cultures = ret;
+ }
+
+ return cultures;
+ }
+
+
+ // Note: May return null.
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private static String[] GetResourceFallbackArray()
+ {
+ // AppCompat note: We've added this feature for desktop V4 but we ripped it out
+ // before shipping V4. It shipped in SL 2 and SL 3. We preserved this behavior in SL 4
+ // for compat with previous Silverlight releases. We considered re-introducing this in .NET
+ // 4.5 for Windows 8 but chose not to because the Windows 8 immersive resources model
+ // has been redesigned from the ground up and we chose to support it (for portable libraries
+ // only) instead of further enhancing support for the classic resources model.
+ // ---------------------------------------------------------------------
+ //
+ // We have an appcompat problem that prevents us from adopting the ideal MUI model for
+ // culture fallback. Up until .NET Framework v4, our fallback was this:
+ //
+ // CurrentUICulture & parents Neutral
+ //
+ // We also had applications that took a dependency on falling back to neutral resources.
+ // IE, say an app is developed by US English developers - they may include English resources
+ // in the main assembly, not ship an "en" satellite assembly, and ship a French satellite.
+ // They may also omit the NeutralResourcesLanguageAttribute.
+ //
+ // Starting with Silverlight v2 and following advice from the MUI team, we wanted to call
+ // the OS's GetThreadPreferredUILanguages, inserting the results like this:
+ //
+ // CurrentUICulture & parents user-preferred fallback OS-preferred fallback Neutral
+ //
+ // This does not fit well for two reasons:
+ // 1) There is no concept of neutral resources in MUI
+ // 2) The user-preferred culture fallbacks make no sense in servers & non-interactive apps
+ // This leads to bad results on certain combinations of OS language installations, user
+ // settings, and applications built in certain styles. The OS-preferred fallback should
+ // be last, and the user-preferred fallback just breaks certain apps no matter where you put it.
+ //
+ // Necessary and sufficient conditions for an AppCompat bug (if we respected user & OS fallbacks):
+ // 1) A French OS (ie, you walk into an Internet café in Paris)
+ // 2) A .NET application whose neutral resources are authored in English.
+ // 3) The application did not provide an English satellite assembly (a common pattern).
+ // 4) The application is localized to French.
+ // 5) The user wants to read English, expressed in either of two ways:
+ // a. Changing Windows’ Display Language in the Regional Options Control Panel
+ // b. The application explicitly ASKS THE USER what language to display.
+ //
+ // Obviously the exact languages above can be interchanged a bit - I’m keeping this concrete.
+ // Also the NeutralResourcesLanguageAttribute will allow this to work, but usually we set it
+ // to en-US for our assemblies, meaning all other English cultures are broken.
+ //
+ // Workarounds:
+ // *) Use the NeutralResourcesLanguageAttribute and tell us that your neutral resources
+ // are in region-neutral English (en).
+ // *) Consider shipping a region-neutral English satellite assembly.
+
+ // Future work:
+ // 2) Consider a mechanism for individual assemblies to opt into wanting user-preferred fallback.
+ // They should ship their neutral resources in a satellite assembly, or use the
+ // NeutralResourcesLanguageAttribute to say their neutral resources are in a REGION-NEUTRAL
+ // language. An appdomain or process-wide flag may not be sufficient.
+ // 3) Ask Windows to clarify the scenario for the OS preferred fallback list, to decide whether
+ // we should probe there before or after looking at the neutral resources. If we move it
+ // to after the neutral resources, ask Windows to return a user-preferred fallback list
+ // without the OS preferred fallback included. This is a feature request for
+ // GetThreadPreferredUILanguages. We can muddle through without it by removing the OS
+ // preferred fallback cultures from end of the combined user + OS preferred fallback list, carefully.
+ // 4) Do not look at user-preferred fallback if Environment.UserInteractive is false. (IE,
+ // the Windows user who launches ASP.NET shouldn't determine how a web page gets
+ // localized - the server itself must respect the remote client's requested languages.)
+ // 6) Figure out what should happen in servers (ASP.NET, SQL, NT Services, etc).
+ //
+ // Done:
+ // 1) Got data from Windows on priority of supporting OS preferred fallback. We need to do it.
+ // Helps with consistency w/ Windows, and may be necessary for a long tail of other languages
+ // (ie, Windows has various degrees of localization support for ~135 languages, and fallbacks
+ // to certain languages is important.)
+ // 5) Revisited guidance for using the NeutralResourcesLanguageAttribute. Our docs should now say
+ // always pick a region-neutral language (ie, "en").
+
+// TODO (matell): I think we actually want to pull this into the PAL on CultureInfo?
+#if FEATURE_COREFX_GLOBALIZATION
+ return null;
+#else
+ return CultureInfo.nativeGetResourceFallbackArray();
+#endif
+ }
+
+#endif // FEATURE_CORECLR
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/ResourceManager.cs b/src/mscorlib/src/System/Resources/ResourceManager.cs
new file mode 100644
index 0000000000..b088e7f492
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/ResourceManager.cs
@@ -0,0 +1,1679 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Default way to access String and Object resources
+** from an assembly.
+**
+**
+===========================================================*/
+
+namespace System.Resources {
+ using System;
+ using System.IO;
+ using System.Globalization;
+ using System.Collections;
+ using System.Text;
+ using System.Reflection;
+ using System.Runtime.Serialization;
+ using System.Security;
+ using System.Security.Permissions;
+ using System.Threading;
+ using System.Runtime.InteropServices;
+ using System.Runtime.CompilerServices;
+ using Microsoft.Win32;
+ using System.Collections.Generic;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+#if !FEATURE_CORECLR
+ using System.Diagnostics.Tracing;
+#endif
+
+#if FEATURE_APPX
+ //
+ // This is implemented in System.Runtime.WindowsRuntime as function System.Resources.WindowsRuntimeResourceManager,
+ // allowing us to ask for a WinRT-specific ResourceManager.
+ // It is important to have WindowsRuntimeResourceManagerBase as regular class with virtual methods and default implementations.
+ // Defining WindowsRuntimeResourceManagerBase as abstract class or interface will cause issues when adding more methods to it
+ // because it’ll create dependency between mscorlib and System.Runtime.WindowsRuntime which will require always shipping both DLLs together.
+ // Also using interface or abstract class will not play nice with FriendAccessAllowed.
+ //
+ [FriendAccessAllowed]
+ [SecurityCritical]
+ internal class WindowsRuntimeResourceManagerBase
+ {
+ [SecurityCritical]
+ public virtual bool Initialize(string libpath, string reswFilename, out PRIExceptionInfo exceptionInfo){exceptionInfo = null; return false;}
+
+ [SecurityCritical]
+ public virtual String GetString(String stringName, String startingCulture, String neutralResourcesCulture){return null;}
+
+ public virtual CultureInfo GlobalResourceContextBestFitCultureInfo {
+ [SecurityCritical]
+ get { return null; }
+ }
+
+ [SecurityCritical]
+ public virtual bool SetGlobalResourceContextDefaultCulture(CultureInfo ci) { return false; }
+ }
+
+ [FriendAccessAllowed]
+ internal class PRIExceptionInfo
+ {
+ public string _PackageSimpleName;
+ public string _ResWFile;
+ }
+#endif // FEATURE_APPX
+
+ // Resource Manager exposes an assembly's resources to an application for
+ // the correct CultureInfo. An example would be localizing text for a
+ // user-visible message. Create a set of resource files listing a name
+ // for a message and its value, compile them using ResGen, put them in
+ // an appropriate place (your assembly manifest(?)), then create a Resource
+ // Manager and query for the name of the message you want. The Resource
+ // Manager will use CultureInfo.GetCurrentUICulture() to look
+ // up a resource for your user's locale settings.
+ //
+ // Users should ideally create a resource file for every culture, or
+ // at least a meaningful subset. The filenames will follow the naming
+ // scheme:
+ //
+ // basename.culture name.resources
+ //
+ // The base name can be the name of your application, or depending on
+ // the granularity desired, possibly the name of each class. The culture
+ // name is determined from CultureInfo's Name property.
+ // An example file name may be MyApp.en-US.resources for
+ // MyApp's US English resources.
+ //
+ // -----------------
+ // Refactoring Notes
+ // -----------------
+ // In Feb 08, began first step of refactoring ResourceManager to improve
+ // maintainability (sd changelist 3012100). This resulted in breaking
+ // apart the InternalGetResourceSet "big loop" so that the file-based
+ // and manifest-based lookup was located in separate methods.
+ // In Apr 08, continued refactoring so that file-based and manifest-based
+ // concerns are encapsulated by separate classes. At construction, the
+ // ResourceManager creates one of these classes based on whether the
+ // RM will need to use file-based or manifest-based resources, and
+ // afterwards refers to this through the interface IResourceGroveler.
+ //
+ // Serialization Compat: Ideally, we could have refactored further but
+ // this would have broken serialization compat. For example, the
+ // ResourceManager member UseManifest and UseSatelliteAssem are no
+ // longer relevant on ResourceManager. Similarly, other members could
+ // ideally be moved to the file-based or manifest-based classes
+ // because they are only relevant for those types of lookup.
+ //
+ // Solution now / in the future:
+ // For now, we simply use a mediator class so that we can keep these
+ // members on ResourceManager but allow the file-based and manifest-
+ // based classes to access/set these members in a uniform way. See
+ // ResourceManagerMediator.
+ // We encapsulate fallback logic in a fallback iterator class, so that
+ // this logic isn't duplicated in several methods.
+ //
+ // In the future, we can look into either breaking serialization if we
+ // decide this doesn't make sense for ResourceManager (i.e. how common
+ // is the scenario), manually make serialization work by providing
+ // appropriate OnSerialization, Deserialization methods. We can also
+ // look into further factoring and better design of IResourceGroveler
+ // interface to accommodate unused parameters that don't make sense
+ // for either file-based or manifest-based lookup paths.
+ //
+ // Benefits of this refactoring:
+ // - Makes it possible to understand what the ResourceManager does,
+ // which is key for maintainability.
+ // - Makes the ResourceManager more extensible by identifying and
+ // encapsulating what varies
+ // - Unearthed a bug that's been lurking a while in file-based
+ // lookup paths for InternalGetResourceSet if createIfNotExists is
+ // false.
+ // - Reuses logic, e.g. by breaking apart the culture fallback into
+ // the fallback iterator class, we don't have to repeat the
+ // sometimes confusing fallback logic across multiple methods
+ // - Fxcop violations reduced to 1/5th of original count. Most
+ // importantly, code complexity violations disappeared.
+ // - Finally, it got rid of dead code paths. Because the big loop was
+ // so confusing, it masked unused chunks of code. Also, dividing
+ // between file-based and manifest-based allowed functionaliy
+ // unused in silverlight to fall out.
+ //
+ // Note: this type is integral to the construction of exception objects,
+ // and sometimes this has to be done in low memory situtations (OOM) or
+ // to create TypeInitializationExceptions due to failure of a static class
+ // constructor. This type needs to be extremely careful and assume that
+ // any type it references may have previously failed to construct, so statics
+ // belonging to that type may not be initialized. FrameworkEventSource.Log
+ // is one such example.
+ //
+
+ [Serializable]
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public class ResourceManager
+ {
+
+ internal class CultureNameResourceSetPair {
+ public String lastCultureName;
+ public ResourceSet lastResourceSet;
+ }
+
+ protected String BaseNameField;
+ // Sets is a many-to-one table of CultureInfos mapped to ResourceSets.
+ // Don't synchronize ResourceSets - too fine-grained a lock to be effective
+ [Obsolete("call InternalGetResourceSet instead")]
+ protected Hashtable ResourceSets;
+
+
+ // don't serialize the cache of ResourceSets
+ [NonSerialized]
+ private Dictionary <String,ResourceSet> _resourceSets;
+ private String moduleDir; // For assembly-ignorant directory location
+ protected Assembly MainAssembly; // Need the assembly manifest sometimes.
+ private Type _locationInfo; // For Assembly or type-based directory layout
+ private Type _userResourceSet; // Which ResourceSet instance to create
+ private CultureInfo _neutralResourcesCulture; // For perf optimizations.
+
+ [NonSerialized]
+ private CultureNameResourceSetPair _lastUsedResourceCache;
+
+ private bool _ignoreCase; // Whether case matters in GetString & GetObject
+
+ private bool UseManifest; // Use Assembly manifest, or grovel disk.
+
+ // unused! But need to keep for serialization
+ [OptionalField(VersionAdded = 1)]
+ private bool UseSatelliteAssem; // Are all the .resources files in the
+ // main assembly, or in satellite assemblies for each culture?
+#if RESOURCE_SATELLITE_CONFIG
+ private static volatile Hashtable _installedSatelliteInfo; // Give the user the option
+ // to prevent certain satellite assembly probes via a config file.
+ // Note that config files are per-appdomain, not per-assembly nor process
+ private static volatile bool _checkedConfigFile; // Did we read the app's config file?
+#endif
+
+ // Whether to fall back to the main assembly or a particular
+ // satellite for the neutral resources.
+ [OptionalField]
+ private UltimateResourceFallbackLocation _fallbackLoc;
+ // Version number of satellite assemblies to look for. May be null.
+ [OptionalField]
+ private Version _satelliteContractVersion;
+ [OptionalField]
+ private bool _lookedForSatelliteContractVersion;
+
+ // unused! But need to keep for serialization
+ [OptionalField(VersionAdded = 1)]
+ private Assembly _callingAssembly; // Assembly who created the ResMgr.
+
+ // replaces _callingAssembly
+ [OptionalField(VersionAdded = 4)]
+ private RuntimeAssembly m_callingAssembly; // Assembly who created the ResMgr.
+
+ // no need to serialize this; just create a new one on deserialization
+ [NonSerialized]
+ private IResourceGroveler resourceGroveler;
+
+ public static readonly int MagicNumber = unchecked((int)0xBEEFCACE); // If only hex had a K...
+
+ // Version number so ResMgr can get the ideal set of classes for you.
+ // ResMgr header is:
+ // 1) MagicNumber (little endian Int32)
+ // 2) HeaderVersionNumber (little endian Int32)
+ // 3) Num Bytes to skip past ResMgr header (little endian Int32)
+ // 4) IResourceReader type name for this file (bytelength-prefixed UTF-8 String)
+ // 5) ResourceSet type name for this file (bytelength-prefixed UTF8 String)
+ public static readonly int HeaderVersionNumber = 1;
+
+ //
+ //It would be better if we could use _neutralCulture instead of calling
+ //CultureInfo.InvariantCulture everywhere, but we run into problems with the .cctor. CultureInfo
+ //initializes assembly, which initializes ResourceManager, which tries to get a CultureInfo which isn't
+ //there yet because CultureInfo's class initializer hasn't finished. If we move SystemResMgr off of
+ //Assembly (or at least make it an internal property) we should be able to circumvent this problem.
+ //
+ // private static CultureInfo _neutralCulture = null;
+
+ // This is our min required ResourceSet type.
+ private static readonly Type _minResourceSet = typeof(ResourceSet);
+ // These Strings are used to avoid using Reflection in CreateResourceSet.
+ // The first set are used by ResourceWriter. The second are used by
+ // InternalResGen.
+ internal static readonly String ResReaderTypeName = typeof(ResourceReader).FullName;
+ internal static readonly String ResSetTypeName = typeof(RuntimeResourceSet).FullName;
+ internal static readonly String MscorlibName = typeof(ResourceReader).Assembly.FullName;
+ internal const String ResFileExtension = ".resources";
+ internal const int ResFileExtensionLength = 10;
+
+ // My private debugging aid. Set to 5 or 6 for verbose output. Set to 3
+ // for summary level information.
+ internal static readonly int DEBUG = 0; //Making this const causes C# to consider all of the code that it guards unreachable.
+
+ private static volatile bool s_IsAppXModel;
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ private void Init()
+ {
+ m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
+ }
+
+ protected ResourceManager()
+ {
+#if !FEATURE_CORECLR
+ // This constructor is not designed to be used under AppX and is not in the Win8 profile.
+ // However designers may use them even if they are running under AppX since they are
+ // not subject to the restrictions of the Win8 profile.
+ Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
+#endif
+
+ Init();
+
+ _lastUsedResourceCache = new CultureNameResourceSetPair();
+ ResourceManagerMediator mediator = new ResourceManagerMediator(this);
+ resourceGroveler = new ManifestBasedResourceGroveler(mediator);
+ }
+
+ // Constructs a Resource Manager for files beginning with
+ // baseName in the directory specified by resourceDir
+ // or in the current directory. This Assembly-ignorant constructor is
+ // mostly useful for testing your own ResourceSet implementation.
+ //
+ // A good example of a baseName might be "Strings". BaseName
+ // should not end in ".resources".
+ //
+ // Note: System.Windows.Forms uses this method at design time.
+ //
+ private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) {
+ if (null==baseName)
+ throw new ArgumentNullException("baseName");
+ if (null==resourceDir)
+ throw new ArgumentNullException("resourceDir");
+ Contract.EndContractBlock();
+
+#if !FEATURE_CORECLR
+ // This constructor is not designed to be used under AppX and is not in the Win8 profile.
+ // However designers may use them even if they are running under AppX since they are
+ // not subject to the restrictions of the Win8 profile.
+ Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
+#endif
+
+ BaseNameField = baseName;
+
+ moduleDir = resourceDir;
+ _userResourceSet = usingResourceSet;
+#pragma warning disable 618
+ ResourceSets = new Hashtable(); // for backward compatibility
+#pragma warning restore 618
+ _resourceSets = new Dictionary<String, ResourceSet>();
+ _lastUsedResourceCache = new CultureNameResourceSetPair();
+ UseManifest = false;
+
+ ResourceManagerMediator mediator = new ResourceManagerMediator(this);
+ resourceGroveler = new FileBasedResourceGroveler(mediator);
+
+#if !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile event providers for coreclr
+ if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
+ CultureInfo culture = CultureInfo.InvariantCulture;
+ String defaultResName = GetResourceFileName(culture);
+
+ if (resourceGroveler.HasNeutralResources(culture, defaultResName)) {
+ FrameworkEventSource.Log.ResourceManagerNeutralResourcesFound(BaseNameField, MainAssembly, defaultResName);
+ }
+ else {
+ FrameworkEventSource.Log.ResourceManagerNeutralResourcesNotFound(BaseNameField, MainAssembly, defaultResName);
+ }
+ }
+#endif
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public ResourceManager(String baseName, Assembly assembly)
+ {
+ if (null==baseName)
+ throw new ArgumentNullException("baseName");
+
+ if (null==assembly)
+ throw new ArgumentNullException("assembly");
+ Contract.EndContractBlock();
+
+ if (!(assembly is RuntimeAssembly))
+ throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
+
+ MainAssembly = assembly;
+ BaseNameField = baseName;
+
+ SetAppXConfiguration();
+
+ CommonAssemblyInit();
+
+ m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
+ // Special case for mscorlib - protect mscorlib's private resources.
+ // This isn't for security reasons, but to ensure we can make
+ // breaking changes to mscorlib's internal resources without
+ // assuming users may have taken a dependency on them.
+ if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
+ {
+ m_callingAssembly = null;
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public ResourceManager(String baseName, Assembly assembly, Type usingResourceSet)
+ {
+ if (null==baseName)
+ throw new ArgumentNullException("baseName");
+ if (null==assembly)
+ throw new ArgumentNullException("assembly");
+ Contract.EndContractBlock();
+
+#if !FEATURE_CORECLR
+ // This constructor is not designed to be used under AppX and is not in the Win8 profile.
+ // However designers may use them even if they are running under AppX since they are
+ // not subject to the restrictions of the Win8 profile.
+ Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
+#endif
+
+ if (!(assembly is RuntimeAssembly))
+ throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
+
+ MainAssembly = assembly;
+ BaseNameField = baseName;
+
+ if (usingResourceSet != null && (usingResourceSet != _minResourceSet) && !(usingResourceSet.IsSubclassOf(_minResourceSet)))
+ throw new ArgumentException(Environment.GetResourceString("Arg_ResMgrNotResSet"), "usingResourceSet");
+ _userResourceSet = usingResourceSet;
+
+ CommonAssemblyInit();
+ m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
+ // Special case for mscorlib - protect mscorlib's private resources.
+ // This isn't for security reasons, but to ensure we can make
+ // breaking changes to mscorlib's internal resources without
+ // assuming users may have taken a dependency on them.
+ if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
+ m_callingAssembly = null;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ public ResourceManager(Type resourceSource)
+ {
+ if (null==resourceSource)
+ throw new ArgumentNullException("resourceSource");
+ Contract.EndContractBlock();
+
+ if (!(resourceSource is RuntimeType))
+ throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"));
+
+ _locationInfo = resourceSource;
+ MainAssembly = _locationInfo.Assembly;
+ BaseNameField = resourceSource.Name;
+
+ SetAppXConfiguration();
+
+ CommonAssemblyInit();
+
+ m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
+ // Special case for mscorlib - protect mscorlib's private resources.
+ if (MainAssembly == typeof(Object).Assembly && m_callingAssembly != MainAssembly)
+ {
+ m_callingAssembly = null;
+ }
+ }
+
+ [OnDeserializing]
+ private void OnDeserializing(StreamingContext ctx)
+ {
+ this._resourceSets = null;
+ this.resourceGroveler = null;
+ this._lastUsedResourceCache = null;
+ }
+
+ [System.Security.SecuritySafeCritical]
+ [OnDeserialized]
+ private void OnDeserialized(StreamingContext ctx)
+ {
+ _resourceSets = new Dictionary<String, ResourceSet>();
+ _lastUsedResourceCache = new CultureNameResourceSetPair();
+ // set up resource groveler, depending on whether this ResourceManager
+ // is looking for files or assemblies
+ ResourceManagerMediator mediator = new ResourceManagerMediator(this);
+ if (UseManifest)
+ {
+ resourceGroveler = new ManifestBasedResourceGroveler(mediator);
+ }
+ else
+ {
+ resourceGroveler = new FileBasedResourceGroveler(mediator);
+ }
+
+ // correct callingAssembly for v2
+ if (this.m_callingAssembly == null)
+ {
+ this.m_callingAssembly = (RuntimeAssembly)_callingAssembly;
+ }
+
+ // v2 does this lazily
+ if (UseManifest && this._neutralResourcesCulture == null)
+ {
+ _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
+ }
+ }
+
+ [OnSerializing]
+ private void OnSerializing(StreamingContext ctx)
+ {
+ // Initialize the fields Whidbey expects
+ _callingAssembly = m_callingAssembly;
+ UseSatelliteAssem = UseManifest;
+#pragma warning disable 618
+ ResourceSets = new Hashtable(); // for backward compatibility
+#pragma warning restore 618
+ }
+
+
+ // Trying to unify code as much as possible, even though having to do a
+ // security check in each constructor prevents it.
+ [System.Security.SecuritySafeCritical]
+ private void CommonAssemblyInit()
+ {
+ if (_bUsingModernResourceManagement == false)
+ {
+ UseManifest = true;
+
+ _resourceSets = new Dictionary<String,ResourceSet>();
+ _lastUsedResourceCache = new CultureNameResourceSetPair();
+
+ _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly;
+
+ ResourceManagerMediator mediator = new ResourceManagerMediator(this);
+ resourceGroveler = new ManifestBasedResourceGroveler(mediator);
+ }
+
+ _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
+
+#if !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile event providers for coreclr
+ if (_bUsingModernResourceManagement == false)
+ {
+ if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
+ CultureInfo culture = CultureInfo.InvariantCulture;
+ String defaultResName = GetResourceFileName(culture);
+
+ if (resourceGroveler.HasNeutralResources(culture, defaultResName)) {
+ FrameworkEventSource.Log.ResourceManagerNeutralResourcesFound(BaseNameField, MainAssembly, defaultResName);
+ }
+ else {
+ String outputResName = defaultResName;
+ if (_locationInfo != null && _locationInfo.Namespace != null)
+ outputResName = _locationInfo.Namespace + Type.Delimiter + defaultResName;
+ FrameworkEventSource.Log.ResourceManagerNeutralResourcesNotFound(BaseNameField, MainAssembly, outputResName);
+ }
+ }
+
+#pragma warning disable 618
+ ResourceSets = new Hashtable(); // for backward compatibility
+#pragma warning restore 618
+ }
+#endif
+ }
+
+ // Gets the base name for the ResourceManager.
+ public virtual String BaseName {
+ get { return BaseNameField; }
+ }
+
+ // Whether we should ignore the capitalization of resources when calling
+ // GetString or GetObject.
+ public virtual bool IgnoreCase {
+ get { return _ignoreCase; }
+ set { _ignoreCase = value; }
+ }
+
+ // Returns the Type of the ResourceSet the ResourceManager uses
+ // to construct ResourceSets.
+ public virtual Type ResourceSetType {
+ get { return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; }
+ }
+
+ protected UltimateResourceFallbackLocation FallbackLocation
+ {
+ get { return _fallbackLoc; }
+ set { _fallbackLoc = value; }
+ }
+
+ // Tells the ResourceManager to call Close on all ResourceSets and
+ // release all resources. This will shrink your working set by
+ // potentially a substantial amount in a running application. Any
+ // future resource lookups on this ResourceManager will be as
+ // expensive as the very first lookup, since it will need to search
+ // for files and load resources again.
+ //
+ // This may be useful in some complex threading scenarios, where
+ // creating a new ResourceManager isn't quite the correct behavior.
+ public virtual void ReleaseAllResources()
+ {
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerReleasingResources(BaseNameField, MainAssembly);
+ }
+#endif
+ Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
+
+ // If any calls to Close throw, at least leave ourselves in a
+ // consistent state.
+ _resourceSets = new Dictionary<String,ResourceSet>();
+ _lastUsedResourceCache = new CultureNameResourceSetPair();
+
+ lock(localResourceSets) {
+ IDictionaryEnumerator setEnum = localResourceSets.GetEnumerator();
+
+#if !FEATURE_CORECLR
+ IDictionaryEnumerator setEnum2 = null;
+#pragma warning disable 618
+ if (ResourceSets != null) {
+ setEnum2 = ResourceSets.GetEnumerator();
+ }
+ ResourceSets = new Hashtable(); // for backwards compat
+#pragma warning restore 618
+#endif
+
+ while (setEnum.MoveNext()) {
+ ((ResourceSet)setEnum.Value).Close();
+ }
+
+#if !FEATURE_CORECLR
+ if (setEnum2 != null) {
+ while (setEnum2.MoveNext()) {
+ ((ResourceSet)setEnum2.Value).Close();
+ }
+ }
+#endif
+ }
+ }
+
+ public static ResourceManager CreateFileBasedResourceManager(String baseName, String resourceDir, Type usingResourceSet)
+ {
+ return new ResourceManager(baseName, resourceDir, usingResourceSet);
+ }
+
+ // Given a CultureInfo, GetResourceFileName generates the name for
+ // the binary file for the given CultureInfo. This method uses
+ // CultureInfo's Name property as part of the file name for all cultures
+ // other than the invariant culture. This method does not touch the disk,
+ // and is used only to construct what a resource file name (suitable for
+ // passing to the ResourceReader constructor) or a manifest resource file
+ // name should look like.
+ //
+ // This method can be overriden to look for a different extension,
+ // such as ".ResX", or a completely different format for naming files.
+ protected virtual String GetResourceFileName(CultureInfo culture) {
+ StringBuilder sb = new StringBuilder(255);
+ sb.Append(BaseNameField);
+ // If this is the neutral culture, don't append culture name.
+ if (!culture.HasInvariantCultureName)
+ {
+ CultureInfo.VerifyCultureName(culture.Name, true);
+ sb.Append('.');
+ sb.Append(culture.Name);
+ }
+ sb.Append(ResFileExtension);
+ return sb.ToString();
+ }
+
+ // WARNING: This function must be kept in sync with ResourceFallbackManager.GetEnumerator()
+ // Return the first ResourceSet, based on the first culture ResourceFallbackManager would return
+ internal ResourceSet GetFirstResourceSet(CultureInfo culture)
+ {
+ // Logic from ResourceFallbackManager.GetEnumerator()
+ if (_neutralResourcesCulture != null && culture.Name == _neutralResourcesCulture.Name)
+ {
+ culture = CultureInfo.InvariantCulture;
+ }
+
+ if(_lastUsedResourceCache != null) {
+ lock (_lastUsedResourceCache) {
+ if (culture.Name == _lastUsedResourceCache.lastCultureName)
+ return _lastUsedResourceCache.lastResourceSet;
+ }
+ }
+
+ // Look in the ResourceSet table
+ Dictionary<String,ResourceSet> localResourceSets = _resourceSets;
+ ResourceSet rs = null;
+ if (localResourceSets != null)
+ {
+ lock (localResourceSets)
+ {
+ localResourceSets.TryGetValue(culture.Name, out rs);
+ }
+ }
+
+ if (rs != null)
+ {
+ // update the cache with the most recent ResourceSet
+ if (_lastUsedResourceCache != null) {
+ lock (_lastUsedResourceCache) {
+ _lastUsedResourceCache.lastCultureName = culture.Name;
+ _lastUsedResourceCache.lastResourceSet = rs;
+ }
+ }
+ return rs;
+ }
+
+ return null;
+ }
+
+ // Looks up a set of resources for a particular CultureInfo. This is
+ // not useful for most users of the ResourceManager - call
+ // GetString() or GetObject() instead.
+ //
+ // The parameters let you control whether the ResourceSet is created
+ // if it hasn't yet been loaded and if parent CultureInfos should be
+ // loaded as well for resource inheritance.
+ //
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
+ public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) {
+ if (null==culture)
+ throw new ArgumentNullException("culture");
+ Contract.EndContractBlock();
+
+ Dictionary<String,ResourceSet> localResourceSets = _resourceSets;
+ ResourceSet rs;
+ if (localResourceSets != null) {
+ lock (localResourceSets) {
+ if (localResourceSets.TryGetValue(culture.Name, out rs))
+ return rs;
+ }
+ }
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+
+ if (UseManifest && culture.HasInvariantCultureName) {
+ string fileName = GetResourceFileName(culture);
+ RuntimeAssembly mainAssembly = (RuntimeAssembly)MainAssembly;
+ Stream stream = mainAssembly.GetManifestResourceStream(_locationInfo, fileName, m_callingAssembly == MainAssembly, ref stackMark);
+ if (createIfNotExists && stream!=null) {
+ rs = ((ManifestBasedResourceGroveler)resourceGroveler).CreateResourceSet(stream, MainAssembly);
+ AddResourceSet(localResourceSets, culture.Name, ref rs);
+ return rs;
+ }
+ }
+
+ // Note: ideally we could plumb through the stack crawl mark here, but we must
+ // call the virtual 3-argument InternalGetResourceSet method for compatibility.
+ // Security-wise, we're not overly interested in protecting access to resources,
+ // since full-trust callers can get them already and most resources are public.
+ // Also, the JIT inliner could always inline a caller into another assembly's
+ // method.
+ // So if we happen to return some resources in cases where we should really be
+ // doing a demand for member access permissions, we're not overly concerned.
+ return InternalGetResourceSet(culture, createIfNotExists, tryParents);
+ }
+
+ // InternalGetResourceSet is a non-threadsafe method where all the logic
+ // for getting a resource set lives. Access to it is controlled by
+ // threadsafe methods such as GetResourceSet, GetString, & GetObject.
+ // This will take a minimal number of locks.
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
+ protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
+ {
+ Contract.Assert(culture != null, "culture != null");
+
+ StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+ return InternalGetResourceSet(culture,createIfNotExists,tryParents, ref stackMark);
+ }
+
+ // InternalGetResourceSet is a non-threadsafe method where all the logic
+ // for getting a resource set lives. Access to it is controlled by
+ // threadsafe methods such as GetResourceSet, GetString, & GetObject.
+ // This will take a minimal number of locks.
+ [System.Security.SecurityCritical]
+ private ResourceSet InternalGetResourceSet(CultureInfo requestedCulture, bool createIfNotExists, bool tryParents, ref StackCrawlMark stackMark)
+ {
+ Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
+ ResourceSet rs = null;
+ CultureInfo foundCulture = null;
+ lock (localResourceSets) {
+ if (localResourceSets.TryGetValue(requestedCulture.Name, out rs)) {
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized) {
+ FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCache(BaseNameField, MainAssembly, requestedCulture.Name);
+ }
+#endif
+ return rs;
+ }
+ }
+
+ ResourceFallbackManager mgr = new ResourceFallbackManager(requestedCulture, _neutralResourcesCulture, tryParents);
+
+ foreach (CultureInfo currentCultureInfo in mgr)
+ {
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerLookingForResourceSet(BaseNameField, MainAssembly, currentCultureInfo.Name);
+ }
+#endif
+ lock(localResourceSets) {
+ if (localResourceSets.TryGetValue(currentCultureInfo.Name, out rs)) {
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCache(BaseNameField, MainAssembly, currentCultureInfo.Name);
+ }
+#endif
+ // we need to update the cache if we fellback
+ if(requestedCulture != currentCultureInfo) foundCulture = currentCultureInfo;
+ break;
+ }
+ }
+
+ // InternalGetResourceSet will never be threadsafe. However, it must
+ // be protected against reentrancy from the SAME THREAD. (ie, calling
+ // GetSatelliteAssembly may send some window messages or trigger the
+ // Assembly load event, which could fail then call back into the
+ // ResourceManager). It's happened.
+
+ rs = resourceGroveler.GrovelForResourceSet(currentCultureInfo, localResourceSets,
+ tryParents, createIfNotExists, ref stackMark);
+
+ // found a ResourceSet; we're done
+ if (rs != null)
+ {
+ foundCulture = currentCultureInfo;
+ break;
+ }
+
+ }
+
+ if (rs != null && foundCulture != null)
+ {
+ // add entries to the cache for the cultures we have gone through
+
+ // currentCultureInfo now refers to the culture that had resources.
+ // update cultures starting from requested culture up to the culture
+ // that had resources.
+ foreach (CultureInfo updateCultureInfo in mgr)
+ {
+ AddResourceSet(localResourceSets, updateCultureInfo.Name, ref rs);
+
+ // stop when we've added current or reached invariant (top of chain)
+ if (updateCultureInfo == foundCulture)
+ {
+ break;
+ }
+ }
+ }
+
+ return rs;
+ }
+
+ // Simple helper to ease maintenance and improve readability.
+ private static void AddResourceSet(Dictionary<String,ResourceSet> localResourceSets, String cultureName, ref ResourceSet rs)
+ {
+ // InternalGetResourceSet is both recursive and reentrant -
+ // assembly load callbacks in particular are a way we can call
+ // back into the ResourceManager in unexpectedly on the same thread.
+ lock(localResourceSets) {
+ // If another thread added this culture, return that.
+ ResourceSet lostRace;
+ if (localResourceSets.TryGetValue(cultureName, out lostRace)) {
+ if (!Object.ReferenceEquals(lostRace, rs)) {
+ // Note: In certain cases, we can be trying to add a ResourceSet for multiple
+ // cultures on one thread, while a second thread added another ResourceSet for one
+ // of those cultures. If there is a race condition we must make sure our ResourceSet
+ // isn't in our dictionary before closing it.
+ if (!localResourceSets.ContainsValue(rs))
+ rs.Dispose();
+ rs = lostRace;
+ }
+ }
+ else {
+ localResourceSets.Add(cultureName, rs);
+ }
+ }
+ }
+
+ protected static Version GetSatelliteContractVersion(Assembly a)
+ {
+ // Ensure that the assembly reference is not null
+ if (a == null) {
+ throw new ArgumentNullException("a", Environment.GetResourceString("ArgumentNull_Assembly"));
+ }
+ Contract.EndContractBlock();
+
+#if !FEATURE_WINDOWSPHONE
+ String v = null;
+ if (a.ReflectionOnly) {
+ foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(a)) {
+ if (data.Constructor.DeclaringType == typeof(SatelliteContractVersionAttribute)) {
+ v = (String)data.ConstructorArguments[0].Value;
+ break;
+ }
+ }
+
+ if (v == null)
+ return null;
+ }
+ else {
+ Object[] attrs = a.GetCustomAttributes(typeof(SatelliteContractVersionAttribute), false);
+ if (attrs.Length == 0)
+ return null;
+ Contract.Assert(attrs.Length == 1, "Cannot have multiple instances of SatelliteContractVersionAttribute on an assembly!");
+ v = ((SatelliteContractVersionAttribute)attrs[0]).Version;
+ }
+ Version ver;
+ try {
+ ver = new Version(v);
+ }
+ catch(ArgumentOutOfRangeException e) {
+ // Note we are prone to hitting infinite loops if mscorlib's
+ // SatelliteContractVersionAttribute contains bogus values.
+ // If this assert fires, please fix the build process for the
+ // BCL directory.
+ if (a == typeof(Object).Assembly) {
+ Contract.Assert(false, System.CoreLib.Name+"'s SatelliteContractVersionAttribute is a malformed version string!");
+ return null;
+ }
+
+ throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSatelliteContract_Asm_Ver", a.ToString(), v), e);
+ }
+ return ver;
+#else
+ // On the phone return null. The calling code will use the assembly version instead to avoid potential type
+ // and library loads caused by CA lookup. NetCF uses the assembly version always.
+ return null;
+#endif
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ protected static CultureInfo GetNeutralResourcesLanguage(Assembly a)
+ {
+ // This method should be obsolete - replace it with the one below.
+ // Unfortunately, we made it protected.
+ UltimateResourceFallbackLocation ignoringUsefulData = UltimateResourceFallbackLocation.MainAssembly;
+ CultureInfo culture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(a, ref ignoringUsefulData);
+ return culture;
+ }
+
+ // IGNORES VERSION
+ internal static bool CompareNames(String asmTypeName1,
+ String typeName2,
+ AssemblyName asmName2)
+ {
+ Contract.Assert(asmTypeName1 != null, "asmTypeName1 was unexpectedly null");
+
+ // First, compare type names
+ int comma = asmTypeName1.IndexOf(',');
+ if (((comma == -1) ? asmTypeName1.Length : comma) != typeName2.Length)
+ return false;
+
+ // case sensitive
+ if (String.Compare(asmTypeName1, 0, typeName2, 0, typeName2.Length, StringComparison.Ordinal) != 0)
+ return false;
+ if (comma == -1)
+ return true;
+
+ // Now, compare assembly display names (IGNORES VERSION AND PROCESSORARCHITECTURE)
+ // also, for mscorlib ignores everything, since that's what the binder is going to do
+ while(Char.IsWhiteSpace(asmTypeName1[++comma]));
+
+ // case insensitive
+ AssemblyName an1 = new AssemblyName(asmTypeName1.Substring(comma));
+ if (String.Compare(an1.Name, asmName2.Name, StringComparison.OrdinalIgnoreCase) != 0)
+ return false;
+
+ // to match IsMscorlib() in VM
+ if (String.Compare(an1.Name, System.CoreLib.Name, StringComparison.OrdinalIgnoreCase) == 0)
+ return true;
+
+
+ if ((an1.CultureInfo != null) && (asmName2.CultureInfo != null) &&
+#if FEATURE_USE_LCID
+ (an1.CultureInfo.LCID != asmName2.CultureInfo.LCID)
+#else
+ (an1.CultureInfo.Name != asmName2.CultureInfo.Name)
+#endif
+ )
+ return false;
+
+ byte[] pkt1 = an1.GetPublicKeyToken();
+ byte[] pkt2 = asmName2.GetPublicKeyToken();
+ if ((pkt1 != null) && (pkt2 != null)) {
+ if (pkt1.Length != pkt2.Length)
+ return false;
+
+ for(int i=0; i < pkt1.Length; i++) {
+ if(pkt1[i] != pkt2[i])
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+#if FEATURE_APPX
+ [SecuritySafeCritical]
+ // Throws WinRT hresults
+ private string GetStringFromPRI(String stringName, String startingCulture, String neutralResourcesCulture) {
+ Contract.Assert(_bUsingModernResourceManagement);
+ Contract.Assert(_WinRTResourceManager != null);
+ Contract.Assert(_PRIonAppXInitialized);
+ Contract.Assert(AppDomain.IsAppXModel());
+
+ if (stringName.Length == 0)
+ return null;
+
+ string resourceString = null;
+
+ // Do not handle exceptions. See the comment in SetAppXConfiguration about throwing
+ // exception types that the ResourceManager class is not documented to throw.
+ resourceString = _WinRTResourceManager.GetString(
+ stringName,
+ String.IsNullOrEmpty(startingCulture) ? null : startingCulture,
+ String.IsNullOrEmpty(neutralResourcesCulture) ? null : neutralResourcesCulture);
+
+ return resourceString;
+ }
+
+ // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the type via reflection.
+ // It would be better if we could just implement WindowsRuntimeResourceManager in mscorlib, but we can't, because
+ // we can do very little with WinRT in mscorlib.
+ [SecurityCritical]
+ internal static WindowsRuntimeResourceManagerBase GetWinRTResourceManager()
+ {
+ Type WinRTResourceManagerType = Type.GetType("System.Resources.WindowsRuntimeResourceManager, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
+ return (WindowsRuntimeResourceManagerBase)Activator.CreateInstance(WinRTResourceManagerType, true);
+ }
+#endif
+
+ [NonSerialized]
+ private bool _bUsingModernResourceManagement; // Written only by SetAppXConfiguration
+
+#if FEATURE_APPX
+ [NonSerialized]
+ [SecurityCritical]
+ private WindowsRuntimeResourceManagerBase _WinRTResourceManager; // Written only by SetAppXConfiguration
+
+ [NonSerialized]
+ private bool _PRIonAppXInitialized; // Written only by SetAppXConfiguration
+
+ [NonSerialized]
+ private PRIExceptionInfo _PRIExceptionInfo; // Written only by SetAppXConfiguration
+
+ // When running under AppX, the following rules apply for resource lookup:
+ //
+ // Desktop
+ // -------
+ //
+ // 1) For Framework assemblies, we always use satellite assembly based lookup.
+ // 2) For non-FX assemblies, we use modern resource manager, with the premise being that app package
+ // contains the PRI resources since such assemblies are expected to be application assemblies.
+ //
+ // CoreCLR
+ // -------
+ //
+ // 1) For Framework assemblies, we always use satellite assembly based lookup.
+ // 2) For non-FX assemblies:
+ //
+ // a) If the assembly lives under PLATFORM_RESOURCE_ROOTS (as specified by the host during AppDomain creation),
+ // then we will use satellite assembly based lookup in assemblies like *.resources.dll.
+ //
+ // b) For any other non-FX assembly, we will use the modern resource manager with the premise that app package
+ // contains the PRI resources.
+ [SecuritySafeCritical]
+ private bool ShouldUseSatelliteAssemblyResourceLookupUnderAppX(RuntimeAssembly resourcesAssembly)
+ {
+ bool fUseSatelliteAssemblyResourceLookupUnderAppX = resourcesAssembly.IsFrameworkAssembly();
+
+#if FEATURE_CORECLR
+ if (!fUseSatelliteAssemblyResourceLookupUnderAppX)
+ {
+ // Check to see if the assembly is under PLATFORM_RESOURCE_ROOTS. If it is, then we should use satellite assembly lookup for it.
+ String platformResourceRoots = (String)(AppDomain.CurrentDomain.GetData("PLATFORM_RESOURCE_ROOTS"));
+ if ((platformResourceRoots != null) && (platformResourceRoots != String.Empty))
+ {
+ string resourceAssemblyPath = resourcesAssembly.Location;
+
+ // Loop through the PLATFORM_RESOURCE_ROOTS and see if the assembly is contained in it.
+ foreach(string pathPlatformResourceRoot in platformResourceRoots.Split(Path.PathSeparator))
+ {
+ if (resourceAssemblyPath.StartsWith(pathPlatformResourceRoot, StringComparison.CurrentCultureIgnoreCase))
+ {
+ // Found the resource assembly to be present in one of the PLATFORM_RESOURCE_ROOT, so stop the enumeration loop.
+ fUseSatelliteAssemblyResourceLookupUnderAppX = true;
+ break;
+ }
+ }
+ }
+ }
+#endif // FEATURE_CORECLR
+ return fUseSatelliteAssemblyResourceLookupUnderAppX;
+
+ }
+
+ [SecuritySafeCritical]
+#endif // FEATURE_APPX
+ // Only call SetAppXConfiguration from ResourceManager constructors, and nowhere else.
+ // Throws MissingManifestResourceException and WinRT HResults
+
+ private void SetAppXConfiguration()
+ {
+ Contract.Assert(_bUsingModernResourceManagement == false); // Only this function writes to this member
+#if FEATURE_APPX
+ Contract.Assert(_WinRTResourceManager == null); // Only this function writes to this member
+ Contract.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member
+ Contract.Assert(_PRIExceptionInfo == null); // Only this function writes to this member
+
+ bool bUsingSatelliteAssembliesUnderAppX = false;
+
+ RuntimeAssembly resourcesAssembly = (RuntimeAssembly)MainAssembly;
+
+ if (resourcesAssembly == null)
+ resourcesAssembly = m_callingAssembly;
+
+ if (resourcesAssembly != null)
+ {
+ if (resourcesAssembly != typeof(Object).Assembly) // We are not loading resources for mscorlib
+ {
+ // Cannot load the WindowsRuntimeResourceManager when in a compilation process, since it
+ // lives in System.Runtime.WindowsRuntime and only mscorlib may be loaded for execution.
+ if (AppDomain.IsAppXModel() && !AppDomain.IsAppXNGen)
+ {
+ s_IsAppXModel = true;
+
+ // If we have the type information from the ResourceManager(Type) constructor, we use it. Otherwise, we use BaseNameField.
+ String reswFilename = _locationInfo == null ? BaseNameField : _locationInfo.FullName;
+
+ // The only way this can happen is if a class inherited from ResourceManager and
+ // did not set the BaseNameField before calling the protected ResourceManager() constructor.
+ // For other constructors, we would already have thrown an ArgumentNullException by now.
+ // Throwing an ArgumentNullException now is not the right thing to do because technically
+ // ResourceManager() takes no arguments, and because it is not documented as throwing
+ // any exceptions. Instead, let's go through the rest of the initialization with this set to
+ // an empty string. We may in fact fail earlier for another reason, but otherwise we will
+ // throw a MissingManifestResourceException when GetString is called indicating that a
+ // resW filename called "" could not be found.
+ if (reswFilename == null)
+ reswFilename = String.Empty;
+
+ WindowsRuntimeResourceManagerBase WRRM = null;
+ bool bWRRM_Initialized = false;
+
+ if (AppDomain.IsAppXDesignMode())
+ {
+ WRRM = GetWinRTResourceManager();
+ try {
+ PRIExceptionInfo exceptionInfo; // If the exception info is filled in, we will ignore it.
+ bWRRM_Initialized = WRRM.Initialize(resourcesAssembly.Location, reswFilename, out exceptionInfo);
+ bUsingSatelliteAssembliesUnderAppX = !bWRRM_Initialized;
+ }
+ catch(Exception e)
+ {
+ bUsingSatelliteAssembliesUnderAppX = true;
+ if (e.IsTransient)
+ throw;
+ }
+ }
+
+ if (!bUsingSatelliteAssembliesUnderAppX)
+ {
+ // See AssemblyNative::IsFrameworkAssembly for details on which kinds of assemblies are considered Framework assemblies.
+ // The Modern Resource Manager is not used for such assemblies - they continue to use satellite assemblies (i.e. .resources.dll files).
+ _bUsingModernResourceManagement = !ShouldUseSatelliteAssemblyResourceLookupUnderAppX(resourcesAssembly);
+
+ if (_bUsingModernResourceManagement)
+ {
+ // Only now are we certain that we need the PRI file.
+
+ // Note that if IsAppXDesignMode is false, we haven't checked if the PRI file exists.
+ // This is by design. We will find out in the call to WindowsRuntimeResourceManager.Initialize below.
+
+ // At this point it is important NOT to set _bUsingModernResourceManagement to false
+ // if the PRI file does not exist because we are now certain we need to load PRI
+ // resources. We want to fail by throwing a MissingManifestResourceException
+ // if WindowsRuntimeResourceManager.Initialize fails to locate the PRI file. We do not
+ // want to fall back to using satellite assemblies anymore. Note that we would not throw
+ // the MissingManifestResourceException from this function, but from GetString. See the
+ // comment below on the reason for this.
+
+ if (WRRM != null && bWRRM_Initialized)
+ {
+ // Reuse the one successfully created earlier
+ _WinRTResourceManager = WRRM;
+ _PRIonAppXInitialized = true;
+ }
+ else
+ {
+ _WinRTResourceManager = GetWinRTResourceManager();
+
+ try {
+ _PRIonAppXInitialized = _WinRTResourceManager.Initialize(resourcesAssembly.Location, reswFilename, out _PRIExceptionInfo);
+
+ // Note that _PRIExceptionInfo might be null - this is OK.
+ // In that case we will just throw the generic
+ // MissingManifestResource_NoPRIresources exception.
+ // See the implementation of GetString for more details.
+ }
+ // We would like to be able to throw a MissingManifestResourceException here if PRI resources
+ // could not be loaded for a recognized reason. However, the ResourceManager constructors
+ // that call SetAppXConfiguration are not documented as throwing MissingManifestResourceException,
+ // and since they are part of the portable profile, we cannot start throwing a new exception type
+ // as that would break existing portable libraries. Hence we must save the exception information
+ // now and throw the exception on the first call to GetString.
+ catch(FileNotFoundException)
+ {
+ // We will throw MissingManifestResource_NoPRIresources from GetString
+ // when we see that _PRIonAppXInitialized is false.
+ }
+ catch(Exception e)
+ {
+ // ERROR_MRM_MAP_NOT_FOUND can be thrown by the call to ResourceManager.get_AllResourceMaps
+ // in WindowsRuntimeResourceManager.Initialize.
+ // In this case _PRIExceptionInfo is now null and we will just throw the generic
+ // MissingManifestResource_NoPRIresources exception.
+ // See the implementation of GetString for more details.
+ if (e.HResult != __HResults.ERROR_MRM_MAP_NOT_FOUND)
+ throw; // Unexpected exception code. Bubble it up to the caller.
+ }
+ // Allow all other exception types to bubble up to the caller.
+
+ // Yes, this causes us to potentially throw exception types that are not documented.
+
+ // Ultimately the tradeoff is the following:
+ // -We could ignore unknown exceptions or rethrow them as inner exceptions
+ // of exceptions that the ResourceManager class is already documented as throwing.
+ // This would allow existing portable libraries to gracefully recover if they don't care
+ // too much about the ResourceManager object they are using. However it could
+ // mask potentially fatal errors that we are not aware of, such as a disk drive failing.
+
+
+ // The alternative, which we chose, is to throw unknown exceptions. This may tear
+ // down the process if the portable library and app don't expect this exception type.
+ // On the other hand, this won't mask potentially fatal errors we don't know about.
+ }
+ }
+ }
+ }
+ }
+ }
+ // resourcesAssembly == null should not happen but it can. See the comment on Assembly.GetCallingAssembly.
+ // However for the sake of 100% backwards compatibility on Win7 and below, we must leave
+ // _bUsingModernResourceManagement as false.
+#endif // FEATURE_APPX
+ }
+
+ // Looks up a resource value for a particular name. Looks in the
+ // current thread's CultureInfo, and if not found, all parent CultureInfos.
+ // Returns null if the resource wasn't found.
+ //
+ public virtual String GetString(String name) {
+ return GetString(name, (CultureInfo)null);
+ }
+
+ // Looks up a resource value for a particular name. Looks in the
+ // specified CultureInfo, and if not found, all parent CultureInfos.
+ // Returns null if the resource wasn't found.
+ //
+ public virtual String GetString(String name, CultureInfo culture) {
+ if (null==name)
+ throw new ArgumentNullException("name");
+ Contract.EndContractBlock();
+
+#if FEATURE_APPX
+ if(s_IsAppXModel)
+ {
+ // If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
+ // null it out, so that we re-compute it. If we use modern resource lookup, we may end up getting a "better"
+ // match, since CultureInfo objects can't represent all the different languages the AppX resource model supports.
+ // For classic resources, this causes us to ignore the languages list and instead use the older Win32 behavior,
+ // which is the design choice we've made. (See the call a little later to GetCurrentUICultureNoAppX()).
+ if(Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
+ {
+ culture = null;
+ }
+ }
+
+ if (_bUsingModernResourceManagement)
+ {
+ if (_PRIonAppXInitialized == false)
+ {
+ // Always throw if we did not fully succeed in initializing the WinRT Resource Manager.
+
+ if (_PRIExceptionInfo != null && _PRIExceptionInfo._PackageSimpleName != null && _PRIExceptionInfo._ResWFile != null)
+ throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_ResWFileNotLoaded", _PRIExceptionInfo._ResWFile, _PRIExceptionInfo._PackageSimpleName));
+
+ throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoPRIresources"));
+ }
+
+ // Throws WinRT hresults.
+ return GetStringFromPRI(name,
+ culture == null ? null : culture.Name,
+ _neutralResourcesCulture.Name);
+ }
+ else
+#endif // FEATURE_APPX
+ {
+ if (null==culture) {
+ // When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
+ // This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
+ culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
+ }
+
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerLookupStarted(BaseNameField, MainAssembly, culture.Name);
+ }
+#endif
+ ResourceSet last = GetFirstResourceSet(culture);
+
+ if (last != null)
+ {
+ String value = last.GetString(name, _ignoreCase);
+ if (value != null)
+ return value;
+ }
+
+
+ // This is the CultureInfo hierarchy traversal code for resource
+ // lookups, similar but necessarily orthogonal to the ResourceSet
+ // lookup logic.
+ ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
+ foreach (CultureInfo currentCultureInfo in mgr) {
+
+ ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
+ if (rs == null)
+ break;
+
+ if (rs != last) {
+ String value = rs.GetString(name, _ignoreCase);
+ if (value != null)
+ {
+ // update last used ResourceSet
+ if (_lastUsedResourceCache != null) {
+ lock (_lastUsedResourceCache) {
+ _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
+ _lastUsedResourceCache.lastResourceSet = rs;
+ }
+ }
+ return value;
+ }
+
+ last = rs;
+ }
+ }
+
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerLookupFailed(BaseNameField, MainAssembly, culture.Name);
+ }
+#endif
+ }
+
+ return null;
+ }
+
+
+ // Looks up a resource value for a particular name. Looks in the
+ // current thread's CultureInfo, and if not found, all parent CultureInfos.
+ // Returns null if the resource wasn't found.
+ //
+ public virtual Object GetObject(String name) {
+ return GetObject(name, (CultureInfo)null, true);
+ }
+
+ // Looks up a resource value for a particular name. Looks in the
+ // specified CultureInfo, and if not found, all parent CultureInfos.
+ // Returns null if the resource wasn't found.
+ public virtual Object GetObject(String name, CultureInfo culture) {
+ return GetObject(name, culture, true);
+ }
+
+ private Object GetObject(String name, CultureInfo culture, bool wrapUnmanagedMemStream)
+ {
+ if (null==name)
+ throw new ArgumentNullException("name");
+ Contract.EndContractBlock();
+
+#if FEATURE_APPX
+ if(s_IsAppXModel)
+ {
+ // If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
+ // null it out, so that we re-compute it based on the Win32 value and not the AppX language list value.
+ // (See the call a little later to GetCurrentUICultureNoAppX()).
+ if(Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
+ {
+ culture = null;
+ }
+ }
+#endif
+
+ if (null==culture) {
+ // When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
+ // This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
+ culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
+ }
+
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerLookupStarted(BaseNameField, MainAssembly, culture.Name);
+ }
+#endif
+ ResourceSet last = GetFirstResourceSet(culture);
+ if (last != null)
+ {
+ Object value = last.GetObject(name, _ignoreCase);
+
+ if (value != null)
+ {
+ UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
+ if (stream != null && wrapUnmanagedMemStream)
+ return new UnmanagedMemoryStreamWrapper(stream);
+ else
+ return value;
+ }
+ }
+
+ // This is the CultureInfo hierarchy traversal code for resource
+ // lookups, similar but necessarily orthogonal to the ResourceSet
+ // lookup logic.
+ ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
+
+ foreach (CultureInfo currentCultureInfo in mgr) {
+ // Note: Technically this method should be passed in a stack crawl mark that we then pass
+ // to InternalGetResourceSet for ensuring we demand permissions to read your private resources
+ // if you're reading resources from an assembly other than yourself. But, we must call our
+ // three argument overload (without the stack crawl mark) for compatibility. After
+ // consideration, we aren't worried about the security impact.
+ ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
+ if (rs == null)
+ break;
+
+ if (rs != last) {
+ Object value = rs.GetObject(name, _ignoreCase);
+ if (value != null) {
+ // update the last used ResourceSet
+ if (_lastUsedResourceCache != null) {
+ lock (_lastUsedResourceCache) {
+ _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
+ _lastUsedResourceCache.lastResourceSet = rs;
+ }
+ }
+
+ UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
+ if (stream != null && wrapUnmanagedMemStream)
+ return new UnmanagedMemoryStreamWrapper(stream);
+ else
+ return value;
+ }
+
+ last = rs;
+ }
+ }
+
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerLookupFailed(BaseNameField, MainAssembly, culture.Name);
+ }
+#endif
+ return null;
+ }
+
+ [ComVisible(false)]
+ public UnmanagedMemoryStream GetStream(String name) {
+ return GetStream(name, (CultureInfo)null);
+ }
+
+ [ComVisible(false)]
+ public UnmanagedMemoryStream GetStream(String name, CultureInfo culture) {
+ Object obj = GetObject(name, culture, false);
+ UnmanagedMemoryStream ums = obj as UnmanagedMemoryStream;
+ if (ums == null && obj != null)
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotStream_Name", name));
+ return ums;
+ }
+
+#if RESOURCE_SATELLITE_CONFIG
+ // Internal helper method - gives an end user the ability to prevent
+ // satellite assembly probes for certain cultures via a config file.
+ [System.Security.SecurityCritical] // auto-generated
+ private bool TryLookingForSatellite(CultureInfo lookForCulture)
+ {
+ if (!_checkedConfigFile) {
+ lock (this) {
+ if (!_checkedConfigFile) {
+ _checkedConfigFile = true;
+ _installedSatelliteInfo = GetSatelliteAssembliesFromConfig();
+ }
+ }
+ }
+
+ if (_installedSatelliteInfo == null)
+ return true;
+
+ String[] installedSatellites = (String[]) _installedSatelliteInfo[MainAssembly.FullName];
+
+ if (installedSatellites == null)
+ return true;
+
+ // The config file told us what satellites might be installed.
+ int pos = Array.IndexOf(installedSatellites, lookForCulture.Name);
+
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
+ if (pos < 0) {
+ FrameworkEventSource.Log.ResourceManagerCultureNotFoundInConfigFile(BaseNameField, MainAssembly, lookForCulture.Name);
+ }
+ else {
+ FrameworkEventSource.Log.ResourceManagerCultureFoundInConfigFile(BaseNameField, MainAssembly, lookForCulture.Name);
+ }
+ }
+#endif
+ return pos >= 0;
+ }
+
+ // Note: There is one config file per appdomain. This is not
+ // per-process nor per-assembly.
+ [System.Security.SecurityCritical] // auto-generated
+ private Hashtable GetSatelliteAssembliesFromConfig()
+ {
+#if FEATURE_FUSION
+
+ String fileName = AppDomain.CurrentDomain.FusionStore.ConfigurationFileInternal;
+ if (fileName == null) {
+ return null;
+ }
+
+ // Don't do a security assert. We need to support semi-trusted
+ // scenarios, but asserting here causes infinite resource lookups
+ // while initializing security & looking up mscorlib's config file.
+ // Use internal methods to bypass security checks.
+
+ // If we're dealing with a local file name or a UNC path instead
+ // of a URL, check to see if the file exists here for perf (avoids
+ // throwing a FileNotFoundException).
+ if (fileName.Length >= 2 &&
+ ((fileName[1] == Path.VolumeSeparatorChar) || (fileName[0] == Path.DirectorySeparatorChar && fileName[1] == Path.DirectorySeparatorChar)) &&
+ !File.InternalExists(fileName))
+ return null;
+
+ ConfigTreeParser parser = new ConfigTreeParser();
+ String queryPath = "/configuration/satelliteassemblies";
+ ConfigNode node = null;
+ // Catch exceptions in case a web app doesn't have a config file.
+ try {
+ node = parser.Parse(fileName, queryPath, true);
+ }
+ catch(Exception) {}
+
+ if (node == null) {
+ return null;
+ }
+
+ // The application config file will contain sections like this:
+ // <?xml version="1.0"?>
+ // <configuration>
+ // <satelliteassemblies>
+ // <assembly name="mscorlib, Version=..., PublicKeyToken=...">
+ // <culture>fr</culture>
+ // </assembly>
+ // <assembly name="UserAssembly, ...">
+ // <culture>fr-FR</culture>
+ // <culture>de-CH</culture>
+ // </assembly>
+ // <assembly name="UserAssembly2, ...">
+ // </assembly>
+ // </satelliteassemblies>
+ // </configuration>
+ Hashtable satelliteInfo = new Hashtable(StringComparer.OrdinalIgnoreCase);
+ foreach(ConfigNode assemblyNode in node.Children) {
+ if (!String.Equals(assemblyNode.Name, "assembly"))
+ throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTag", Path.GetFileName(fileName), assemblyNode.Name));
+
+ if (assemblyNode.Attributes.Count == 0)
+ throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTagNoAttr", Path.GetFileName(fileName)));
+
+ DictionaryEntry de = (DictionaryEntry) assemblyNode.Attributes[0];
+ String assemblyName = (String) de.Value;
+ if (!String.Equals(de.Key, "name") || String.IsNullOrEmpty(assemblyName) || assemblyNode.Attributes.Count > 1)
+ throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTagBadAttr", Path.GetFileName(fileName), de.Key, de.Value));
+
+ ArrayList list = new ArrayList(5);
+ foreach(ConfigNode child in assemblyNode.Children)
+ if (child.Value != null)
+ list.Add(child.Value);
+
+ String[] satellites = new String[list.Count];
+ for(int i=0; i<satellites.Length; i++) {
+ String cultureName = (String)list[i];
+ satellites[i] = cultureName;
+#if !FEATURE_CORECLR
+ if (FrameworkEventSource.IsInitialized)
+ {
+ FrameworkEventSource.Log.ResourceManagerAddingCultureFromConfigFile(BaseNameField, MainAssembly, cultureName);
+ }
+#endif
+ }
+
+ satelliteInfo.Add(assemblyName, satellites);
+ }
+
+ return satelliteInfo;
+#else
+ return null;
+#endif //FEATURE_FUSION
+
+ }
+#endif // RESOURCE_SATELLITE_CONFIG
+
+ internal class ResourceManagerMediator
+ {
+ private ResourceManager _rm;
+
+ internal ResourceManagerMediator(ResourceManager rm)
+ {
+ if (rm == null)
+ {
+ throw new ArgumentNullException("rm");
+ }
+ _rm = rm;
+ }
+
+ // NEEDED ONLY BY FILE-BASED
+ internal String ModuleDir
+ {
+ get { return _rm.moduleDir; }
+ }
+
+ // NEEDED BOTH BY FILE-BASED AND ASSEMBLY-BASED
+ internal Type LocationInfo
+ {
+ get { return _rm._locationInfo; }
+ }
+
+ internal Type UserResourceSet
+ {
+ get { return _rm._userResourceSet; }
+ }
+
+ internal String BaseNameField
+ {
+ get { return _rm.BaseNameField; }
+ }
+
+ internal CultureInfo NeutralResourcesCulture
+ {
+ get { return _rm._neutralResourcesCulture; }
+ set { _rm._neutralResourcesCulture = value; }
+ }
+
+ internal String GetResourceFileName(CultureInfo culture)
+ {
+ return _rm.GetResourceFileName(culture);
+ }
+
+ // NEEDED ONLY BY ASSEMBLY-BASED
+ internal bool LookedForSatelliteContractVersion
+ {
+ get { return _rm._lookedForSatelliteContractVersion; }
+ set { _rm._lookedForSatelliteContractVersion = value; }
+ }
+
+ internal Version SatelliteContractVersion
+ {
+ get { return _rm._satelliteContractVersion; }
+ set { _rm._satelliteContractVersion = value; }
+ }
+
+ internal Version ObtainSatelliteContractVersion(Assembly a)
+ {
+ return ResourceManager.GetSatelliteContractVersion(a);
+ }
+
+ internal UltimateResourceFallbackLocation FallbackLoc
+ {
+ get { return _rm.FallbackLocation; }
+ set { _rm._fallbackLoc = value; }
+ }
+
+ internal RuntimeAssembly CallingAssembly
+ {
+ get { return _rm.m_callingAssembly; }
+ }
+
+ internal RuntimeAssembly MainAssembly
+ {
+ get { return (RuntimeAssembly)_rm.MainAssembly; }
+ }
+
+ // this is weird because we have BaseNameField accessor above, but we're sticking
+ // with it for compat.
+ internal String BaseName
+ {
+ get { return _rm.BaseName; }
+ }
+
+
+#if RESOURCE_SATELLITE_CONFIG
+ [System.Security.SecurityCritical] // auto-generated
+ internal bool TryLookingForSatellite(CultureInfo lookForCulture)
+ {
+ return _rm.TryLookingForSatellite(lookForCulture);
+ }
+#endif
+
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/ResourceReader.cs b/src/mscorlib/src/System/Resources/ResourceReader.cs
new file mode 100644
index 0000000000..a269d5c5fe
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/ResourceReader.cs
@@ -0,0 +1,1424 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Default way to read streams of resources on
+** demand.
+**
+** Version 2 support on October 6, 2003
+**
+===========================================================*/
+namespace System.Resources {
+ using System;
+ using System.IO;
+ using System.Text;
+ using System.Collections;
+ using System.Collections.Generic;
+#if FEATURE_SERIALIZATION
+ using System.Runtime.Serialization;
+ using System.Runtime.Serialization.Formatters;
+ using System.Runtime.Serialization.Formatters.Binary;
+#endif // FEATURE_SERIALIZATION
+ using System.Reflection;
+ using System.Security.Permissions;
+ using System.Security;
+ using System.Globalization;
+ using System.Configuration.Assemblies;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+
+ // Provides the default implementation of IResourceReader, reading
+ // .resources file from the system default binary format. This class
+ // can be treated as an enumerator once.
+ //
+ // See the RuntimeResourceSet overview for details on the system
+ // default file format.
+ //
+
+ internal struct ResourceLocator
+ {
+ internal Object _value; // Can be null. Consider WeakReference instead?
+ internal int _dataPos;
+
+ internal ResourceLocator(int dataPos, Object value)
+ {
+ _dataPos = dataPos;
+ _value = value;
+ }
+
+ internal int DataPosition {
+ get { return _dataPos; }
+ //set { _dataPos = value; }
+ }
+
+ // Allows adding in profiling data in a future version, or a special
+ // resource profiling build. We could also use WeakReference.
+ internal Object Value {
+ get { return _value; }
+ set { _value = value; }
+ }
+
+ internal static bool CanCache(ResourceTypeCode value)
+ {
+ Contract.Assert(value >= 0, "negative ResourceTypeCode. What?");
+ return value <= ResourceTypeCode.LastPrimitive;
+ }
+ }
+
+
+ [System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class ResourceReader : IResourceReader
+ {
+ // A reasonable default buffer size for reading from files, especially
+ // when we will likely be seeking frequently. Could be smaller, but does
+ // it make sense to use anything less than one page?
+ private const int DefaultFileStreamBufferSize = 4096;
+
+ private BinaryReader _store; // backing store we're reading from.
+ // Used by RuntimeResourceSet and this class's enumerator. Maps
+ // resource name to a value, a ResourceLocator, or a
+ // LooselyLinkedManifestResource.
+ internal Dictionary<String, ResourceLocator> _resCache;
+ private long _nameSectionOffset; // Offset to name section of file.
+ private long _dataSectionOffset; // Offset to Data section of file.
+
+ // Note this class is tightly coupled with UnmanagedMemoryStream.
+ // At runtime when getting an embedded resource from an assembly,
+ // we're given an UnmanagedMemoryStream referring to the mmap'ed portion
+ // of the assembly. The pointers here are pointers into that block of
+ // memory controlled by the OS's loader.
+ private int[] _nameHashes; // hash values for all names.
+ [SecurityCritical]
+ private unsafe int* _nameHashesPtr; // In case we're using UnmanagedMemoryStream
+ private int[] _namePositions; // relative locations of names
+ [SecurityCritical]
+ private unsafe int* _namePositionsPtr; // If we're using UnmanagedMemoryStream
+ private RuntimeType[] _typeTable; // Lazy array of Types for resource values.
+ private int[] _typeNamePositions; // To delay initialize type table
+#if FEATURE_SERIALIZATION
+ private BinaryFormatter _objFormatter; // Deserialization stuff.
+#endif // FEATURE_SERIALIZATION
+ private int _numResources; // Num of resources files, in case arrays aren't allocated.
+
+ // We'll include a separate code path that uses UnmanagedMemoryStream to
+ // avoid allocating String objects and the like.
+ private UnmanagedMemoryStream _ums;
+
+ // Version number of .resources file, for compatibility
+ private int _version;
+
+#if RESOURCE_FILE_FORMAT_DEBUG
+ private bool _debug; // Whether this file has debugging stuff in it.
+#endif
+
+#if FEATURE_SERIALIZATION
+ private bool[] _safeToDeserialize; // Whether to assert serialization permission
+ private TypeLimitingDeserializationBinder _typeLimitingBinder;
+
+ // One of our goals is to make sure localizable Windows Forms apps
+ // work in semi-trusted scenarios (ie, without serialization permission).
+ // Unfortunate we're serializing out some complex types that currently
+ // require a security check on deserialization. We may fix this
+ // in a next version, but for now just hard-code a list.
+ // Hard-code in the assembly name (minus version) so people can't spoof us.
+ private static readonly String[] TypesSafeForDeserialization = {
+ "System.String[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.DateTime[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.Bitmap, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.Imaging.Metafile, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.Point, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.PointF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.Size, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.SizeF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.Font, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.Icon, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Drawing.Color, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
+ "System.Windows.Forms.Cursor, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.Padding, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.LinkArea, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.ImageListStreamer, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.ListViewGroup, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.ListViewItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.ListViewItem+ListViewSubItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.ListViewItem+ListViewSubItem+SubItemStyle, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.OwnerDrawPropertyBag, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
+ "System.Windows.Forms.TreeNode, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken
+ };
+#endif // FEATURE_SERIALIZATION
+
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+ public ResourceReader(String fileName)
+ {
+ _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
+ _store = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.RandomAccess, Path.GetFileName(fileName), false), Encoding.UTF8);
+ BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(String). UnmanagedMemoryStream: "+(_ums!=null));
+
+ try {
+ ReadResources();
+ }
+ catch {
+ _store.Close(); // If we threw an exception, close the file.
+ throw;
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+ public ResourceReader(Stream stream)
+ {
+ if (stream==null)
+ throw new ArgumentNullException("stream");
+ if (!stream.CanRead)
+ throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
+ Contract.EndContractBlock();
+
+ _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
+ _store = new BinaryReader(stream, Encoding.UTF8);
+ // We have a faster code path for reading resource files from an assembly.
+ _ums = stream as UnmanagedMemoryStream;
+
+ BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream). UnmanagedMemoryStream: "+(_ums!=null));
+ ReadResources();
+ }
+
+ // This is the constructor the RuntimeResourceSet calls,
+ // passing in the stream to read from and the RuntimeResourceSet's
+ // internal hash table (hash table of names with file offsets
+ // and values, coupled to this ResourceReader).
+ [System.Security.SecurityCritical] // auto-generated
+ internal ResourceReader(Stream stream, Dictionary<String, ResourceLocator> resCache)
+ {
+ Contract.Requires(stream != null, "Need a stream!");
+ Contract.Requires(stream.CanRead, "Stream should be readable!");
+ Contract.Requires(resCache != null, "Need a Dictionary!");
+
+ _resCache = resCache;
+ _store = new BinaryReader(stream, Encoding.UTF8);
+
+ _ums = stream as UnmanagedMemoryStream;
+
+ BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream, Hashtable). UnmanagedMemoryStream: "+(_ums!=null));
+ ReadResources();
+ }
+
+
+ public void Close()
+ {
+ Dispose(true);
+ }
+
+ public void Dispose()
+ {
+ Close();
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private unsafe void Dispose(bool disposing)
+ {
+ if (_store != null) {
+ _resCache = null;
+ if (disposing) {
+ // Close the stream in a thread-safe way. This fix means
+ // that we may call Close n times, but that's safe.
+ BinaryReader copyOfStore = _store;
+ _store = null;
+ if (copyOfStore != null)
+ copyOfStore.Close();
+ }
+ _store = null;
+ _namePositions = null;
+ _nameHashes = null;
+ _ums = null;
+ _namePositionsPtr = null;
+ _nameHashesPtr = null;
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal static unsafe int ReadUnalignedI4(int* p)
+ {
+ byte* buffer = (byte*)p;
+ // Unaligned, little endian format
+ return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+ }
+
+ private void SkipInt32() {
+ _store.BaseStream.Seek(4, SeekOrigin.Current);
+ }
+
+
+ private void SkipString() {
+ int stringLength = _store.Read7BitEncodedInt();
+ if (stringLength < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
+ }
+ _store.BaseStream.Seek(stringLength, SeekOrigin.Current);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private unsafe int GetNameHash(int index)
+ {
+ Contract.Assert(index >=0 && index < _numResources, "Bad index into hash array. index: "+index);
+ Contract.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) ||
+ (_ums != null && _nameHashes == null && _nameHashesPtr != null), "Internal state mangled.");
+ if (_ums == null)
+ return _nameHashes[index];
+ else
+ return ReadUnalignedI4(&_nameHashesPtr[index]);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private unsafe int GetNamePosition(int index)
+ {
+ Contract.Assert(index >=0 && index < _numResources, "Bad index into name position array. index: "+index);
+ Contract.Assert((_ums == null && _namePositions != null && _namePositionsPtr == null) ||
+ (_ums != null && _namePositions == null && _namePositionsPtr != null), "Internal state mangled.");
+ int r;
+ if (_ums == null)
+ r = _namePositions[index];
+ else
+ r = ReadUnalignedI4(&_namePositionsPtr[index]);
+ if (r < 0 || r > _dataSectionOffset - _nameSectionOffset) {
+ throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameInvalidOffset", r));
+ }
+ return r;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public IDictionaryEnumerator GetEnumerator()
+ {
+ if (_resCache == null)
+ throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
+ return new ResourceEnumerator(this);
+ }
+
+ internal ResourceEnumerator GetEnumeratorInternal()
+ {
+ return new ResourceEnumerator(this);
+ }
+
+ // From a name, finds the associated virtual offset for the data.
+ // To read the data, seek to _dataSectionOffset + dataPos, then
+ // read the resource type & data.
+ // This does a binary search through the names.
+ internal int FindPosForResource(String name)
+ {
+ Contract.Assert(_store != null, "ResourceReader is closed!");
+ int hash = FastResourceComparer.HashFunction(name);
+ BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+" hash: "+hash.ToString("x", CultureInfo.InvariantCulture));
+ // Binary search over the hashes. Use the _namePositions array to
+ // determine where they exist in the underlying stream.
+ int lo = 0;
+ int hi = _numResources - 1;
+ int index = -1;
+ bool success = false;
+ while (lo <= hi) {
+ index = (lo + hi) >> 1;
+ // Do NOT use subtraction here, since it will wrap for large
+ // negative numbers.
+ int currentHash = GetNameHash(index);
+ int c;
+ if (currentHash == hash)
+ c = 0;
+ else if (currentHash < hash)
+ c = -1;
+ else
+ c = 1;
+ //BCLDebug.Log("RESMGRFILEFORMAT", " Probing index "+index+" lo: "+lo+" hi: "+hi+" c: "+c);
+ if (c == 0) {
+ success = true;
+ break;
+ }
+ if (c < 0)
+ lo = index + 1;
+ else
+ hi = index - 1;
+ }
+ if (!success) {
+#if RESOURCE_FILE_FORMAT_DEBUG
+ String lastReadString;
+ lock(this) {
+ _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(index), SeekOrigin.Begin);
+ lastReadString = _store.ReadString();
+ }
+ BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "FindPosForResource for ", name, " failed. i: ", index, " lo: ", lo, " hi: ", hi, " last read string: \"", lastReadString, '\'');
+#endif
+ return -1;
+ }
+
+ // index is the location in our hash array that corresponds with a
+ // value in the namePositions array.
+ // There could be collisions in our hash function. Check on both sides
+ // of index to find the range of hash values that are equal to the
+ // target hash value.
+ if (lo != index) {
+ lo = index;
+ while (lo > 0 && GetNameHash(lo - 1) == hash)
+ lo--;
+ }
+ if (hi != index) {
+ hi = index;
+ while (hi < _numResources - 1 && GetNameHash(hi + 1) == hash)
+ hi++;
+ }
+
+ lock(this) {
+ for(int i = lo; i<=hi; i++) {
+ _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(i), SeekOrigin.Begin);
+ if (CompareStringEqualsName(name)) {
+ int dataPos = _store.ReadInt32();
+ if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset) {
+ throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataPos));
+ }
+ return dataPos;
+ }
+ }
+ }
+ BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+": Found a hash collision, HOWEVER, neither of these collided values equaled the given string.");
+ return -1;
+ }
+
+ // This compares the String in the .resources file at the current position
+ // with the string you pass in.
+ // Whoever calls this method should make sure that they take a lock
+ // so no one else can cause us to seek in the stream.
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private unsafe bool CompareStringEqualsName(String name)
+ {
+ Contract.Assert(_store != null, "ResourceReader is closed!");
+ int byteLen = _store.Read7BitEncodedInt();
+ if (byteLen < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
+ }
+ if (_ums != null) {
+ byte* bytes = _ums.PositionPointer;
+ // Skip over the data in the Stream, positioning ourselves right after it.
+ _ums.Seek(byteLen, SeekOrigin.Current);
+ if (_ums.Position > _ums.Length) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameTooLong"));
+ }
+
+ // On 64-bit machines, these char*'s may be misaligned. Use a
+ // byte-by-byte comparison instead.
+ //return FastResourceComparer.CompareOrdinal((char*)bytes, byteLen/2, name) == 0;
+ return FastResourceComparer.CompareOrdinal(bytes, byteLen, name) == 0;
+ }
+ else {
+ // This code needs to be fast
+ byte[] bytes = new byte[byteLen];
+ int numBytesToRead = byteLen;
+ while(numBytesToRead > 0) {
+ int n = _store.Read(bytes, byteLen - numBytesToRead, numBytesToRead);
+ if (n == 0)
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
+ numBytesToRead -= n;
+ }
+ return FastResourceComparer.CompareOrdinal(bytes, byteLen/2, name) == 0;
+ }
+ }
+
+ // This is used in the enumerator. The enumerator iterates from 0 to n
+ // of our resources and this returns the resource name for a particular
+ // index. The parameter is NOT a virtual offset.
+ [System.Security.SecurityCritical] // auto-generated
+ private unsafe String AllocateStringForNameIndex(int index, out int dataOffset)
+ {
+ Contract.Assert(_store != null, "ResourceReader is closed!");
+ byte[] bytes;
+ int byteLen;
+ long nameVA = GetNamePosition(index);
+ lock (this) {
+ _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
+ // Can't use _store.ReadString, since it's using UTF-8!
+ byteLen = _store.Read7BitEncodedInt();
+ if (byteLen < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
+ }
+
+ if (_ums != null) {
+ if (_ums.Position > _ums.Length - byteLen)
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesIndexTooLong", index));
+
+ String s = null;
+ char* charPtr = (char*)_ums.PositionPointer;
+#if IA64
+ if (((int)charPtr & 1) != 0) {
+ char[] destArray = new char[byteLen/2];
+ fixed(char* pDest = destArray) {
+ Buffer.Memcpy((byte*)pDest, (byte*)charPtr, byteLen);
+ }
+ s = new String(destArray);
+ }
+ else {
+#endif //IA64
+ s = new String(charPtr, 0, byteLen/2);
+#if IA64
+ }
+#endif //IA64
+ _ums.Position += byteLen;
+ dataOffset = _store.ReadInt32();
+ if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset) {
+ throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataOffset));
+ }
+ return s;
+ }
+
+ bytes = new byte[byteLen];
+ // We must read byteLen bytes, or we have a corrupted file.
+ // Use a blocking read in case the stream doesn't give us back
+ // everything immediately.
+ int count = byteLen;
+ while(count > 0) {
+ int n = _store.Read(bytes, byteLen - count, count);
+ if (n == 0)
+ throw new EndOfStreamException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted_NameIndex", index));
+ count -= n;
+ }
+ dataOffset = _store.ReadInt32();
+ if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset) {
+ throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataOffset));
+ }
+ }
+ return Encoding.Unicode.GetString(bytes, 0, byteLen);
+ }
+
+ // This is used in the enumerator. The enumerator iterates from 0 to n
+ // of our resources and this returns the resource value for a particular
+ // index. The parameter is NOT a virtual offset.
+ private Object GetValueForNameIndex(int index)
+ {
+ Contract.Assert(_store != null, "ResourceReader is closed!");
+ long nameVA = GetNamePosition(index);
+ lock(this) {
+ _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
+ SkipString();
+ //BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex for index: "+index+" skip (name length): "+skip);
+ int dataPos = _store.ReadInt32();
+ if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset) {
+ throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataPos));
+ }
+ BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex: dataPos: "+dataPos);
+ ResourceTypeCode junk;
+ if (_version == 1)
+ return LoadObjectV1(dataPos);
+ else
+ return LoadObjectV2(dataPos, out junk);
+ }
+ }
+
+ // This takes a virtual offset into the data section and reads a String
+ // from that location.
+ // Anyone who calls LoadObject should make sure they take a lock so
+ // no one can cause us to do a seek in here.
+ internal String LoadString(int pos)
+ {
+ Contract.Assert(_store != null, "ResourceReader is closed!");
+ _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
+ String s = null;
+ int typeIndex = _store.Read7BitEncodedInt();
+ if (_version == 1) {
+ if (typeIndex == -1)
+ return null;
+ if (FindType(typeIndex) != typeof(String))
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", FindType(typeIndex).FullName));
+ s = _store.ReadString();
+ }
+ else {
+ ResourceTypeCode typeCode = (ResourceTypeCode) typeIndex;
+ if (typeCode != ResourceTypeCode.String && typeCode != ResourceTypeCode.Null) {
+ String typeString;
+ if (typeCode < ResourceTypeCode.StartOfUserTypes)
+ typeString = typeCode.ToString();
+ else
+ typeString = FindType(typeCode - ResourceTypeCode.StartOfUserTypes).FullName;
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", typeString));
+ }
+ if (typeCode == ResourceTypeCode.String) // ignore Null
+ s = _store.ReadString();
+ }
+ BCLDebug.Log("RESMGRFILEFORMAT", "LoadString("+pos.ToString("x", CultureInfo.InvariantCulture)+" returned "+(s==null ? "[a null string]" : s));
+ return s;
+ }
+
+ // Called from RuntimeResourceSet
+ internal Object LoadObject(int pos)
+ {
+ if (_version == 1)
+ return LoadObjectV1(pos);
+ ResourceTypeCode typeCode;
+ return LoadObjectV2(pos, out typeCode);
+ }
+
+ internal Object LoadObject(int pos, out ResourceTypeCode typeCode)
+ {
+ if (_version == 1) {
+ Object o = LoadObjectV1(pos);
+ typeCode = (o is String) ? ResourceTypeCode.String : ResourceTypeCode.StartOfUserTypes;
+ return o;
+ }
+ return LoadObjectV2(pos, out typeCode);
+ }
+
+ // This takes a virtual offset into the data section and reads an Object
+ // from that location.
+ // Anyone who calls LoadObject should make sure they take a lock so
+ // no one can cause us to do a seek in here.
+ internal Object LoadObjectV1(int pos)
+ {
+ Contract.Assert(_store != null, "ResourceReader is closed!");
+ Contract.Assert(_version == 1, ".resources file was not a V1 .resources file!");
+
+ try {
+ // mega try-catch performs exceptionally bad on x64; factored out body into
+ // _LoadObjectV1 and wrap here.
+ return _LoadObjectV1(pos);
+ }
+ catch (EndOfStreamException eof) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), eof);
+ }
+ catch (ArgumentOutOfRangeException e) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), e);
+ }
+ }
+
+#if FEATURE_SERIALIZATION
+ [SecuritySafeCritical]
+#endif
+ private Object _LoadObjectV1(int pos) {
+ _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
+ int typeIndex = _store.Read7BitEncodedInt();
+ if (typeIndex == -1)
+ return null;
+ RuntimeType type = FindType(typeIndex);
+ BCLDebug.Log("RESMGRFILEFORMAT", "LoadObject type: "+type.Name+" pos: 0x"+_store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
+ // Consider putting in logic to see if this type is a
+ // primitive or a value type first, so we can reach the
+ // deserialization code faster for arbitrary objects.
+
+ if (type == typeof(String))
+ return _store.ReadString();
+ else if (type == typeof(Int32))
+ return _store.ReadInt32();
+ else if (type == typeof(Byte))
+ return _store.ReadByte();
+ else if (type == typeof(SByte))
+ return _store.ReadSByte();
+ else if (type == typeof(Int16))
+ return _store.ReadInt16();
+ else if (type == typeof(Int64))
+ return _store.ReadInt64();
+ else if (type == typeof(UInt16))
+ return _store.ReadUInt16();
+ else if (type == typeof(UInt32))
+ return _store.ReadUInt32();
+ else if (type == typeof(UInt64))
+ return _store.ReadUInt64();
+ else if (type == typeof(Single))
+ return _store.ReadSingle();
+ else if (type == typeof(Double))
+ return _store.ReadDouble();
+ else if (type == typeof(DateTime)) {
+ // Ideally we should use DateTime's ToBinary & FromBinary,
+ // but we can't for compatibility reasons.
+ return new DateTime(_store.ReadInt64());
+ }
+ else if (type == typeof(TimeSpan))
+ return new TimeSpan(_store.ReadInt64());
+ else if (type == typeof(Decimal)) {
+ int[] bits = new int[4];
+ for(int i=0; i<bits.Length; i++)
+ bits[i] = _store.ReadInt32();
+ return new Decimal(bits);
+ }
+ else {
+#if FEATURE_SERIALIZATION
+ return DeserializeObject(typeIndex);
+#else
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
+#endif // FEATURE_SERIALIZATION
+ }
+ }
+
+ internal Object LoadObjectV2(int pos, out ResourceTypeCode typeCode)
+ {
+ Contract.Assert(_store != null, "ResourceReader is closed!");
+ Contract.Assert(_version >= 2, ".resources file was not a V2 (or higher) .resources file!");
+
+ try {
+ // mega try-catch performs exceptionally bad on x64; factored out body into
+ // _LoadObjectV2 and wrap here.
+ return _LoadObjectV2(pos, out typeCode);
+ }
+ catch (EndOfStreamException eof) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), eof);
+ }
+ catch (ArgumentOutOfRangeException e) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), e);
+ }
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private Object _LoadObjectV2(int pos, out ResourceTypeCode typeCode) {
+ _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
+ typeCode = (ResourceTypeCode) _store.Read7BitEncodedInt();
+
+ BCLDebug.Log("RESMGRFILEFORMAT", "LoadObjectV2 type: "+typeCode+" pos: 0x"+_store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
+
+ switch(typeCode) {
+ case ResourceTypeCode.Null:
+ return null;
+
+ case ResourceTypeCode.String:
+ return _store.ReadString();
+
+ case ResourceTypeCode.Boolean:
+ return _store.ReadBoolean();
+
+ case ResourceTypeCode.Char:
+ return (char) _store.ReadUInt16();
+
+ case ResourceTypeCode.Byte:
+ return _store.ReadByte();
+
+ case ResourceTypeCode.SByte:
+ return _store.ReadSByte();
+
+ case ResourceTypeCode.Int16:
+ return _store.ReadInt16();
+
+ case ResourceTypeCode.UInt16:
+ return _store.ReadUInt16();
+
+ case ResourceTypeCode.Int32:
+ return _store.ReadInt32();
+
+ case ResourceTypeCode.UInt32:
+ return _store.ReadUInt32();
+
+ case ResourceTypeCode.Int64:
+ return _store.ReadInt64();
+
+ case ResourceTypeCode.UInt64:
+ return _store.ReadUInt64();
+
+ case ResourceTypeCode.Single:
+ return _store.ReadSingle();
+
+ case ResourceTypeCode.Double:
+ return _store.ReadDouble();
+
+ case ResourceTypeCode.Decimal:
+ return _store.ReadDecimal();
+
+ case ResourceTypeCode.DateTime:
+ // Use DateTime's ToBinary & FromBinary.
+ Int64 data = _store.ReadInt64();
+ return DateTime.FromBinary(data);
+
+ case ResourceTypeCode.TimeSpan:
+ Int64 ticks = _store.ReadInt64();
+ return new TimeSpan(ticks);
+
+ // Special types
+ case ResourceTypeCode.ByteArray: {
+ int len = _store.ReadInt32();
+ if (len < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
+ }
+
+ if (_ums == null) {
+ if (len > _store.BaseStream.Length) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
+ }
+ return _store.ReadBytes(len);
+ }
+
+ if (len > _ums.Length - _ums.Position) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
+ }
+
+ byte[] bytes = new byte[len];
+ int r = _ums.Read(bytes, 0, len);
+ Contract.Assert(r == len, "ResourceReader needs to use a blocking read here. (Call _store.ReadBytes(len)?)");
+ return bytes;
+ }
+
+ case ResourceTypeCode.Stream: {
+ int len = _store.ReadInt32();
+ if (len < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
+ }
+ if (_ums == null) {
+ byte[] bytes = _store.ReadBytes(len);
+ // Lifetime of memory == lifetime of this stream.
+ return new PinnedBufferMemoryStream(bytes);
+ }
+
+ // make sure we don't create an UnmanagedMemoryStream that is longer than the resource stream.
+ if (len > _ums.Length - _ums.Position) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
+ }
+
+ // For the case that we've memory mapped in the .resources
+ // file, just return a Stream pointing to that block of memory.
+ unsafe {
+ return new UnmanagedMemoryStream(_ums.PositionPointer, len, len, FileAccess.Read, true);
+ }
+ }
+
+ default:
+ if (typeCode < ResourceTypeCode.StartOfUserTypes) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"));
+ }
+ break;
+ }
+
+ // Normal serialized objects
+#if FEATURE_SERIALIZATION
+ int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
+ return DeserializeObject(typeIndex);
+#else
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
+#endif // FEATURE_SERIALIZATION
+ }
+
+
+#if FEATURE_SERIALIZATION
+ // Helper method to safely deserialize a type, using a type-limiting
+ // deserialization binder to simulate a type-limiting deserializer.
+ // This method handles types that are safe to deserialize, as well as
+ // ensuring we only get back what we expect.
+ [System.Security.SecurityCritical] // auto-generated
+ private Object DeserializeObject(int typeIndex)
+ {
+ RuntimeType type = FindType(typeIndex);
+ // Initialize deserialization permission array, if needed
+ if (_safeToDeserialize == null)
+ InitSafeToDeserializeArray();
+
+ // Ensure that the object we deserialized is exactly the same
+ // type of object we thought we should be deserializing. This
+ // will help prevent malformed .resources files from using our
+ // serialization permission assert to deserialize anything
+ // via a malformed type ID.
+
+ Object graph;
+ if (_safeToDeserialize[typeIndex]) {
+ // Don't assert serialization permission - just ask the binary
+ // formatter to avoid a permission check. This ensures that any
+ // types which do demand serialization permission in their
+ // deserialization .cctors will fail.
+ // Also, use a serialization binder to limit bind requests to
+ // our allowed list of WinForms types.
+ _objFormatter.Binder = _typeLimitingBinder;
+ _typeLimitingBinder.ExpectingToDeserialize(type);
+ graph = _objFormatter.UnsafeDeserialize(_store.BaseStream, null);
+ }
+ else {
+ _objFormatter.Binder = null;
+ graph = _objFormatter.Deserialize(_store.BaseStream);
+ }
+
+ // This check is about correctness, not security at this point.
+ if (graph.GetType() != type)
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", type.FullName, graph.GetType().FullName));
+
+ return graph;
+ }
+#endif // FEATURE_SERIALIZATION
+
+ // Reads in the header information for a .resources file. Verifies some
+ // of the assumptions about this resource set, and builds the class table
+ // for the default resource file format.
+ [System.Security.SecurityCritical] // auto-generated
+ private void ReadResources()
+ {
+ Contract.Assert(_store != null, "ResourceReader is closed!");
+#if FEATURE_SERIALIZATION
+ BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence));
+ _typeLimitingBinder = new TypeLimitingDeserializationBinder();
+ bf.Binder = _typeLimitingBinder;
+ _objFormatter = bf;
+#endif // FEATURE_SERIALIZATION
+
+ try {
+ // mega try-catch performs exceptionally bad on x64; factored out body into
+ // _ReadResources and wrap here.
+ _ReadResources();
+ }
+ catch (EndOfStreamException eof) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"), eof);
+ }
+ catch (IndexOutOfRangeException e) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"), e);
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private void _ReadResources()
+ {
+ // Read ResourceManager header
+ // Check for magic number
+ int magicNum = _store.ReadInt32();
+ if (magicNum != ResourceManager.MagicNumber)
+ throw new ArgumentException(Environment.GetResourceString("Resources_StreamNotValid"));
+ // Assuming this is ResourceManager header V1 or greater, hopefully
+ // after the version number there is a number of bytes to skip
+ // to bypass the rest of the ResMgr header. For V2 or greater, we
+ // use this to skip to the end of the header
+ int resMgrHeaderVersion = _store.ReadInt32();
+ int numBytesToSkip = _store.ReadInt32();
+ if (numBytesToSkip < 0 || resMgrHeaderVersion < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
+ }
+ if (resMgrHeaderVersion > 1) {
+ BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "ReadResources: Unexpected ResMgr header version: {0} Skipping ahead {1} bytes.", resMgrHeaderVersion, numBytesToSkip);
+ _store.BaseStream.Seek(numBytesToSkip, SeekOrigin.Current);
+ }
+ else {
+ BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Parsing ResMgr header v1.");
+ // We don't care about numBytesToSkip; read the rest of the header
+
+ // Read in type name for a suitable ResourceReader
+ // Note ResourceWriter & InternalResGen use different Strings.
+ String readerType = _store.ReadString();
+ readerType = System.CoreLib.FixupCoreLibName(readerType);
+ AssemblyName mscorlib = new AssemblyName(ResourceManager.MscorlibName);
+
+ if (!ResourceManager.CompareNames(readerType, ResourceManager.ResReaderTypeName, mscorlib))
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_WrongResourceReader_Type", readerType));
+
+ // Skip over type name for a suitable ResourceSet
+ SkipString();
+ }
+
+ // Read RuntimeResourceSet header
+ // Do file version check
+ int version = _store.ReadInt32();
+ if (version != RuntimeResourceSet.Version && version != 1)
+ throw new ArgumentException(Environment.GetResourceString("Arg_ResourceFileUnsupportedVersion", RuntimeResourceSet.Version, version));
+ _version = version;
+
+#if RESOURCE_FILE_FORMAT_DEBUG
+ // Look for ***DEBUG*** to see if this is a debuggable file.
+ long oldPos = _store.BaseStream.Position;
+ _debug = false;
+ try {
+ String debugString = _store.ReadString();
+ _debug = String.Equals("***DEBUG***", debugString);
+ }
+ catch(IOException) {
+ }
+ catch(OutOfMemoryException) {
+ }
+ if (_debug) {
+ Console.WriteLine("ResourceReader is looking at a debuggable .resources file, version {0}", _version);
+ }
+ else {
+ _store.BaseStream.Position = oldPos;
+ }
+#endif
+
+ _numResources = _store.ReadInt32();
+ if (_numResources < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
+ }
+ BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Expecting " + _numResources + " resources.");
+#if _DEBUG
+ if (ResourceManager.DEBUG >= 4)
+ Console.WriteLine("ResourceReader::ReadResources - Reading in "+_numResources+" resources");
+#endif
+
+ // Read type positions into type positions array.
+ // But delay initialize the type table.
+ int numTypes = _store.ReadInt32();
+ if (numTypes < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
+ }
+ _typeTable = new RuntimeType[numTypes];
+ _typeNamePositions = new int[numTypes];
+ for (int i=0; i<numTypes; i++) {
+ _typeNamePositions[i] = (int) _store.BaseStream.Position;
+
+ // Skip over the Strings in the file. Don't create types.
+ SkipString();
+ }
+
+#if _DEBUG
+ if (ResourceManager.DEBUG >= 5)
+ Console.WriteLine("ResourceReader::ReadResources - Reading in "+numTypes+" type table entries");
+#endif
+
+ // Prepare to read in the array of name hashes
+ // Note that the name hashes array is aligned to 8 bytes so
+ // we can use pointers into it on 64 bit machines. (4 bytes
+ // may be sufficient, but let's plan for the future)
+ // Skip over alignment stuff. All public .resources files
+ // should be aligned No need to verify the byte values.
+ long pos = _store.BaseStream.Position;
+ int alignBytes = ((int)pos) & 7;
+ if (alignBytes != 0) {
+ for (int i = 0; i < 8 - alignBytes; i++) {
+ _store.ReadByte();
+ }
+ }
+
+ // Read in the array of name hashes
+#if RESOURCE_FILE_FORMAT_DEBUG
+ // Skip over "HASHES->"
+ if (_debug) {
+ _store.BaseStream.Position += 8;
+ }
+#endif
+
+ if (_ums == null) {
+ _nameHashes = new int[_numResources];
+ for (int i = 0; i < _numResources; i++) {
+ _nameHashes[i] = _store.ReadInt32();
+ }
+ }
+ else {
+ int seekPos = unchecked(4 * _numResources);
+ if (seekPos < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
+ }
+ unsafe {
+ _nameHashesPtr = (int*)_ums.PositionPointer;
+ // Skip over the array of nameHashes.
+ _ums.Seek(seekPos, SeekOrigin.Current);
+ // get the position pointer once more to check that the whole table is within the stream
+ byte* junk = _ums.PositionPointer;
+ }
+ }
+
+ // Read in the array of relative positions for all the names.
+#if RESOURCE_FILE_FORMAT_DEBUG
+ // Skip over "POS---->"
+ if (_debug) {
+ _store.BaseStream.Position += 8;
+ }
+#endif
+ if (_ums == null) {
+ _namePositions = new int[_numResources];
+ for (int i = 0; i < _numResources; i++) {
+ int namePosition = _store.ReadInt32();
+ if (namePosition < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
+ }
+
+ _namePositions[i] = namePosition;
+ }
+ }
+ else {
+ int seekPos = unchecked(4 * _numResources);
+ if (seekPos < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
+ }
+ unsafe {
+ _namePositionsPtr = (int*)_ums.PositionPointer;
+ // Skip over the array of namePositions.
+ _ums.Seek(seekPos, SeekOrigin.Current);
+ // get the position pointer once more to check that the whole table is within the stream
+ byte* junk = _ums.PositionPointer;
+ }
+ }
+
+ // Read location of data section.
+ _dataSectionOffset = _store.ReadInt32();
+ if (_dataSectionOffset < 0) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
+ }
+
+ // Store current location as start of name section
+ _nameSectionOffset = _store.BaseStream.Position;
+
+ // _nameSectionOffset should be <= _dataSectionOffset; if not, it's corrupt
+ if (_dataSectionOffset < _nameSectionOffset) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
+ }
+
+ BCLDebug.Log("RESMGRFILEFORMAT", String.Format(CultureInfo.InvariantCulture, "ReadResources: _nameOffset = 0x{0:x} _dataOffset = 0x{1:x}", _nameSectionOffset, _dataSectionOffset));
+ }
+
+ // This allows us to delay-initialize the Type[]. This might be a
+ // good startup time savings, since we might have to load assemblies
+ // and initialize Reflection.
+ private RuntimeType FindType(int typeIndex)
+ {
+ if (typeIndex < 0 || typeIndex >= _typeTable.Length) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_InvalidType"));
+ }
+ if (_typeTable[typeIndex] == null) {
+ long oldPos = _store.BaseStream.Position;
+ try {
+ _store.BaseStream.Position = _typeNamePositions[typeIndex];
+ String typeName = _store.ReadString();
+ _typeTable[typeIndex] = (RuntimeType)Type.GetType(typeName, true);
+ }
+#if !FEATURE_SERIALIZATION
+ // If serialization isn't supported, we convert FileNotFoundException to
+ // NotSupportedException for consistency with v2. This is a corner-case, but the
+ // idea is that we want to give the user a more accurate error message. Even if
+ // the dependency were found, we know it will require serialization since it
+ // can't be one of the types we special case. So if the dependency were found,
+ // it would go down the serialization code path, resulting in NotSupported for
+ // SKUs without serialization.
+ //
+ // We don't want to regress the expected case by checking the type info before
+ // getting to Type.GetType -- this is costly with v1 resource formats.
+ catch (FileNotFoundException)
+ {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
+ }
+#endif // FEATURE_SERIALIZATION
+ finally {
+ _store.BaseStream.Position = oldPos;
+ }
+ }
+ Contract.Assert(_typeTable[typeIndex] != null, "Should have found a type!");
+ return _typeTable[typeIndex];
+ }
+
+#if FEATURE_SERIALIZATION
+ [System.Security.SecurityCritical] // auto-generated
+ private void InitSafeToDeserializeArray()
+ {
+ _safeToDeserialize = new bool[_typeTable.Length];
+ for(int i=0; i<_typeTable.Length; i++) {
+ long oldPos = _store.BaseStream.Position;
+ String typeName;
+ try {
+ _store.BaseStream.Position = _typeNamePositions[i];
+ typeName = _store.ReadString();
+ }
+ finally {
+ _store.BaseStream.Position = oldPos;
+ }
+
+ AssemblyName an;
+ String typePart;
+ RuntimeType resourceType = (RuntimeType)Type.GetType(typeName, false);
+ if (resourceType == null) {
+ an = null;
+ typePart = typeName;
+ }
+ else {
+ // Enums should be safe to deserialize, and this helps out
+ // partially trusted, localized WinForms apps.
+ if (resourceType.BaseType == typeof(Enum)) {
+ _safeToDeserialize[i] = true;
+ continue;
+ }
+
+ // For most types, check our TypesSafeForDeserialization.
+ typePart = resourceType.FullName;
+
+ an = new AssemblyName();
+
+ // resourceType is retrieved via Type.GetType and must be a RuntimeType
+ RuntimeAssembly a = (RuntimeAssembly)resourceType.Assembly;
+ an.Init(a.GetSimpleName(),
+ a.GetPublicKey(),
+ null, // public key token
+ null, // version
+ a.GetLocale(),
+ AssemblyHashAlgorithm.None,
+ AssemblyVersionCompatibility.SameMachine,
+ null, // codebase
+ AssemblyNameFlags.PublicKey,
+ null); // strong name key pair
+ }
+
+ foreach(String safeType in TypesSafeForDeserialization) {
+ if (ResourceManager.CompareNames(safeType, typePart, an)) {
+#if _DEBUG
+ if (ResourceManager.DEBUG >= 7)
+ Console.WriteLine("ResReader: Found a type-safe type to deserialize! Type name: {0}", typeName);
+#endif
+ _safeToDeserialize[i] = true;
+ continue;
+ }
+ }
+
+#if _DEBUG
+ if (ResourceManager.DEBUG >= 7)
+ if (!_safeToDeserialize[i])
+ Console.WriteLine("ResReader: Found a type that wasn't safe to deserialize: {0}", typeName);
+#endif
+ }
+ }
+#endif // FEATURE_SERIALIZATION
+
+ public void GetResourceData(String resourceName, out String resourceType, out byte[] resourceData)
+ {
+ if (resourceName == null)
+ throw new ArgumentNullException("resourceName");
+ Contract.EndContractBlock();
+ if (_resCache == null)
+ throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
+
+ // Get the type information from the data section. Also,
+ // sort all of the data section's indexes to compute length of
+ // the serialized data for this type (making sure to subtract
+ // off the length of the type code).
+ int[] sortedDataPositions = new int[_numResources];
+ int dataPos = FindPosForResource(resourceName);
+ if( dataPos == -1) {
+ throw new ArgumentException(Environment.GetResourceString("Arg_ResourceNameNotExist", resourceName));
+ }
+
+ lock(this) {
+ // Read all the positions of data within the data section.
+ for(int i=0; i<_numResources; i++) {
+ _store.BaseStream.Position = _nameSectionOffset + GetNamePosition(i);
+ // Skip over name of resource
+ int numBytesToSkip = _store.Read7BitEncodedInt();
+ if (numBytesToSkip < 0) {
+ throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameInvalidOffset", numBytesToSkip));
+ }
+ _store.BaseStream.Position += numBytesToSkip;
+
+ int dPos = _store.ReadInt32();
+ if (dPos < 0 || dPos >= _store.BaseStream.Length - _dataSectionOffset) {
+ throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dPos));
+ }
+ sortedDataPositions[i] = dPos;
+ }
+ Array.Sort(sortedDataPositions);
+
+ int index = Array.BinarySearch(sortedDataPositions, dataPos);
+ Contract.Assert(index >= 0 && index < _numResources, "Couldn't find data position within sorted data positions array!");
+ long nextData = (index < _numResources - 1) ? sortedDataPositions[index + 1] + _dataSectionOffset : _store.BaseStream.Length;
+ int len = (int) (nextData - (dataPos + _dataSectionOffset));
+ Contract.Assert(len >= 0 && len <= (int) _store.BaseStream.Length - dataPos + _dataSectionOffset, "Length was negative or outside the bounds of the file!");
+
+ // Read type code then byte[]
+ _store.BaseStream.Position = _dataSectionOffset + dataPos;
+ ResourceTypeCode typeCode = (ResourceTypeCode) _store.Read7BitEncodedInt();
+ if (typeCode < 0 || typeCode >= ResourceTypeCode.StartOfUserTypes + _typeTable.Length) {
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_InvalidType"));
+ }
+ resourceType = TypeNameFromTypeCode(typeCode);
+
+ // The length must be adjusted to subtract off the number
+ // of bytes in the 7 bit encoded type code.
+ len -= (int) (_store.BaseStream.Position - (_dataSectionOffset + dataPos));
+ byte[] bytes = _store.ReadBytes(len);
+ if (bytes.Length != len)
+ throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
+ resourceData = bytes;
+ }
+ }
+
+ private String TypeNameFromTypeCode(ResourceTypeCode typeCode)
+ {
+ Contract.Requires(typeCode >= 0, "can't be negative");
+ if (typeCode < ResourceTypeCode.StartOfUserTypes) {
+ Contract.Assert(!String.Equals(typeCode.ToString(), "LastPrimitive"), "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers.");
+ return "ResourceTypeCode." + typeCode.ToString();
+ }
+ else {
+ int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
+ Contract.Assert(typeIndex >= 0 && typeIndex < _typeTable.Length, "TypeCode is broken or corrupted!");
+ long oldPos = _store.BaseStream.Position;
+ try {
+ _store.BaseStream.Position = _typeNamePositions[typeIndex];
+ return _store.ReadString();
+ }
+ finally {
+ _store.BaseStream.Position = oldPos;
+ }
+ }
+ }
+
+#if FEATURE_SERIALIZATION
+ // We need to build a type-limiting deserializer. We know exactly which
+ // type we want to deserialize, and if someone tells us we have type X
+ // (which might be safe to deserialize) and they give us a serialized
+ // form of type Y, we don't want to run the deserialization constructor
+ // because we've asserted serialization formatter permission. Instead,
+ // limit the binary formatter's type binding to precisely the type we
+ // expect. If they ever don't match, that's a corrupt .resources file.
+ // We also must check the complete object graph to ensure all of the
+ // graph contains safe objects.
+ // Note this is tightly coupled to the BinaryFormatter, since we use
+ // its internal ObjectReader::FastBindToType method, and we had to
+ // change the ObjectReader to register itself with this type.
+ internal sealed class TypeLimitingDeserializationBinder : SerializationBinder
+ {
+ private RuntimeType _typeToDeserialize;
+ // This is tightly coupled with the binary formatter, because we
+ // want to use exactly the same code found in the ObjectReader
+ // to do the lookup, then just give a thumbs up or down based on
+ // a type equality comparison. In the future, we could consider
+ // some better refactoring of this code.
+ private ObjectReader _objectReader;
+
+ internal ObjectReader ObjectReader {
+ get { return _objectReader; }
+ set { _objectReader = value; }
+ }
+
+ internal void ExpectingToDeserialize(RuntimeType type)
+ {
+ _typeToDeserialize = type;
+ }
+
+ [System.Security.SecuritySafeCritical] // overrides transparent public member
+ public override Type BindToType(string assemblyName, string typeName)
+ {
+ // BinaryObjectReader::Bind tries us first, then its own code.
+ // Returning null means let the default binding rules happen.
+ AssemblyName an = new AssemblyName(assemblyName);
+
+ bool safe = false;
+ foreach(String safeType in TypesSafeForDeserialization) {
+ if (ResourceManager.CompareNames(safeType, typeName, an)) {
+ safe = true;
+ break;
+ }
+ }
+
+ // WinForms types may internally use some enums that aren't
+ // on our safe to deserialize list, like Font using FontStyle.
+ Type t = ObjectReader.FastBindToType(assemblyName, typeName);
+ if (t.IsEnum)
+ safe = true;
+
+ if (safe)
+ return null;
+
+ // Throw instead of returning null.
+ // If you're looking at this in a debugger, you've either
+ // got a malformed .resources file on your hands, or WinForms
+ // types have taken a new dependency on another type. Check
+ // whether assemblyName & typeName refer to a trustworthy type,
+ // & consider adding it to the TypesSafeToDeserialize list.
+ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", _typeToDeserialize.FullName, typeName));
+ }
+ }
+#endif // FEATURE_SERIALIZATION
+
+
+ internal sealed class ResourceEnumerator : IDictionaryEnumerator
+ {
+ private const int ENUM_DONE = Int32.MinValue;
+ private const int ENUM_NOT_STARTED = -1;
+
+ private ResourceReader _reader;
+ private bool _currentIsValid;
+ private int _currentName;
+ private int _dataPosition; // cached for case-insensitive table
+
+ internal ResourceEnumerator(ResourceReader reader)
+ {
+ _currentName = ENUM_NOT_STARTED;
+ _reader = reader;
+ _dataPosition = -2;
+ }
+
+ public bool MoveNext()
+ {
+ if (_currentName == _reader._numResources - 1 || _currentName == ENUM_DONE) {
+ _currentIsValid = false;
+ _currentName = ENUM_DONE;
+ return false;
+ }
+ _currentIsValid = true;
+ _currentName++;
+ return true;
+ }
+
+ public Object Key {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get {
+ if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
+ if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
+
+ return _reader.AllocateStringForNameIndex(_currentName, out _dataPosition);
+ }
+ }
+
+ public Object Current {
+ get {
+ return Entry;
+ }
+ }
+
+ // Warning: This requires that you call the Key or Entry property FIRST before calling it!
+ internal int DataPosition {
+ get {
+ return _dataPosition;
+ }
+ }
+
+ public DictionaryEntry Entry {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get {
+ if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
+ if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
+
+ String key;
+ Object value = null;
+ lock (_reader) { // locks should be taken in the same order as in RuntimeResourceSet.GetObject to avoid deadlock
+ lock (_reader._resCache) {
+ key = _reader.AllocateStringForNameIndex(_currentName, out _dataPosition); // AllocateStringForNameIndex could lock on _reader
+ ResourceLocator locator;
+ if (_reader._resCache.TryGetValue(key, out locator)) {
+ value = locator.Value;
+ }
+ if (value == null) {
+ if (_dataPosition == -1)
+ value = _reader.GetValueForNameIndex(_currentName);
+ else
+ value = _reader.LoadObject(_dataPosition);
+ // If enumeration and subsequent lookups happen very
+ // frequently in the same process, add a ResourceLocator
+ // to _resCache here. But WinForms enumerates and
+ // just about everyone else does lookups. So caching
+ // here may bloat working set.
+ }
+ }
+ }
+ return new DictionaryEntry(key, value);
+ }
+ }
+
+ public Object Value {
+ get {
+ if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
+ if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
+ if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
+
+ // Consider using _resCache here, eventually, if
+ // this proves to be an interesting perf scenario.
+ // But mixing lookups and enumerators shouldn't be
+ // particularly compelling.
+ return _reader.GetValueForNameIndex(_currentName);
+ }
+ }
+
+ public void Reset()
+ {
+ if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
+ _currentIsValid = false;
+ _currentName = ENUM_NOT_STARTED;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/ResourceSet.cs b/src/mscorlib/src/System/Resources/ResourceSet.cs
new file mode 100644
index 0000000000..ed40a1237f
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/ResourceSet.cs
@@ -0,0 +1,332 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Culture-specific collection of resources.
+**
+**
+===========================================================*/
+namespace System.Resources {
+ using System;
+ using System.Collections;
+ using System.IO;
+ using System.Globalization;
+ using System.Security.Permissions;
+ using System.Runtime.InteropServices;
+ using System.Reflection;
+ using System.Runtime.Serialization;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+
+ // A ResourceSet stores all the resources defined in one particular CultureInfo.
+ //
+ // The method used to load resources is straightforward - this class
+ // enumerates over an IResourceReader, loading every name and value, and
+ // stores them in a hash table. Custom IResourceReaders can be used.
+ //
+ [Serializable]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public class ResourceSet : IDisposable, IEnumerable
+ {
+ [NonSerialized] protected IResourceReader Reader;
+#if FEATURE_CORECLR
+ internal Hashtable Table;
+#else
+ protected Hashtable Table;
+#endif
+
+ private Hashtable _caseInsensitiveTable; // For case-insensitive lookups.
+
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ [OptionalField]
+ private Assembly _assembly; // For LooselyLinkedResourceReferences
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
+ protected ResourceSet()
+ {
+ // To not inconvenience people subclassing us, we should allocate a new
+ // hashtable here just so that Table is set to something.
+ CommonInit();
+ }
+
+ // For RuntimeResourceSet, ignore the Table parameter - it's a wasted
+ // allocation.
+ internal ResourceSet(bool junk)
+ {
+ }
+
+ // Creates a ResourceSet using the system default ResourceReader
+ // implementation. Use this constructor to open & read from a file
+ // on disk.
+ //
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #endif
+ public ResourceSet(String fileName)
+ {
+ Reader = new ResourceReader(fileName);
+ CommonInit();
+ ReadResources();
+ }
+
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ public ResourceSet(String fileName, Assembly assembly)
+ {
+ Reader = new ResourceReader(fileName);
+ CommonInit();
+ _assembly = assembly;
+ ReadResources();
+ }
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
+ // Creates a ResourceSet using the system default ResourceReader
+ // implementation. Use this constructor to read from an open stream
+ // of data.
+ //
+ [System.Security.SecurityCritical] // auto-generated_required
+ public ResourceSet(Stream stream)
+ {
+ Reader = new ResourceReader(stream);
+ CommonInit();
+ ReadResources();
+ }
+
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ [System.Security.SecurityCritical] // auto_generated_required
+ public ResourceSet(Stream stream, Assembly assembly)
+ {
+ Reader = new ResourceReader(stream);
+ CommonInit();
+ _assembly = assembly;
+ ReadResources();
+ }
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
+ public ResourceSet(IResourceReader reader)
+ {
+ if (reader == null)
+ throw new ArgumentNullException("reader");
+ Contract.EndContractBlock();
+ Reader = reader;
+ CommonInit();
+ ReadResources();
+ }
+
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ public ResourceSet(IResourceReader reader, Assembly assembly)
+ {
+ if (reader == null)
+ throw new ArgumentNullException("reader");
+ Contract.EndContractBlock();
+ Reader = reader;
+ CommonInit();
+ _assembly = assembly;
+ ReadResources();
+ }
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
+ private void CommonInit()
+ {
+ Table = new Hashtable();
+ }
+
+ // Closes and releases any resources used by this ResourceSet, if any.
+ // All calls to methods on the ResourceSet after a call to close may
+ // fail. Close is guaranteed to be safely callable multiple times on a
+ // particular ResourceSet, and all subclasses must support these semantics.
+ public virtual void Close()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing) {
+ // Close the Reader in a thread-safe way.
+ IResourceReader copyOfReader = Reader;
+ Reader = null;
+ if (copyOfReader != null)
+ copyOfReader.Close();
+ }
+ Reader = null;
+ _caseInsensitiveTable = null;
+ Table = null;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ // Optional - used for resolving assembly manifest resource references.
+ // This can safely be null.
+ [ComVisible(false)]
+ public Assembly Assembly {
+ get { return _assembly; }
+ /*protected*/ set { _assembly = value; }
+ }
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
+ // Returns the preferred IResourceReader class for this kind of ResourceSet.
+ // Subclasses of ResourceSet using their own Readers &; should override
+ // GetDefaultReader and GetDefaultWriter.
+ public virtual Type GetDefaultReader()
+ {
+ return typeof(ResourceReader);
+ }
+
+#if !FEATURE_CORECLR
+ // Returns the preferred IResourceWriter class for this kind of ResourceSet.
+ // Subclasses of ResourceSet using their own Readers &; should override
+ // GetDefaultReader and GetDefaultWriter.
+ public virtual Type GetDefaultWriter()
+ {
+ return typeof(ResourceWriter);
+ }
+#endif // !FEATURE_CORECLR
+
+ [ComVisible(false)]
+ public virtual IDictionaryEnumerator GetEnumerator()
+ {
+ return GetEnumeratorHelper();
+ }
+
+ /// <internalonly/>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumeratorHelper();
+ }
+
+ private IDictionaryEnumerator GetEnumeratorHelper()
+ {
+ Hashtable copyOfTable = Table; // Avoid a race with Dispose
+ if (copyOfTable == null)
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
+ return copyOfTable.GetEnumerator();
+ }
+
+ // Look up a string value for a resource given its name.
+ //
+ public virtual String GetString(String name)
+ {
+ Object obj = GetObjectInternal(name);
+ try {
+ return (String)obj;
+ }
+ catch (InvalidCastException) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
+ }
+ }
+
+ public virtual String GetString(String name, bool ignoreCase)
+ {
+ Object obj;
+ String s;
+
+ // Case-sensitive lookup
+ obj = GetObjectInternal(name);
+ try {
+ s = (String)obj;
+ }
+ catch (InvalidCastException) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
+ }
+
+ // case-sensitive lookup succeeded
+ if (s != null || !ignoreCase) {
+ return s;
+ }
+
+ // Try doing a case-insensitive lookup
+ obj = GetCaseInsensitiveObjectInternal(name);
+ try {
+ return (String)obj;
+ }
+ catch (InvalidCastException) {
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Name", name));
+ }
+ }
+
+ // Look up an object value for a resource given its name.
+ //
+ public virtual Object GetObject(String name)
+ {
+ return GetObjectInternal(name);
+ }
+
+ public virtual Object GetObject(String name, bool ignoreCase)
+ {
+ Object obj = GetObjectInternal(name);
+
+ if (obj != null || !ignoreCase)
+ return obj;
+
+ return GetCaseInsensitiveObjectInternal(name);
+ }
+
+ protected virtual void ReadResources()
+ {
+ IDictionaryEnumerator en = Reader.GetEnumerator();
+ while (en.MoveNext()) {
+ Object value = en.Value;
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ if (Assembly != null && value is LooselyLinkedResourceReference) {
+ LooselyLinkedResourceReference assRef = (LooselyLinkedResourceReference) value;
+ value = assRef.Resolve(Assembly);
+ }
+#endif //LOOSELYLINKEDRESOURCEREFERENCE
+ Table.Add(en.Key, value);
+ }
+ // While technically possible to close the Reader here, don't close it
+ // to help with some WinRes lifetime issues.
+ }
+
+ private Object GetObjectInternal(String name)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+ Contract.EndContractBlock();
+
+ Hashtable copyOfTable = Table; // Avoid a race with Dispose
+
+ if (copyOfTable == null)
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
+
+ return copyOfTable[name];
+ }
+
+ private Object GetCaseInsensitiveObjectInternal(String name)
+ {
+ Hashtable copyOfTable = Table; // Avoid a race with Dispose
+
+ if (copyOfTable == null)
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
+
+ Hashtable caseTable = _caseInsensitiveTable; // Avoid a race condition with Close
+ if (caseTable == null)
+ {
+ caseTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
+#if _DEBUG
+ //Console.WriteLine("ResourceSet::GetObject loading up case-insensitive data");
+ BCLDebug.Perf(false, "Using case-insensitive lookups is bad perf-wise. Consider capitalizing "+name+" correctly in your source");
+#endif
+
+ IDictionaryEnumerator en = copyOfTable.GetEnumerator();
+ while (en.MoveNext())
+ {
+ caseTable.Add(en.Key, en.Value);
+ }
+ _caseInsensitiveTable = caseTable;
+ }
+
+ return caseTable[name];
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/ResourceTypeCode.cs b/src/mscorlib/src/System/Resources/ResourceTypeCode.cs
new file mode 100644
index 0000000000..64fb076eb5
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/ResourceTypeCode.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Marker for types in .resources files
+**
+**
+===========================================================*/
+
+namespace System.Resources {
+ /* An internal implementation detail for .resources files, describing
+ what type an object is.
+ Ranges:
+ 0 - 0x1F Primitives and reserved values
+ 0x20 - 0x3F Specially recognized types, like byte[] and Streams
+
+ Note this data must be included in any documentation describing the
+ internals of .resources files.
+ */
+ [Serializable]
+ internal enum ResourceTypeCode {
+ // Primitives
+ Null = 0,
+ String = 1,
+ Boolean = 2,
+ Char = 3,
+ Byte = 4,
+ SByte = 5,
+ Int16 = 6,
+ UInt16 = 7,
+ Int32 = 8,
+ UInt32 = 9,
+ Int64 = 0xa,
+ UInt64 = 0xb,
+ Single = 0xc,
+ Double = 0xd,
+ Decimal = 0xe,
+ DateTime = 0xf,
+ TimeSpan = 0x10,
+
+ // A meta-value - change this if you add new primitives
+ LastPrimitive = TimeSpan,
+
+ // Types with a special representation, like byte[] and Stream
+ ByteArray = 0x20,
+ Stream = 0x21,
+
+ // User types - serialized using the binary formatter.
+ StartOfUserTypes = 0x40
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/RuntimeResourceSet.cs b/src/mscorlib/src/System/Resources/RuntimeResourceSet.cs
new file mode 100644
index 0000000000..6b512bcf6a
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/RuntimeResourceSet.cs
@@ -0,0 +1,435 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: CultureInfo-specific collection of resources.
+**
+**
+===========================================================*/
+namespace System.Resources {
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Reflection;
+ using System.Runtime.Versioning;
+ using System.Diagnostics.Contracts;
+
+ // A RuntimeResourceSet stores all the resources defined in one
+ // particular CultureInfo, with some loading optimizations.
+ //
+ // It is expected that nearly all the runtime's users will be satisfied with the
+ // default resource file format, and it will be more efficient than most simple
+ // implementations. Users who would consider creating their own ResourceSets and/or
+ // ResourceReaders and ResourceWriters are people who have to interop with a
+ // legacy resource file format, are creating their own resource file format
+ // (using XML, for instance), or require doing resource lookups at runtime over
+ // the network. This group will hopefully be small, but all the infrastructure
+ // should be in place to let these users write & plug in their own tools.
+ //
+ // The Default Resource File Format
+ //
+ // The fundamental problems addressed by the resource file format are:
+ //
+ // * Versioning - A ResourceReader could in theory support many different
+ // file format revisions.
+ // * Storing intrinsic datatypes (ie, ints, Strings, DateTimes, etc) in a compact
+ // format
+ // * Support for user-defined classes - Accomplished using Serialization
+ // * Resource lookups should not require loading an entire resource file - If you
+ // look up a resource, we only load the value for that resource, minimizing working set.
+ //
+ //
+ // There are four sections to the default file format. The first
+ // is the Resource Manager header, which consists of a magic number
+ // that identifies this as a Resource file, and a ResourceSet class name.
+ // The class name is written here to allow users to provide their own
+ // implementation of a ResourceSet (and a matching ResourceReader) to
+ // control policy. If objects greater than a certain size or matching a
+ // certain naming scheme shouldn't be stored in memory, users can tweak that
+ // with their own subclass of ResourceSet.
+ //
+ // The second section in the system default file format is the
+ // RuntimeResourceSet specific header. This contains a version number for
+ // the .resources file, the number of resources in this file, the number of
+ // different types contained in the file, followed by a list of fully
+ // qualified type names. After this, we include an array of hash values for
+ // each resource name, then an array of virtual offsets into the name section
+ // of the file. The hashes allow us to do a binary search on an array of
+ // integers to find a resource name very quickly without doing many string
+ // compares (except for once we find the real type, of course). If a hash
+ // matches, the index into the array of hash values is used as the index
+ // into the name position array to find the name of the resource. The type
+ // table allows us to read multiple different classes from the same file,
+ // including user-defined types, in a more efficient way than using
+ // Serialization, at least when your .resources file contains a reasonable
+ // proportion of base data types such as Strings or ints. We use
+ // Serialization for all the non-instrinsic types.
+ //
+ // The third section of the file is the name section. It contains a
+ // series of resource names, written out as byte-length prefixed little
+ // endian Unicode strings (UTF-16). After each name is a four byte virtual
+ // offset into the data section of the file, pointing to the relevant
+ // string or serialized blob for this resource name.
+ //
+ // The fourth section in the file is the data section, which consists
+ // of a type and a blob of bytes for each item in the file. The type is
+ // an integer index into the type table. The data is specific to that type,
+ // but may be a number written in binary format, a String, or a serialized
+ // Object.
+ //
+ // The system default file format (V1) is as follows:
+ //
+ // What Type of Data
+ // ==================================================== ===========
+ //
+ // Resource Manager header
+ // Magic Number (0xBEEFCACE) Int32
+ // Resource Manager header version Int32
+ // Num bytes to skip from here to get past this header Int32
+ // Class name of IResourceReader to parse this file String
+ // Class name of ResourceSet to parse this file String
+ //
+ // RuntimeResourceReader header
+ // ResourceReader version number Int32
+ // [Only in debug V2 builds - "***DEBUG***"] String
+ // Number of resources in the file Int32
+ // Number of types in the type table Int32
+ // Name of each type Set of Strings
+ // Padding bytes for 8-byte alignment (use PAD) Bytes (0-7)
+ // Hash values for each resource name Int32 array, sorted
+ // Virtual offset of each resource name Int32 array, coupled with hash values
+ // Absolute location of Data section Int32
+ //
+ // RuntimeResourceReader Name Section
+ // Name & virtual offset of each resource Set of (UTF-16 String, Int32) pairs
+ //
+ // RuntimeResourceReader Data Section
+ // Type and Value of each resource Set of (Int32, blob of bytes) pairs
+ //
+ // This implementation, when used with the default ResourceReader class,
+ // loads only the strings that you look up for. It can do string comparisons
+ // without having to create a new String instance due to some memory mapped
+ // file optimizations in the ResourceReader and FastResourceComparer
+ // classes. This keeps the memory we touch to a minimum when loading
+ // resources.
+ //
+ // If you use a different IResourceReader class to read a file, or if you
+ // do case-insensitive lookups (and the case-sensitive lookup fails) then
+ // we will load all the names of each resource and each resource value.
+ // This could probably use some optimization.
+ //
+ // In addition, this supports object serialization in a similar fashion.
+ // We build an array of class types contained in this file, and write it
+ // to RuntimeResourceReader header section of the file. Every resource
+ // will contain its type (as an index into the array of classes) with the data
+ // for that resource. We will use the Runtime's serialization support for this.
+ //
+ // All strings in the file format are written with BinaryReader and
+ // BinaryWriter, which writes out the length of the String in bytes as an
+ // Int32 then the contents as Unicode chars encoded in UTF-8. In the name
+ // table though, each resource name is written in UTF-16 so we can do a
+ // string compare byte by byte against the contents of the file, without
+ // allocating objects. Ideally we'd have a way of comparing UTF-8 bytes
+ // directly against a String object, but that may be a lot of work.
+ //
+ // The offsets of each resource string are relative to the beginning
+ // of the Data section of the file. This way, if a tool decided to add
+ // one resource to a file, it would only need to increment the number of
+ // resources, add the hash &amp; location of last byte in the name section
+ // to the array of resource hashes and resource name positions (carefully
+ // keeping these arrays sorted), add the name to the end of the name &amp;
+ // offset list, possibly add the type list of types types (and increase
+ // the number of items in the type table), and add the resource value at
+ // the end of the file. The other offsets wouldn't need to be updated to
+ // reflect the longer header section.
+ //
+ // Resource files are currently limited to 2 gigabytes due to these
+ // design parameters. A future version may raise the limit to 4 gigabytes
+ // by using unsigned integers, or may use negative numbers to load items
+ // out of an assembly manifest. Also, we may try sectioning the resource names
+ // into smaller chunks, each of size sqrt(n), would be substantially better for
+ // resource files containing thousands of resources.
+ //
+ internal sealed class RuntimeResourceSet : ResourceSet, IEnumerable
+ {
+ internal const int Version = 2; // File format version number
+
+ // Cache for resources. Key is the resource name, which can be cached
+ // for arbitrarily long times, since the object is usually a string
+ // literal that will live for the lifetime of the appdomain. The
+ // value is a ResourceLocator instance, which might cache the object.
+ private Dictionary<String, ResourceLocator> _resCache;
+
+
+ // For our special load-on-demand reader, cache the cast. The
+ // RuntimeResourceSet's implementation knows how to treat this reader specially.
+ private ResourceReader _defaultReader;
+
+ // This is a lookup table for case-insensitive lookups, and may be null.
+ // Consider always using a case-insensitive resource cache, as we don't
+ // want to fill this out if we can avoid it. The problem is resource
+ // fallback will somewhat regularly cause us to look up resources that
+ // don't exist.
+ private Dictionary<String, ResourceLocator> _caseInsensitiveTable;
+
+ // If we're not using our custom reader, then enumerate through all
+ // the resources once, adding them into the table.
+ private bool _haveReadFromReader;
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal RuntimeResourceSet(String fileName) : base(false)
+ {
+ BCLDebug.Log("RESMGRFILEFORMAT", "RuntimeResourceSet .ctor(String)");
+ _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
+ Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
+ _defaultReader = new ResourceReader(stream, _resCache);
+ Reader = _defaultReader;
+ }
+
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ internal RuntimeResourceSet(Stream stream, Assembly assembly) : base(false)
+ {
+ BCLDebug.Log("RESMGRFILEFORMAT", "RuntimeResourceSet .ctor(Stream)");
+ _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
+ _defaultReader = new ResourceReader(stream, _resCache);
+ Reader = _defaultReader;
+ Assembly = assembly;
+ }
+#else
+ [System.Security.SecurityCritical] // auto-generated
+ internal RuntimeResourceSet(Stream stream) : base(false)
+ {
+ BCLDebug.Log("RESMGRFILEFORMAT", "RuntimeResourceSet .ctor(Stream)");
+ _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
+ _defaultReader = new ResourceReader(stream, _resCache);
+ Reader = _defaultReader;
+ }
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
+ protected override void Dispose(bool disposing)
+ {
+ if (Reader == null)
+ return;
+
+ if (disposing) {
+ lock(Reader) {
+ _resCache = null;
+ if (_defaultReader != null) {
+ _defaultReader.Close();
+ _defaultReader = null;
+ }
+ _caseInsensitiveTable = null;
+ // Set Reader to null to avoid a race in GetObject.
+ base.Dispose(disposing);
+ }
+ }
+ else {
+ // Just to make sure we always clear these fields in the future...
+ _resCache = null;
+ _caseInsensitiveTable = null;
+ _defaultReader = null;
+ base.Dispose(disposing);
+ }
+ }
+
+ public override IDictionaryEnumerator GetEnumerator()
+ {
+ return GetEnumeratorHelper();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumeratorHelper();
+ }
+
+ private IDictionaryEnumerator GetEnumeratorHelper()
+ {
+ IResourceReader copyOfReader = Reader;
+ if (copyOfReader == null || _resCache == null)
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
+
+ return copyOfReader.GetEnumerator();
+ }
+
+
+ public override String GetString(String key)
+ {
+ Object o = GetObject(key, false, true);
+ return (String) o;
+ }
+
+ public override String GetString(String key, bool ignoreCase)
+ {
+ Object o = GetObject(key, ignoreCase, true);
+ return (String) o;
+ }
+
+ public override Object GetObject(String key)
+ {
+ return GetObject(key, false, false);
+ }
+
+ public override Object GetObject(String key, bool ignoreCase)
+ {
+ return GetObject(key, ignoreCase, false);
+ }
+
+ private Object GetObject(String key, bool ignoreCase, bool isString)
+ {
+ if (key==null)
+ throw new ArgumentNullException("key");
+ if (Reader == null || _resCache == null)
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
+ Contract.EndContractBlock();
+
+ Object value = null;
+ ResourceLocator resLocation;
+
+ lock(Reader) {
+ if (Reader == null)
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
+
+ if (_defaultReader != null) {
+ BCLDebug.Log("RESMGRFILEFORMAT", "Going down fast path in RuntimeResourceSet::GetObject");
+
+ // Find the offset within the data section
+ int dataPos = -1;
+ if (_resCache.TryGetValue(key, out resLocation)) {
+ value = resLocation.Value;
+ dataPos = resLocation.DataPosition;
+ }
+
+ if (dataPos == -1 && value == null) {
+ dataPos = _defaultReader.FindPosForResource(key);
+ }
+
+ if (dataPos != -1 && value == null) {
+ Contract.Assert(dataPos >= 0, "data section offset cannot be negative!");
+ // Normally calling LoadString or LoadObject requires
+ // taking a lock. Note that in this case, we took a
+ // lock on the entire RuntimeResourceSet, which is
+ // sufficient since we never pass this ResourceReader
+ // to anyone else.
+ ResourceTypeCode typeCode;
+ if (isString) {
+ value = _defaultReader.LoadString(dataPos);
+ typeCode = ResourceTypeCode.String;
+ }
+ else {
+ value = _defaultReader.LoadObject(dataPos, out typeCode);
+ }
+
+ resLocation = new ResourceLocator(dataPos, (ResourceLocator.CanCache(typeCode)) ? value : null);
+ lock(_resCache) {
+ _resCache[key] = resLocation;
+ }
+ }
+
+ if (value != null || !ignoreCase) {
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ if (Assembly != null && (value is LooselyLinkedResourceReference)) {
+ LooselyLinkedResourceReference assRef = (LooselyLinkedResourceReference) value;
+ value = assRef.Resolve(Assembly);
+ }
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
+ return value; // may be null
+ }
+ } // if (_defaultReader != null)
+
+ // At this point, we either don't have our default resource reader
+ // or we haven't found the particular resource we're looking for
+ // and may have to search for it in a case-insensitive way.
+ if (!_haveReadFromReader) {
+ // If necessary, init our case insensitive hash table.
+ if (ignoreCase && _caseInsensitiveTable == null) {
+ _caseInsensitiveTable = new Dictionary<String, ResourceLocator>(StringComparer.OrdinalIgnoreCase);
+ }
+#if _DEBUG
+ BCLDebug.Perf(!ignoreCase, "Using case-insensitive lookups is bad perf-wise. Consider capitalizing "+key+" correctly in your source");
+#endif
+
+ if (_defaultReader == null) {
+ IDictionaryEnumerator en = Reader.GetEnumerator();
+ while (en.MoveNext()) {
+ DictionaryEntry entry = en.Entry;
+ String readKey = (String) entry.Key;
+ ResourceLocator resLoc = new ResourceLocator(-1, entry.Value);
+ _resCache.Add(readKey, resLoc);
+ if (ignoreCase)
+ _caseInsensitiveTable.Add(readKey, resLoc);
+ }
+ // Only close the reader if it is NOT our default one,
+ // since we need it around to resolve ResourceLocators.
+ if (!ignoreCase)
+ Reader.Close();
+ }
+ else {
+ Contract.Assert(ignoreCase, "This should only happen for case-insensitive lookups");
+ ResourceReader.ResourceEnumerator en = _defaultReader.GetEnumeratorInternal();
+ while (en.MoveNext()) {
+ // Note: Always ask for the resource key before the data position.
+ String currentKey = (String) en.Key;
+ int dataPos = en.DataPosition;
+ ResourceLocator resLoc = new ResourceLocator(dataPos, null);
+ _caseInsensitiveTable.Add(currentKey, resLoc);
+ }
+ }
+ _haveReadFromReader = true;
+ }
+ Object obj = null;
+ bool found = false;
+ bool keyInWrongCase = false;
+ if (_defaultReader != null) {
+ if (_resCache.TryGetValue(key, out resLocation)) {
+ found = true;
+ obj = ResolveResourceLocator(resLocation, key, _resCache, keyInWrongCase);
+ }
+ }
+ if (!found && ignoreCase) {
+ if (_caseInsensitiveTable.TryGetValue(key, out resLocation)) {
+ found = true;
+ keyInWrongCase = true;
+ obj = ResolveResourceLocator(resLocation, key, _resCache, keyInWrongCase);
+ }
+ }
+ return obj;
+ } // lock(Reader)
+ }
+
+ // The last parameter indicates whether the lookup required a
+ // case-insensitive lookup to succeed, indicating we shouldn't add
+ // the ResourceLocation to our case-sensitive cache.
+ private Object ResolveResourceLocator(ResourceLocator resLocation, String key, Dictionary<String, ResourceLocator> copyOfCache, bool keyInWrongCase)
+ {
+ // We need to explicitly resolve loosely linked manifest
+ // resources, and we need to resolve ResourceLocators with null objects.
+ Object value = resLocation.Value;
+ if (value == null) {
+ ResourceTypeCode typeCode;
+ lock(Reader) {
+ value = _defaultReader.LoadObject(resLocation.DataPosition, out typeCode);
+ }
+ if (!keyInWrongCase && ResourceLocator.CanCache(typeCode)) {
+ resLocation.Value = value;
+ copyOfCache[key] = resLocation;
+ }
+ }
+#if LOOSELY_LINKED_RESOURCE_REFERENCE
+ if (Assembly != null && value is LooselyLinkedResourceReference) {
+ LooselyLinkedResourceReference assRef = (LooselyLinkedResourceReference) value;
+ value = assRef.Resolve(Assembly);
+ }
+#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+ return value;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/SatelliteContractVersionAttribute.cs b/src/mscorlib/src/System/Resources/SatelliteContractVersionAttribute.cs
new file mode 100644
index 0000000000..f72e810227
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/SatelliteContractVersionAttribute.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Specifies which version of a satellite assembly
+** the ResourceManager should ask for.
+**
+**
+===========================================================*/
+
+namespace System.Resources {
+ using System;
+ using System.Diagnostics.Contracts;
+
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false)]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public sealed class SatelliteContractVersionAttribute : Attribute
+ {
+ private String _version;
+
+ public SatelliteContractVersionAttribute(String version)
+ {
+ if (version == null)
+ throw new ArgumentNullException("version");
+ Contract.EndContractBlock();
+ _version = version;
+ }
+
+ public String Version {
+ get { return _version; }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/UltimateResourceFallbackLocation.cs b/src/mscorlib/src/System/Resources/UltimateResourceFallbackLocation.cs
new file mode 100644
index 0000000000..5785bfd357
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/UltimateResourceFallbackLocation.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+**
+** Purpose: Tells the ResourceManager where to find the
+** ultimate fallback resources for your assembly.
+**
+**
+===========================================================*/
+
+using System;
+
+namespace System.Resources {
+
+[Serializable]
+[System.Runtime.InteropServices.ComVisible(true)]
+ public enum UltimateResourceFallbackLocation
+ {
+ MainAssembly,
+ Satellite
+ }
+}
diff --git a/src/mscorlib/src/System/Resources/__FastResourceComparer.cs b/src/mscorlib/src/System/Resources/__FastResourceComparer.cs
new file mode 100644
index 0000000000..5bc7333863
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/__FastResourceComparer.cs
@@ -0,0 +1,141 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: A collection of quick methods for comparing
+** resource keys (strings)
+**
+**
+===========================================================*/
+namespace System.Resources {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+
+ internal sealed class FastResourceComparer : IComparer, IEqualityComparer, IComparer<String>, IEqualityComparer<String>
+ {
+ internal static readonly FastResourceComparer Default = new FastResourceComparer();
+
+ // Implements IHashCodeProvider too, due to Hashtable requirements.
+ public int GetHashCode(Object key)
+ {
+ String s = (String) key;
+ return FastResourceComparer.HashFunction(s);
+ }
+
+ public int GetHashCode(String key)
+ {
+ return FastResourceComparer.HashFunction(key);
+ }
+
+ // This hash function MUST be publically documented with the resource
+ // file format, AND we may NEVER change this hash function's return
+ // value (without changing the file format).
+ internal static int HashFunction(String key)
+ {
+ // Never change this hash function. We must standardize it so that
+ // others can read & write our .resources files. Additionally, we
+ // have a copy of it in InternalResGen as well.
+ uint hash = 5381;
+ for(int i=0; i<key.Length; i++)
+ hash = ((hash << 5) + hash) ^ key[i];
+ return (int) hash;
+ }
+
+ // Compares Strings quickly in a case-sensitive way
+ public int Compare(Object a, Object b)
+ {
+ if (a == b) return 0;
+ String sa = (String)a;
+ String sb = (String)b;
+ return String.CompareOrdinal(sa, sb);
+ }
+
+ public int Compare(String a, String b)
+ {
+ return String.CompareOrdinal(a, b);
+ }
+
+ public bool Equals(String a, String b)
+ {
+ return String.Equals(a, b);
+ }
+
+ public new bool Equals(Object a, Object b)
+ {
+ if (a == b) return true;
+ String sa = (String)a;
+ String sb = (String)b;
+ return String.Equals(sa,sb);
+ }
+
+ // Input is one string to compare with, and a byte[] containing chars in
+ // little endian unicode. Pass in the number of valid chars.
+ [System.Security.SecurityCritical] // auto-generated
+ public unsafe static int CompareOrdinal(String a, byte[] bytes, int bCharLength)
+ {
+ Contract.Assert(a != null && bytes != null, "FastResourceComparer::CompareOrdinal must have non-null params");
+ Contract.Assert(bCharLength * 2 <= bytes.Length, "FastResourceComparer::CompareOrdinal - numChars is too big!");
+ // This is a managed version of strcmp, but I can't take advantage
+ // of a terminating 0, unlike strcmp in C.
+ int i = 0;
+ int r = 0;
+ // Compare the min length # of characters, then return length diffs.
+ int numChars = a.Length;
+ if (numChars > bCharLength)
+ numChars = bCharLength;
+ if (bCharLength == 0) // Can't use fixed on a 0-element array.
+ return (a.Length == 0) ? 0 : -1;
+ fixed(byte* pb = bytes) {
+
+ byte *pChar = pb;
+ while (i < numChars && r == 0) {
+ // little endian format
+ int b = pChar[0] | pChar[1] << 8;
+ r = a[i++] - b;
+ pChar += sizeof(char);
+ }
+ }
+ if (r != 0) return r;
+ return a.Length - bCharLength;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ public static int CompareOrdinal(byte[] bytes, int aCharLength, String b)
+ {
+ return -CompareOrdinal(b, bytes, aCharLength);
+ }
+
+ // This method is to handle potentially misaligned data accesses.
+ // The byte* must point to little endian Unicode characters.
+ [System.Security.SecurityCritical] // auto-generated
+ internal unsafe static int CompareOrdinal(byte* a, int byteLen, String b)
+ {
+ Contract.Assert((byteLen & 1) == 0, "CompareOrdinal is expecting a UTF-16 string length, which must be even!");
+ Contract.Assert(a != null && b != null, "Null args not allowed.");
+ Contract.Assert(byteLen >= 0, "byteLen must be non-negative.");
+
+ int r = 0;
+ int i = 0;
+ // Compare the min length # of characters, then return length diffs.
+ int numChars = byteLen >> 1;
+ if (numChars > b.Length)
+ numChars = b.Length;
+ while(i < numChars && r == 0) {
+ // Must compare character by character, not byte by byte.
+ char aCh = (char) (*a++ | (*a++ << 8));
+ r = aCh - b[i++];
+ }
+ if (r != 0) return r;
+ return byteLen - b.Length * 2;
+ }
+ }
+}
+
diff --git a/src/mscorlib/src/System/Resources/__HResults.cs b/src/mscorlib/src/System/Resources/__HResults.cs
new file mode 100644
index 0000000000..c1546edc63
--- /dev/null
+++ b/src/mscorlib/src/System/Resources/__HResults.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//=============================================================================
+//
+//
+//
+// Purpose: Define HResult constants returned by the Windows Modern Resource Manager
+// and consumed by System.Resources.ResourceManager.
+//
+//===========================================================================*/
+#if FEATURE_APPX
+namespace System.Resources {
+ using System;
+ // Only static data no need to serialize
+ internal static class __HResults
+ {
+ // From WinError.h
+ public const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073B1F);
+ }
+}
+#endif