单组装件多语言Windows窗体部署(ILMerge和附属组装件/本地化)-可能吗?

我有一个使用Visual Studio 2008构建的简单Windows窗体(C#、. NET 2.0)应用程序。


我想支持多种UI语言,并使用表单的“ Localizable”属性以及特定于文化的.resx文件,本地化方面可以无缝且轻松地工作。Visual Studio会自动将区域性特定的resx文件编译为附属程序集,因此在我的已编译应用程序文件夹中,包含这些区域性程序集的特定于区域性的子文件夹。


我希望将应用程序作为单个程序集部署(复制到位),但仍保留包含多组特定于文化的资源的能力。


使用ILMerge(或ILRepack),我可以将附属程序集合并到主要的可执行程序集中,但是标准的.NET ResourceManager后备机制找不到编译到主要程序集中的特定于区域性的资源。


有趣的是,如果将合并的(可执行)程序集放入其特定于区域性的子文件夹中,则一切正常!同样,当我使用Reflector(或ILSpy)时,可以在合并的assemby中看到主要资源和特定于文化的资源。但是将主程序集复制到特定于文化的子文件夹中仍然无法实现合并的目的-我真的需要只有单个程序集的一个副本...


我想知道是否有任何方法可以劫持或影响ResourceManager后备机制,以在同一程序集中而不是在GAC和以文化命名的子文件夹中查找特定于文化的资源。我看到了以下文章中描述的回退机制,但是没有关于如何修改的线索:Resource Manager上的BCL团队博客文章。


有谁有想法吗?这似乎是一个在线上相对频繁的问题(例如,Stack Overflow上的另一个问题:“ ILMerge和本地化资源程序集 ”),但是我在任何地方都没有找到任何权威性的答案。


有只小跳蛙
浏览 496回答 3
3回答

交互式爱情

基本解决方案遵循casperOne的建议,我终于能够完成这项工作。我将解决方案代码放在问题中,因为casperOne提供了唯一的答案,我不想添加自己的答案。通过将胆量从“ InternalGetResourceSet”方法中实现的Framework资源查找后备机制中剔除,并使我们的程序集搜索成为第一个使用的机制,我能够使其工作。如果在当前程序集中找不到该资源,则我们调用base方法来启动默认的搜索机制(由于下面的@Wouter注释)。为此,我派生了“ ComponentResourceManager”类,并覆盖了仅一个方法(并重新实现了私有框架方法):class SingleAssemblyComponentResourceManager :     System.ComponentModel.ComponentResourceManager{    private Type _contextTypeInfo;    private CultureInfo _neutralResourcesCulture;    public SingleAssemblyComponentResourceManager(Type t)        : base(t)    {        _contextTypeInfo = t;    }    protected override ResourceSet InternalGetResourceSet(CultureInfo culture,         bool createIfNotExists, bool tryParents)    {        ResourceSet rs = (ResourceSet)this.ResourceSets[culture];        if (rs == null)        {            Stream store = null;            string resourceFileName = null;            //lazy-load default language (without caring about duplicate assignment in race conditions, no harm done);            if (this._neutralResourcesCulture == null)            {                this._neutralResourcesCulture =                     GetNeutralResourcesLanguage(this.MainAssembly);            }            // if we're asking for the default language, then ask for the            // invariant (non-specific) resources.            if (_neutralResourcesCulture.Equals(culture))                culture = CultureInfo.InvariantCulture;            resourceFileName = GetResourceFileName(culture);            store = this.MainAssembly.GetManifestResourceStream(                this._contextTypeInfo, resourceFileName);            //If we found the appropriate resources in the local assembly            if (store != null)            {                rs = new ResourceSet(store);                //save for later.                AddResourceSet(this.ResourceSets, culture, ref rs);            }            else            {                rs = base.InternalGetResourceSet(culture, createIfNotExists, tryParents);            }        }        return rs;    }    //private method in framework, had to be re-specified here.    private static void AddResourceSet(Hashtable localResourceSets,         CultureInfo culture, ref ResourceSet rs)    {        lock (localResourceSets)        {            ResourceSet objA = (ResourceSet)localResourceSets[culture];            if (objA != null)            {                if (!object.Equals(objA, rs))                {                    rs.Dispose();                    rs = objA;                }            }            else            {                localResourceSets.Add(culture, rs);            }        }    }}要实际使用此类,您需要在Visual Studio创建的“ XXX.Designer.cs”文件中替换System.ComponentModel.ComponentResourceManager-并且每次更改设计表单时都需要执行此操作-Visual Studio会替换该类自动编码。(该问题在“ 自定义Windows窗体设计器以使用MyResourceManager ”中进行了讨论,但没有找到更优雅的解决方案-我在预构建步骤中使用fart.exe来自动替换。)

米脂

另一个快速说明-使用ILMerge困扰我的一件事是它是附加的专有Microsoft工具,默认情况下未随Visual Studio一起安装,因此存在额外的依赖关系,这使得第三方上手有点困难与我的开源项目。我最近发现了ILRepack,它是一种等效于开源(Apache 2.0)的工具,到目前为止,它对我也可以正常工作(直接替换),并且可以随项目源自由分发。我希望这可以帮助某个人!
打开App,查看更多内容
随时随地看视频慕课网APP