Skip to content

Instantly share code, notes, and snippets.

@PedroMGMendes
Created March 3, 2020 11:21
Show Gist options
  • Save PedroMGMendes/ffde0af9feed7a18960d28f18a5f9a0d to your computer and use it in GitHub Desktop.
Save PedroMGMendes/ffde0af9feed7a18960d28f18a5f9a0d to your computer and use it in GitHub Desktop.
C# custom ResourceManager loader for usage with ILRepack.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Resources;
namespace ILRepack
{
/// <summary>
/// SingleAssembly Resource Manager.
/// </summary>
/// <seealso cref="System.Resources.ResourceManager" />
public class SingleAssemblyResourceManager : ResourceManager
{
private Dictionary<string, ResourceSet> singleAssemblyResourceSets;
private string[] singleAssemblyResouceNames;
/// <summary>
/// Initializes a new instance of the <see cref="SingleAssemblyResourceManager" /> class.
/// </summary>
/// <param name="baseName">The root name of the resource file without its extension but including any fully qualified namespace name. For example, the root name for the resource file named MyApplication.MyResource.en-US.resources is MyApplication.MyResource.</param>
/// <param name="assembly">The main assembly for the resources.</param>
public SingleAssemblyResourceManager(string baseName, Assembly assembly)
: base(baseName, assembly)
{
this.singleAssemblyResourceSets = new Dictionary<string, ResourceSet>();
this.singleAssemblyResouceNames = this.MainAssembly.GetManifestResourceNames();
}
/// <summary>
/// Internals the get resource set.
/// </summary>
/// <param name="culture">The culture.</param>
/// <param name="createIfNotExists">if set to <c>true</c> [create if not exists].</param>
/// <param name="tryParents">if set to <c>true</c> [try parents].</param>
/// <returns>sample jk.</returns>
protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
{
// intentional behaviour always try to fallback to parent cultures
while (culture != null && culture != CultureInfo.InvariantCulture)
{
// Look in the Single Assembly ResourceSet table
Dictionary<string, ResourceSet> localResourceSets = this.singleAssemblyResourceSets;
ResourceSet rs = null;
if (localResourceSets != null)
{
lock (localResourceSets)
{
localResourceSets.TryGetValue(culture.Name, out rs);
}
}
if (rs != null)
{
return rs;
}
var cultureNameResource = this.FindResourceNameIgnoreCase(this.BaseName + "." + culture.Name + ".resources");
if (!string.IsNullOrWhiteSpace(cultureNameResource))
{
var resourceStream = this.MainAssembly.GetManifestResourceStream(cultureNameResource);
if (resourceStream != null)
{
rs = new ResourceSet(resourceStream);
AddResourceSet(localResourceSets, culture.Name, ref rs);
return rs;
}
}
culture = culture.Parent;
}
var resSet = base.InternalGetResourceSet(culture, createIfNotExists, tryParents);
return resSet;
}
// Simple helper to ease maintenance and improve readability.
private string FindResourceNameIgnoreCase(string resourceName)
{
foreach (var item in this.singleAssemblyResouceNames)
{
if (item.Equals(resourceName, StringComparison.OrdinalIgnoreCase))
{
return item;
}
}
return string.Empty;
}
// 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);
}
}
}
/// <summary>
/// Tells the resource manager to call the <see cref="M:System.Resources.ResourceSet.Close"></see> method on all <see cref="T:System.Resources.ResourceSet"></see> objects and release all resources.
/// </summary>
public override void ReleaseAllResources()
{
base.ReleaseAllResources();
Dictionary<string, ResourceSet> localResourceSets = this.singleAssemblyResourceSets;
// If any calls to Close throw, at least leave ourselves in a
// consistent state.
this.singleAssemblyResourceSets = new Dictionary<string, ResourceSet>();
lock (localResourceSets)
{
foreach (var item in localResourceSets)
{
item.Value.Close();
}
}
}
/// <summary>
/// Returns the value of the string resource localized for the specified culture.
/// </summary>
/// <param name="name">The name of the resource to retrieve.</param>
/// <param name="culture">An object that represents the culture for which the resource is localized.</param>
/// <returns>
/// The value of the resource localized for the specified culture, or null if <paramref name="name">name</paramref> cannot be found in a resource set.
/// </returns>
public override string GetString(string name, CultureInfo culture)
{
return base.GetString(name, culture);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment