gpt4 book ai didi

c# - 单程序集多语言 Windows 窗体部署(ILMerge 和附属程序集/本地化) - 可能吗?

转载 作者:IT王子 更新时间:2023-10-29 03:48:03 24 4
gpt4 key购买 nike

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

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

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

使用 ILMerge (或 ILRepack ),我可以将附属程序集合并到主可执行程序集,但标准的 .NET ResourceManager 回退机制找不到编译到主程序集的特定于文化的资源。

有趣的是,如果我将合并的(可执行的)程序集放入特定于文化的子文件夹中,则一切正常!同样,当我使用 Reflector 时,我可以看到合并程序集中的主要和特定于文化的资源。 (或 ILSpy)。但是将主程序集复制到特定于文化的子文件夹中无论如何都会破坏合并的目的 - 我真的需要单个程序集的单个副本......

我在想 是否有任何方法可以劫持或影响 ResourceManager 回退机制以在同一程序集中而不是在 GAC 和文化命名的子文件夹中查找特定于文化的资源 .我看到以下文章中描述的回退机制,但不知道如何修改它:BCL Team Blog Article on ResourceManager .

有谁有想法吗?这似乎是网上比较常见的一个问题(例如 Stack Overflow 上的另一个问题:“ILMerge and localized resource assemblies”),但我没有在任何地方找到任何权威的答案。

更新 1:基本解决方案

关注 casperOne's recommendation below ,我终于能够完成这项工作。

我将解决方案代码放在问题中,因为 casperOne 提供了唯一的答案,我不想添加自己的答案。

我能够通过从“InternalGetResourceSet”方法中实现的框架资源查找回退机制中抽出勇气并让我们的相同程序集搜索 来使其工作。第一 使用的机制。如果在当前程序集中没有找到资源,那么我们调用 base 方法来启动默认搜索机制(感谢@Wouter 在下面的评论)。

为此,我派生了“ComponentResourceManager”类,并仅覆盖了一个方法(并重新实现了一个私有(private)框架方法):

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 会替换它自动编码。 (该问题在“ Customize Windows Forms Designer to use MyResourceManager ”中讨论,我没有找到更优雅的解决方案 - 我在预构建步骤中使用 fart.exe 来自动替换。)

更新 2:另一个实际考虑 - 超过 2 种语言

在我报告上述解决方案时,我实际上只支持两种语言,而 ILMerge 在将我的附属程序集合并到最终合并程序集方面做得很好。

最近我开始从事一个类似的项目,其中有多种辅助语言,因此有多个附属程序集,而 ILMerge 做了一些非常奇怪的事情:它没有合并我请求的多个附属程序集,而是多次合并第一个附属程序集!

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

使用该命令行,我在合并的程序集中获得了以下资源集(使用 ILSpy 反编译器观察):
InputProg.resources
InputProg.es.resources
InputProg.es.resources <-- Duplicated!

经过一番尝试,我最终意识到这只是 ILMerge 遇到 时的一个错误。多个同名文件 在单个命令行调用中。解决方案只是在不同的命令行调用中合并每个附属程序集:
"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

当我这样做时,最终装配中的结果资源是正确的:
InputProg.resources
InputProg.es.resources
InputProg.fr.resources

最后,如果这有助于澄清,这里有一个完整的构建后批处理文件:
"%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 困扰我的一件事是它是一个额外的专有 Microsoft 工具,默认情况下没有安装在 Visual Studio 中,因此额外的依赖使第三方上手变得有点困难与我的开源项目。

我最近发现 ILRepack ,一个开源 (Apache 2.0) 等价物,到目前为止对我来说也同样适用(直接替换),并且可以随您的项目源免费分发。

我希望这可以帮助那里的人!

最佳答案

我能看到这个工作的唯一方法是创建一个派生自 ResourceManager 的类。然后覆盖 InternalGetResourceSet GetResourceFileName 方法。从那里,您应该能够覆盖获取资源的位置,给定 CultureInfo 实例。

关于c# - 单程序集多语言 Windows 窗体部署(ILMerge 和附属程序集/本地化) - 可能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1952638/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com