单组装的多语言的Windows窗体部署(ILMerge和卫星组件/本地化) - 可能吗? [英] Single-assembly multi-language Windows Forms deployment (ILMerge and satellite assemblies / localization) - possible?

查看:276
本文介绍了单组装的多语言的Windows窗体部署(ILMerge和卫星组件/本地化) - 可能吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a simple Windows Forms (C#, .NET 2.0) application, built with Visual Studio 2008.

我想支持多用户界面语言,并且使用形式的本地化的属性,文化特有的.resx文件,本地化方面的无缝和容易。 Visual Studio会自动编译区域性特定的resx文件到附属程序集,所以在我的编译的应用程序文件夹中有含有这些附属程序集的区域性特定的子文件夹。

I would like to support multiple UI languages, and using the "Localizable" property of the form, and culture-specific .resx files, the localization aspect works seamlessly and easily. Visual Studio automatically compiles the culture-specific resx files into satellite assemblies, so in my compiled application folder there are culture-specific subfolders containing these satellite assemblies.

我想有应用程序部署(复制到的地方)为单一组装,但保留包含多套区域性特定资源的能力。

I would like to have the application be deployed (copied into place) as a single assembly, and yet retain the ability to contain multiple sets of culture-specific resources.

使用<一个href="http://www.microsoft.com/downloads/details.aspx?familyid=22914587-b4ad-4eae-87cf-b14ae6a939b0&displaylang=en#Overview">ILMerge (或 ILRepack ),我可以合并的附属程序集到主可执行程序集,但该标准的.NET ResourceManager的回退机制做没有发现被编译到主组件中的培养特定资源。

Using ILMerge (or ILRepack), I can merge the satellite assemblies into the main executable assembly, but the standard .NET ResourceManager fallback mechanisms do not find the culture-specific resources that were compiled into the main assembly.

有趣的是,如果我把我的合并(可执行文件)装配及地点将其复制到文化特有的子文件夹,然后一切正常!同样,我可以看到主要的和区域性特定资源的合并assemby当我使用反射(或 ILSpy )。但复制的主要组装成区域性特定子击败合并的目的,反正 - 我真的需要有成为单一组件的只是一个单一的副本...

Interestingly, if I take my merged (executable) assembly and place copies of it into the culture-specific subfolders, then everything works! Similarly, I can see the main and culture-specific resources in the merged assemby when I use Reflector (or ILSpy). But copying the main assembly into culture-specific subfolders defeats the purpose of the merging anyway - I really need there to be just a single copy of the single assembly...

我想知道的是否有任何的方式来劫持或影响ResourceManager中回退机制,以寻找特定于区域性的资源在同一个组件,而不是在GAC和文化命名的子文件夹。我看到在下面的文章中描述的回退机制,但没有线索,如何将被修改:<一href="http://blogs.msdn.com/bclteam/archive/2009/02/16/working-with-the-resourcemanager-kim-hamilton.aspx">BCL团队博客文章上的ResourceManager 。

I'm wondering whether there is any way to hijack or influence the ResourceManager fallback mechanisms to look for the culture-specific resources in the same assembly rather than in the GAC and culture-named subfolders. I see the fallback mechanism described in the following articles, but no clue as to how it would be modified: BCL Team Blog Article on ResourceManager.

有没有人有什么想法?这似乎是一个比较常见的问题,在网上(例如,在这里对堆栈溢出了另一个问题: ILMerge和本地化的资源组件的),但我还没有发现任何权威的答案任何地方。

Does anyone have any idea? This seems to be a relatively frequent question online (for example, another question here on Stack Overflow: "ILMerge and localized resource assemblies"), but I have not found any authoritative answer anywhere.

随着<一href="http://stackoverflow.com/questions/1952638/single-assembly-multi-language-windows-forms-deployment-ilmerge-and-satellite-as/1955060#1955060">casperOne's下面建议,我终于能够使这项工作。

Following casperOne's recommendation below, I was finally able to make this work.

我把解决code在这里的问题,因为casperOne提供的唯一的答案,我不想增加我自己的。

I'm putting the solution code here in the question because casperOne provided the only answer, I don't want to add my own.

我能得到它拉动的胆量了,在InternalGetResourceSet的方法实现的框架资源调查回退机制,使我们相同的组件搜索第一用于工作机制。如果未在当前汇编中发现的资源,那么我们就调用基方法来启动默认的搜索机制(感谢以下@沃特的评论)。

I was able to get it to work by pulling the guts out of the Framework resource-finding fallback mechanisms implemented in the "InternalGetResourceSet" method and making our same-assembly search the first mechanism used. If the resource is not found in the current assembly, then we call the base method to initiate the default search mechanisms (thanks to @Wouter's comment below).

要做到这一点,我得出了ComponentResourceManager级,并推翻了只有一个方法(和重新实现一个私有的框架方法):

To do this, I derived the "ComponentResourceManager" class, and overrode just one method (and re-implemented a private framework method):

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);
            }
        }
    }
}

要真正使用这个类,你需要更换System.ComponentModel.ComponentResourceManager在XXX.Designer.cs由Visual Studio创建的文件 - 你会需要这个每次更改设计的表格的时间做 - 可视化Studio将自动替换code。 (该问题已在讨论<一href="http://social.msdn.microsoft.com/Forums/en/vsx/thread/c87c991d-78cf-4fa3-aa7d-c3531fddbe88">Customize Windows窗体设计使用MyResourceManager ,我没有找到一个更好的解决方案 - 我用 fart.exe 在pre-生成步骤来自动替换。)

To actually use this class, you need to replace the System.ComponentModel.ComponentResourceManager in the "XXX.Designer.cs" files created by Visual Studio - and you will need to do this every time you change the designed form - Visual Studio replaces that code automatically. (The problem was discussed in "Customize Windows Forms Designer to use MyResourceManager", I did not find a more elegant solution - I use fart.exe in a pre-build step to auto-replace.)

目前报我的解决方案上面,我其实是只支持两种语言,并ILMerge在做合并我的卫星组装成最终的合并装配了很好的工作,时间

At the time I reported the solution above, I was actually only supporting two languages, and ILMerge was doing a fine job of merging my satellite assembly into the final merged assembly.

最近,我开始工作的一个类似的项目,其中有的辅助语言,因此,多个卫星装配和ILMerge是做一些很奇怪:不是合并多个附属程序集我已要求,它被合并在多次的第一颗卫星组装!

Recently I started working on a similar project where there are multiple secondary languages, and therefore multiple satellite assemblies, and ILMerge was doing something very strange: Instead of merging the multiple satellite assemblies I had requested, it was merging the first satellite assembly in multiple times!

如命令行:

"c:\Program Files\Microsoft\ILMerge\ILMerge.exe" /t:exe /out:%1SomeFinalProg.exe %1InputProg.exe %1es\InputProg.resources.dll %1fr\InputProg.resources.dll

使用的命令行,我得到的资源的以下设置在合并后的组件(具有ILSpy反编译器观察到的):

With that command-line, I was getting the following sets of resources in the merged assembly (observed with ILSpy decompiler):

InputProg.resources
InputProg.es.resources
InputProg.es.resources <-- Duplicated!

经过一番摆弄,我最终意识到这是ILMerge只是一个错误遇到的多个在一个命令行调用相同的名称文件时。解决的办法很简单,就是在不同的命令行调用合并的每个附属程序集:

After some playing around, I ended up realizing this is just a bug in ILMerge when it encounters multiple files with the same name in a single command-line call. The solution is simply to merge each satellite assembly in a different command-line call:

"c:\Program Files\Microsoft\ILMerge\ILMerge.exe" /t:exe /out:%1TempProg.exe %1InputProg.exe %1es\InputProg.resources.dll
"c:\Program Files\Microsoft\ILMerge\ILMerge.exe" /t:exe /out:%1SomeFinalProg.exe %1TempProg.exe %1fr\InputProg.resources.dll

当我做到这一点,在最后的组装造成的资源是正确的:

When I do this, the resulting resources in the final assembly are correct:

InputProg.resources
InputProg.es.resources
InputProg.fr.resources

所以最后,如果这有助于澄清一下,这里有一个完整的后期生成批处理文件:

So finally, in case this helps clarify, here's a complete post-build batch file:

"%ProgramFiles%\Microsoft\ILMerge\ILMerge.exe" /t:exe /out:%1TempProg.exe %1InputProg.exe %1es\InputProg.resources.dll 
IF %ERRORLEVEL% NEQ 0 GOTO END

"%ProgramFiles%\Microsoft\ILMerge\ILMerge.exe" /t:exe /out:%1SomeFinalProg.exe %1TempProg.exe %1fr\InputProg.resources.dll 
IF %ERRORLEVEL% NEQ 0 GOTO END

del %1InputProg.exe 
del %1InputProg.pdb 
del %1TempProg.exe 
del %1TempProg.pdb 
del %1es\*.* /Q 
del %1fr\*.* /Q 
:END


更新3:ILRepack

另一个要点 - 一个困扰我与ILMerge是,它是一个额外的专有的微软工具,默认情况下不与Visual Studio,因此额外的依赖,使得它那一点点更难第三方安装的东西开始使用我的开源项目。


UPDATE 3: ILRepack

Another quick note - One of the things that bothered me with ILMerge was that it is an additional proprietary Microsoft tool, not installed by default with Visual Studio, and therefore an extra dependency that makes it that little bit harder for a third party to get started with my open-source projects.

我最近发现 ILRepack ,一个开源的(Apache 2.0中),相当于到目前为止的作品一样好,我(插入式更换),并且可以自由发布与您的项目来源。

I recently discovered ILRepack, an open-source (Apache 2.0) equivalent that so far works just as well for me (drop-in replacement), and can be freely distributed with your project sources.

我希望这有助于有人出来!

I hope this helps someone out there!

推荐答案

我可以看到这个工作的唯一方法是通过创建一个派生自<一类href="http://msdn.microsoft.com/en-us/library/system.resources.resourcemanager.aspx"><$c$c>ResourceManager然后重写<一href="http://msdn.microsoft.com/en-us/library/system.resources.resourcemanager.internalgetresourceset.aspx"><$c$c>InternalGetResourceSet和<一href="http://msdn.microsoft.com/en-us/library/system.resources.resourcemanager.getresourcefilename.aspx"><$c$c>GetResourceFileName的方法。从那里,你应该能够覆盖得到的资源在哪里,给定一个<一个href="http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.aspx"><$c$c>CultureInfo实例。

The only way I can see this working is by creating a class that derives from ResourceManager and then overriding the InternalGetResourceSet and GetResourceFileName methods. From there, you should be able to override where resources are obtained, given a CultureInfo instance.

这篇关于单组装的多语言的Windows窗体部署(ILMerge和卫星组件/本地化) - 可能吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆