// Obtains a resource string given the name of the string resource, and optional starting // and neutral resource cultures (e.g. "de-DE"). // Thread-safe provided that the call to Initialize() happened only once on one thread // and has already completed successfully, and that the WinRT APIs called below // continue to be thread-safe. // Throws exceptions public override String GetString(String stringName, String startingCulture, String neutralResourcesCulture) { Debug.Assert(stringName != null); Debug.Assert(_resourceMap != null); // Should have been initialized by now ResourceCandidate resourceCandidate = null; stringName = UriUtility.UriEncode(stringName); if (startingCulture == null && neutralResourcesCulture == null) { #pragma warning disable 618 resourceCandidate = _resourceMap.GetValue(stringName); #pragma warning restore 618 } else { Debug.Assert(_clonedResourceContext != null); // Should have been initialized by now Debug.Assert(_clonedResourceContextFallBackList != null); // Should have been initialized by now Debug.Assert(s_globalResourceContextFallBackList != null); // Should have been initialized by now Debug.Assert(s_globalResourceContextFallBackList.Length > 0); // Should never be empty // We need to modify the culture fallback list used by the Modern Resource Manager // The starting culture has to be looked up first, and neutral resources culture has // to be looked up last. string newResourceFallBackList = null; newResourceFallBackList = (startingCulture == null ? "" : startingCulture + ";") + s_globalResourceContextFallBackList + // Our tests do not test this line of code, so be extra careful if you modify or move it. (neutralResourcesCulture == null ? "" : ";" + neutralResourcesCulture); lock (_clonedResourceContext) { // s_globalResourceContextFallBackList may have changed on another thread by now. // We cannot prevent that from happening because it may have happened on a native // thread, and we do not share the same lock mechanisms with native code. // The worst that can happen is that a string is unexpectedly missing // or in the wrong language. if (!String.Equals(newResourceFallBackList, _clonedResourceContextFallBackList, StringComparison.Ordinal)) { _clonedResourceContext.Languages = StringToReadOnlyList(newResourceFallBackList); _clonedResourceContextFallBackList = newResourceFallBackList; } resourceCandidate = _resourceMap.GetValue(stringName, _clonedResourceContext); } } if (resourceCandidate != null) { return(resourceCandidate.ValueAsString); } return(null); }
// Obtain instances of the Resource Map and Resource Context provided by // the Windows Modern Resource Manager (MRM). // Not thread-safe. Only call this once on one thread for each object instance. // For example, System.Runtime.ResourceManager only calls this from its constructors, // guaranteeing that this only gets called once, on one thread, for each new instance // of System.Runtime.ResourceManager. // Throws exceptions // Only returns true if the function succeeded completely. // Outputs exceptionInfo since it may be needed for debugging purposes // if an exception is thrown by one of Initialize's callees. public override bool Initialize(string libpath, string reswFilename, out PRIExceptionInfo exceptionInfo) { Debug.Assert(libpath != null); Debug.Assert(reswFilename != null); exceptionInfo = null; if (InitializeStatics()) { // AllResourceMaps can throw ERROR_MRM_MAP_NOT_FOUND, // although in that case we are not sure for which package it failed to find // resources (if we are looking for resources for a framework package, // it might throw ERROR_MRM_MAP_NOT_FOUND if the app package // resources could not be loaded, even if the framework package // resources are properly configured). So we will not fill in the // exceptionInfo structure at this point since we don't have any // reliable information to include in it. IReadOnlyDictionary <String, ResourceMap> resourceMapDictionary = s_globalResourceManager.AllResourceMaps; if (resourceMapDictionary != null) { string packageSimpleName = FindPackageSimpleNameForFilename(libpath); #if netstandard // If we have found a simple package name for the assembly, lets make sure it is not *.resource.dll that // an application may have packaged in its AppX. This is to enforce AppX apps to use PRI resources. if (packageSimpleName != null) { if (packageSimpleName.EndsWith(".resources.dll", StringComparison.CurrentCultureIgnoreCase)) { // Pretend we didn't get a package name. When an attempt is made to get resource string, GetString implementation // will see that we are going to use modern resource manager but we don't have PRI and will thrown an exception indicating // so. This will force the developer to have a valid PRI based resource. packageSimpleName = null; } } #endif // netstandard if (packageSimpleName != null) { ResourceMap packageResourceMap = null; // Win8 enforces that package simple names are unique (for example, the App Store will not // allow two apps with the same package simple name). That is why the Modern Resource Manager // keys access to resources based on the package simple name. if (resourceMapDictionary.TryGetValue(packageSimpleName, out packageResourceMap)) { if (packageResourceMap != null) { // GetSubtree returns null when it cannot find resource strings // named "reswFilename/*" for the package we are looking up. reswFilename = UriUtility.UriEncode(reswFilename); _resourceMap = packageResourceMap.GetSubtree(reswFilename); if (_resourceMap == null) { exceptionInfo = new PRIExceptionInfo(); exceptionInfo._PackageSimpleName = packageSimpleName; exceptionInfo._ResWFile = reswFilename; } else { _clonedResourceContext = s_globalResourceContext.Clone(); if (_clonedResourceContext != null) { // Will need to be changed the first time it is used. But we can't set it to "" since we will take a lock on it. _clonedResourceContextFallBackList = ReadOnlyListToString(_clonedResourceContext.Languages); if (_clonedResourceContextFallBackList != null) { return(true); } } } } } } } } return(false); }