Move ResourceManager to shared CoreLib. (dotnet/coreclr#22249)
authorFilip Navara <filip.navara@gmail.com>
Fri, 1 Feb 2019 22:25:56 +0000 (23:25 +0100)
committerJan Kotas <jkotas@microsoft.com>
Fri, 1 Feb 2019 22:25:56 +0000 (14:25 -0800)
* Move ResourceManager to shared CoreLib.

* Split UAP code out of ResourceManager.cs into separate files.

* Remove ResourceManager.NonUap.cs.

* Do not access internal field (CoreRT build fix).

* Fix non-Windows builds (CoreRT).

* Remove obsolete Debug code.

Commit migrated from https://github.com/dotnet/coreclr/commit/a6155cedca904208f28c9c1e998029a6c6e5ba62

src/coreclr/src/System.Private.CoreLib/Resources/Strings.resx
src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/coreclr/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreCLR.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/Internal/Resources/PRIExceptionInfo.cs [moved from src/coreclr/src/System.Private.CoreLib/src/Internal/Resources/PRIExceptionInfo.cs with 100% similarity]
src/libraries/System.Private.CoreLib/src/Internal/Resources/WindowsRuntimeResourceManagerBase.cs [moved from src/coreclr/src/System.Private.CoreLib/src/Internal/Resources/WindowsRuntimeResourceManagerBase.cs with 100% similarity]
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs [moved from src/coreclr/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs with 100% similarity]
src/libraries/System.Private.CoreLib/src/System/Resources/IResourceGroveler.cs [moved from src/coreclr/src/System.Private.CoreLib/src/System/Resources/IResourceGroveler.cs with 100% similarity]
src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs [moved from src/coreclr/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs with 87% similarity]
src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.Uap.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.cs [moved from src/coreclr/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs with 66% similarity]

index 6800d8e..2af274f 100644 (file)
   <data name="Arg_InvalidNeutralResourcesLanguage_FallbackLoc" xml:space="preserve">
     <value>The NeutralResourcesLanguageAttribute specifies an invalid or unrecognized ultimate resource fallback location: "{0}".</value>
   </data>
+  <data name="Arg_InvalidSatelliteContract_Asm_Ver" xml:space="preserve">
+    <value>Satellite contract version attribute on the assembly '{0}' specifies an invalid version: {1}.</value>
+  </data>
   <data name="Arg_InvalidOleVariantTypeException" xml:space="preserve">
     <value>Specified OLE variant was invalid.</value>
   </data>
index d8bde72..d8c8079 100644 (file)
   <!-- Sources -->
   <ItemGroup>
     <Compile Include="$(BclSourcesRoot)\Internal\Console.cs" />
-    <Compile Include="$(BclSourcesRoot)\Internal\Resources\PRIExceptionInfo.cs" Condition="'$(FeatureAppX)' == 'true'" />
-    <Compile Include="$(BclSourcesRoot)\Internal\Resources\WindowsRuntimeResourceManagerBase.cs" Condition="'$(FeatureAppX)' == 'true'" />
     <Compile Include="$(BclSourcesRoot)\Internal\Runtime\Augments\EnvironmentAugments.cs" />
     <Compile Include="$(BclSourcesRoot)\Internal\Runtime\Augments\RuntimeThread.cs" />
     <Compile Include="$(BclSourcesRoot)\Microsoft\Win32\UnsafeNativeMethods.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeModule.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeParameterInfo.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimePropertyInfo.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Resources\FileBasedResourceGroveler.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Resources\IResourceGroveler.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Resources\ResourceManager.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\RtType.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ConditionalWeakTable.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastableHelpers.cs" />
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreCLR.cs
new file mode 100644 (file)
index 0000000..44803d9
--- /dev/null
@@ -0,0 +1,33 @@
+// 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.
+
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System.Resources
+{
+    internal partial class ManifestBasedResourceGroveler
+    {
+        // Internal version of GetSatelliteAssembly that avoids throwing FileNotFoundException
+        private static Assembly InternalGetSatelliteAssembly(Assembly mainAssembly,
+                                                             CultureInfo culture,
+                                                             Version version)
+        {
+            return ((RuntimeAssembly)mainAssembly).InternalGetSatelliteAssembly(culture, version, throwOnFileNotFound: false);
+        }
+
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        private static extern bool GetNeutralResourcesLanguageAttribute(RuntimeAssembly assemblyHandle, StringHandleOnStack cultureName, out short fallbackLocation);
+
+        private static bool GetNeutralResourcesLanguageAttribute(Assembly assemblyHandle, ref string cultureName, out short fallbackLocation)
+        {
+            return GetNeutralResourcesLanguageAttribute(((RuntimeAssembly)assemblyHandle).GetNativeHandle(),
+                                                        JitHelpers.GetStringHandleOnStack(ref cultureName),
+                                                        out fallbackLocation);
+        }
+    }
+}
index 4dde786..b113504 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\ResolveEventArgs.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ResolveEventHandler.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\FastResourceComparer.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Resources\FileBasedResourceGroveler.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Resources\IResourceGroveler.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\IResourceReader.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Resources\ManifestBasedResourceGroveler.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\MissingManifestResourceException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\MissingSatelliteAssemblyException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\NeutralResourcesLanguageAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\ResourceFallbackManager.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Resources\ResourceManager.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\ResourceReader.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\ResourceSet.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Resources\ResourceTypeCode.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Semaphore.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\EventWaitHandle.Windows.cs" />
   </ItemGroup>
+  <ItemGroup Condition="'$(FeatureAppX)' == 'true' or '$(EnableWinRT)' == 'true'">
+    <Compile Include="$(MSBuildThisFileDirectory)Internal\Resources\PRIExceptionInfo.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Internal\Resources\WindowsRuntimeResourceManagerBase.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Resources\ResourceManager.Uap.cs" />
+  </ItemGroup>
   <ItemGroup Condition="$(TargetsUnix)">
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\Interop.Errors.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\Interop.IOErrors.cs" />
@@ -39,7 +39,7 @@ namespace System.Resources
     // belonging to that type may not be initialized. FrameworkEventSource.Log
     // is one such example.
     //
-    internal class ManifestBasedResourceGroveler : IResourceGroveler
+    internal partial class ManifestBasedResourceGroveler : IResourceGroveler
     {
         private ResourceManager.ResourceManagerMediator _mediator;
 
@@ -371,30 +371,16 @@ namespace System.Resources
             // 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 = InternalGetSatelliteAssembly(_mediator.MainAssembly, lookForCulture, _mediator.SatelliteContractVersion);
             }
-
-            // 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)
+            catch (FileLoadException)
             {
-                // 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 != Win32Marshal.MakeHRFromErrorCode(Interop.Errors.ERROR_ACCESS_DENIED))
-                {
-                    Debug.Fail("[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.GetName().Version.ToString() : _mediator.SatelliteContractVersion.ToString()) + " of assembly " + _mediator.MainAssembly.GetName().Name + " 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)
+            catch (BadImageFormatException)
             {
-                Debug.Fail("[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.GetName().Version.ToString() : _mediator.SatelliteContractVersion.ToString()) + " of assembly " + _mediator.MainAssembly.GetName().Name + Environment.NewLine + "Exception: " + bife);
+                // Don't throw for zero-length satellite assemblies, for compat with v1
             }
 
             return satellite;
@@ -479,24 +465,5 @@ namespace System.Resources
             resName += fileName;
             throw new MissingManifestResourceException(SR.Format(SR.MissingManifestResource_NoNeutralAsm, resName, _mediator.MainAssembly.GetName().Name));
         }
-
-        // Internal version of GetSatelliteAssembly that avoids throwing FileNotFoundException
-        private static Assembly InternalGetSatelliteAssembly(Assembly mainAssembly,
-                                                             CultureInfo culture,
-                                                             Version version)
-        {
-            return ((RuntimeAssembly)mainAssembly).InternalGetSatelliteAssembly(culture, version, throwOnFileNotFound: false);
-        }
-
-        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        [return: MarshalAs(UnmanagedType.Bool)]
-        private static extern bool GetNeutralResourcesLanguageAttribute(RuntimeAssembly assemblyHandle, StringHandleOnStack cultureName, out short fallbackLocation);
-
-        private static bool GetNeutralResourcesLanguageAttribute(Assembly assemblyHandle, ref string cultureName, out short fallbackLocation)
-        {
-            return GetNeutralResourcesLanguageAttribute(((RuntimeAssembly)assemblyHandle).GetNativeHandle(),
-                                                        JitHelpers.GetStringHandleOnStack(ref cultureName),
-                                                        out fallbackLocation);
-        }
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.Uap.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.Uap.cs
new file mode 100644 (file)
index 0000000..8a89f5e
--- /dev/null
@@ -0,0 +1,256 @@
+// 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.
+
+
+using System;
+using System.IO;
+using System.Globalization;
+using System.Collections;
+using System.Text;
+using System.Reflection;
+using System.Security;
+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;
+using Internal.Resources;
+
+namespace System.Resources
+{
+    public partial class ResourceManager
+    {
+        private WindowsRuntimeResourceManagerBase _WinRTResourceManager;
+        private bool _PRIonAppXInitialized;
+        private PRIExceptionInfo _PRIExceptionInfo;
+
+        private bool UseUapResourceManagement { get; set; }
+
+        private string GetStringFromPRI(string stringName, CultureInfo culture, string neutralResourcesCulture)
+        {
+            Debug.Assert(UseUapResourceManagement);
+            Debug.Assert(_WinRTResourceManager != null);
+            Debug.Assert(_PRIonAppXInitialized);
+
+            // If the caller explicitly 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.
+            if (object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
+            {
+                culture = null;
+            }
+
+            string startingCulture = culture?.Name;
+
+            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(SR.Format(SR.MissingManifestResource_ResWFileNotLoaded, _PRIExceptionInfo.ResWFile, _PRIExceptionInfo.PackageSimpleName));
+
+                throw new MissingManifestResourceException(SR.MissingManifestResource_NoPRIresources);
+            }
+
+            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 System.Private.CoreLib, we have to get the type via reflection.
+        // It would be better if we could just implement WindowsRuntimeResourceManager in System.Private.CoreLib, but we can't, because
+        // we can do very little with WinRT in System.Private.CoreLib.
+#if FEATURE_APPX
+        internal static WindowsRuntimeResourceManagerBase GetWinRTResourceManager()
+        {
+            Type WinRTResourceManagerType = Type.GetType("System.Resources.WindowsRuntimeResourceManager, System.Runtime.WindowsRuntime", throwOnError: true);
+            return (WindowsRuntimeResourceManagerBase)Activator.CreateInstance(WinRTResourceManagerType, true);
+        }
+#endif
+#if ENABLE_WINRT
+        internal static WindowsRuntimeResourceManagerBase GetWinRTResourceManager()
+        {
+            Assembly hiddenScopeAssembly = Assembly.Load(RuntimeAugments.HiddenScopeAssemblyName);
+            Type WinRTResourceManagerType = hiddenScopeAssembly.GetType("System.Resources.WindowsRuntimeResourceManager", true);
+            return (WindowsRuntimeResourceManagerBase)Activator.CreateInstance(WinRTResourceManagerType, true);
+        }
+#endif
+
+        // CoreCLR: When running under AppX, the following rules apply for resource lookup:
+        //
+        // 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.
+        //
+        // .NET Native: If it is framework assembly we'll return true. The reason is in .NetNative we don't merge the
+        // resources to the app PRI file.
+        // The framework assemblies are tagged with attribute [assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
+        private bool ShouldUseSatelliteAssemblyResourceLookupUnderAppX(Assembly resourcesAssembly)
+        {
+            if (typeof(object).Assembly == resourcesAssembly)
+                return true;
+
+#if FEATURE_APPX
+            // 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)(AppContext.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.
+                        return true;
+                    }
+                }
+            }
+#else // ENABLE_WINRT
+            foreach (var attrib in resourcesAssembly.GetCustomAttributes())
+            {
+                AssemblyMetadataAttribute meta = attrib as AssemblyMetadataAttribute;
+                if (meta != null && meta.Key.Equals(".NETFrameworkAssembly"))
+                {
+                    return true;
+                }
+            }
+#endif
+
+            return false;
+        }
+
+        // Only call SetAppXConfiguration from ResourceManager constructors, and nowhere else.
+        // Throws MissingManifestResourceException and WinRT HResults
+        private void SetAppXConfiguration()
+        {
+            Debug.Assert(UseUapResourceManagement == false); // Only this function writes to this member
+            Debug.Assert(_WinRTResourceManager == null); // Only this function writes to this member
+            Debug.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member
+            Debug.Assert(_PRIExceptionInfo == null); // Only this function writes to this member
+
+            bool bUsingSatelliteAssembliesUnderAppX = false;
+
+            if (MainAssembly != null)
+            {
+                if (MainAssembly != typeof(object).Assembly) // We are not loading resources for System.Private.CoreLib
+                {
+#if ENABLE_WINRT
+                    WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks;
+                    if (callbacks != null && callbacks.IsAppxModel())
+#else
+                    if (ApplicationModel.IsUap)
+#endif
+                    {
+                        // 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;
+
+                        if (!bUsingSatelliteAssembliesUnderAppX)
+                        {
+                            UseUapResourceManagement = !ShouldUseSatelliteAssemblyResourceLookupUnderAppX(MainAssembly);
+
+                            if (UseUapResourceManagement)
+                            {
+                                // Only now are we certain that we need the PRI file.
+
+                                // At this point it is important NOT to set UseUapResourceManagement 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.
+
+                                _WinRTResourceManager = GetWinRTResourceManager();
+
+                                try
+                                {
+                                    _PRIonAppXInitialized = _WinRTResourceManager.Initialize(MainAssembly.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.
+                                }
+
+                                if (!_PRIonAppXInitialized)
+                                {
+                                    UseUapResourceManagement = false;
+                                }
+                                // 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.
+                            }
+                        }
+                    }
+                }
+            }
+            // MainAssembly == 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.
+        }
+    }
+}
@@ -2,37 +2,15 @@
 // 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.
-**
-** 
-===========================================================*/
+
+using System.IO;
+using System.Globalization;
+using System.Reflection;
+using System.Collections.Generic;
+using System.Diagnostics;
 
 namespace System.Resources
 {
-    using System;
-    using System.IO;
-    using System.Globalization;
-    using System.Collections;
-    using System.Text;
-    using System.Reflection;
-    using System.Security;
-    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;
-#if FEATURE_APPX
-    using Internal.Resources;
-#endif
     // 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 
@@ -113,7 +91,7 @@ namespace System.Resources
     // is one such example.
     //
 
-    public class ResourceManager
+    public partial class ResourceManager
     {
         internal class CultureNameResourceSetPair
         {
@@ -122,11 +100,6 @@ namespace System.Resources
         }
 
         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")]
-        internal Hashtable ResourceSets;
-
 
         private Dictionary<string, ResourceSet> _resourceSets;
         private string moduleDir;      // For assembly-ignorant directory location
@@ -206,9 +179,6 @@ namespace System.Resources
 
             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;
@@ -229,8 +199,6 @@ namespace System.Resources
             MainAssembly = assembly;
             BaseNameField = baseName;
 
-            SetAppXConfiguration();
-
             CommonAssemblyInit();
         }
 
@@ -264,8 +232,6 @@ namespace System.Resources
             MainAssembly = _locationInfo.Assembly;
             BaseNameField = resourceSource.Name;
 
-            SetAppXConfiguration();
-
             CommonAssemblyInit();
         }
 
@@ -273,6 +239,10 @@ namespace System.Resources
         // security check in each constructor prevents it.
         private void CommonAssemblyInit()
         {
+#if FEATURE_APPX || ENABLE_WINRT
+            SetAppXConfiguration();
+#endif
+
             // Now we can use the managed resources even when using PRI's to support the APIs GetObject, GetStream...etc.
             UseManifest = true;
 
@@ -566,9 +536,20 @@ namespace System.Resources
                 throw new ArgumentNullException(nameof(a), SR.ArgumentNull_Assembly);
             }
 
-            // 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;
+            string v = a.GetCustomAttribute<SatelliteContractVersionAttribute>()?.Version;
+            if (v == null)
+            {
+                // Return null. The calling code will use the assembly version instead to avoid potential type
+                // and library loads caused by CA lookup.
+                return null;
+            }
+
+            if (!Version.TryParse(v, out Version version))
+            {
+                throw new ArgumentException(SR.Format(SR.Arg_InvalidSatelliteContract_Asm_Ver, a.ToString(), v));
+            }
+
+            return version;
         }
 
         protected static CultureInfo GetNeutralResourcesLanguage(Assembly a)
@@ -608,199 +589,6 @@ namespace System.Resources
             return string.Equals(an.Name, "mscorlib", StringComparison.OrdinalIgnoreCase);
         }
 
-#if FEATURE_APPX
-        private string GetStringFromPRI(string stringName, string startingCulture, string neutralResourcesCulture)
-        {
-            Debug.Assert(_bUsingModernResourceManagement);
-            Debug.Assert(_WinRTResourceManager != null);
-            Debug.Assert(_PRIonAppXInitialized);
-            Debug.Assert(ApplicationModel.IsUap);
-
-            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.
-        internal static WindowsRuntimeResourceManagerBase GetWinRTResourceManager()
-        {
-            Type WinRTResourceManagerType = Type.GetType("System.Resources.WindowsRuntimeResourceManager, System.Runtime.WindowsRuntime", throwOnError: true);
-            return (WindowsRuntimeResourceManagerBase)Activator.CreateInstance(WinRTResourceManagerType, true);
-        }
-#endif
-
-        private bool _bUsingModernResourceManagement; // Written only by SetAppXConfiguration
-
-#if FEATURE_APPX
-        private WindowsRuntimeResourceManagerBase _WinRTResourceManager; // Written only by SetAppXConfiguration
-
-        private bool _PRIonAppXInitialized; // Written only by SetAppXConfiguration
-
-        private PRIExceptionInfo _PRIExceptionInfo; // Written only by SetAppXConfiguration
-
-        // When running under AppX, the following rules apply for resource lookup:
-        //
-        // 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.
-        private bool ShouldUseSatelliteAssemblyResourceLookupUnderAppX(Assembly resourcesAssembly)
-        {
-            bool fUseSatelliteAssemblyResourceLookupUnderAppX = typeof(object).Assembly == resourcesAssembly;
-
-            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)(AppContext.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;
-                        }
-                    }
-                }
-            }
-
-            return fUseSatelliteAssemblyResourceLookupUnderAppX;
-        }
-#endif // FEATURE_APPX
-
-        // Only call SetAppXConfiguration from ResourceManager constructors, and nowhere else.
-        // Throws MissingManifestResourceException and WinRT HResults
-
-        private void SetAppXConfiguration()
-        {
-            Debug.Assert(_bUsingModernResourceManagement == false); // Only this function writes to this member
-#if FEATURE_APPX
-            Debug.Assert(_WinRTResourceManager == null); // Only this function writes to this member
-            Debug.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member
-            Debug.Assert(_PRIExceptionInfo == null); // Only this function writes to this member
-
-            bool bUsingSatelliteAssembliesUnderAppX = false;
-
-            if (MainAssembly != null)
-            {
-                if (MainAssembly != typeof(object).Assembly) // We are not loading resources for mscorlib
-                {
-                    if (ApplicationModel.IsUap)
-                    {
-                        // 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;
-
-                        if (!bUsingSatelliteAssembliesUnderAppX)
-                        {
-                            _bUsingModernResourceManagement = !ShouldUseSatelliteAssemblyResourceLookupUnderAppX(MainAssembly);
-
-                            if (_bUsingModernResourceManagement)
-                            {
-                                // Only now are we certain that we need the PRI file.
-
-                                // 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.
-
-                                _WinRTResourceManager = GetWinRTResourceManager();
-
-                                try
-                                {
-                                    _PRIonAppXInitialized = _WinRTResourceManager.Initialize(MainAssembly.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.
-                                }
-
-                                if (!_PRIonAppXInitialized)
-                                {
-                                    _bUsingModernResourceManagement = false;
-                                }
-                                // 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.
-                            }
-                        }
-                    }
-                }
-            }
-            // MainAssembly == 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.
@@ -819,86 +607,62 @@ namespace System.Resources
             if (null == name)
                 throw new ArgumentNullException(nameof(name));
 
-#if FEATURE_APPX
-            if (_bUsingModernResourceManagement)
+#if FEATURE_APPX || ENABLE_WINRT
+            if (UseUapResourceManagement)
             {
-                // If the caller explicitly 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.
-                if (object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
-                {
-                    culture = null;
-                }
-
-                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(SR.Format(SR.MissingManifestResource_ResWFileNotLoaded, _PRIExceptionInfo.ResWFile, _PRIExceptionInfo.PackageSimpleName));
-
-                    throw new MissingManifestResourceException(SR.MissingManifestResource_NoPRIresources);
-                }
-
                 // Throws WinRT hresults.
-                return GetStringFromPRI(name,
-                                        culture == null ? null : culture.Name,
-                                        _neutralResourcesCulture.Name);
+                return GetStringFromPRI(name, culture, _neutralResourcesCulture.Name);
             }
-            else
-#endif // FEATURE_APPX
+#endif
+
+            if (culture == null)
             {
-                if (culture == null)
-                {
-                    culture = CultureInfo.CurrentUICulture;
-                }
+                culture = CultureInfo.CurrentUICulture;
+            }
 
-                ResourceSet last = GetFirstResourceSet(culture);
+            ResourceSet last = GetFirstResourceSet(culture);
 
-                if (last != null)
-                {
-                    string value = last.GetString(name, _ignoreCase);
-                    if (value != null)
-                        return value;
-                }
+            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;
 
-                // 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)
+                if (rs != last)
                 {
-                    ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
-                    if (rs == null)
-                        break;
-
-                    if (rs != last)
+                    string value = rs.GetString(name, _ignoreCase);
+                    if (value != null)
                     {
-                        string value = rs.GetString(name, _ignoreCase);
-                        if (value != null)
+                        // update last used ResourceSet
+                        if (_lastUsedResourceCache != null)
                         {
-                            // update last used ResourceSet
-                            if (_lastUsedResourceCache != null)
+                            lock (_lastUsedResourceCache)
                             {
-                                lock (_lastUsedResourceCache)
-                                {
-                                    _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
-                                    _lastUsedResourceCache.lastResourceSet = rs;
-                                }
+                                _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
+                                _lastUsedResourceCache.lastResourceSet = rs;
                             }
-                            return value;
                         }
-
-                        last = rs;
+                        return value;
                     }
+
+                    last = rs;
                 }
             }
 
             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.