【问题标题】:Same source, multiple targets with different resources (Visual Studio .Net 2008)相同的源,具有不同资源的多个目标(Visual Studio .Net 2008)
【发布时间】:2023-03-12 18:39:01
【问题描述】:

一组软件产品的区别仅在于它们的资源字符串、二进制资源以及它们的 Visual Studio 安装项目使用的字符串/图形/产品密钥。创建、组织和维护它们的最佳方式是什么?

即所有产品本质上都包含相同的核心功能,这些功能由图形、字符串和其他资源数据定制以形成每个产品。 假设您正在创建一组产品,如“银行家 Excel”、“园丁用 Excel”、“CEO 用 Excel”等。每个产品具有相同的功能,但名称、图形、帮助文件不同,包括模板等。

构建这些的环境是:vanilla Windows.Forms / Visual Studio 2008 / C# / .Net。

理想的解决方案应该易于维护。例如如果我引入一个新的字符串/新资源项目,我没有添加资源应该在编译时失败,而不是运行时。 (而且后续的产品本地化也应该是可行的)。

希望我错过了做这一切的明显而简单的方法。这是什么?

============ 澄清================

“产品”是指由安装程序安装并出售给最终用户的软件包。

目前我有一个解决方案,由多个项目(包括一个安装项目)组成,它构建一组程序集并创建一个安装程序。

我需要生产多个产品/安装程序,它们都具有相似的功能,它们是从同一组程序集构建的,但其中一个程序集使用的资源集不同。这样做的最佳方法是什么?

------------ 95%的解决方案-----

根据 Daminen_the_unbeliever 的回答,每个配置的资源文件可以实现如下:

  1. 创建一个类库项目(“Satellite”)。
  2. 删除默认 .cs 文件并添加文件夹(“默认”)
  3. 在“MyResources”文件夹中创建资源文件
  4. 属性 - 将 CustomToolNamespace 设置为某物 适当的(例如“XXX”)
  5. 确保资源的访问修饰符是“公共”。添加 资源。编辑源代码。 参考代码中的资源 作为 XXX.MyResources.ResourceName)
  6. 为每个产品变体创建配置(“ConfigN”)
  7. 为每个产品变体创建一个文件夹(“VariantN”)
  8. 将 MyResources 文件复制并粘贴到每个 VariantN 文件夹中
  9. 卸载“Satellite”项目,并编辑 .csproj 文件
  10. 对于每个“VariantN/MyResources”<Compile> 或 <EmbeddedResource> 标签, 添加Condition="'$(Configuration)' == 'ConfigN'" 属性。
  11. 保存,重新加载 .csproj,大功告成...

这将创建一个按配置的资源文件,该文件(可能)可以进一步本地化。对于缺少资源的任何配置,都会生成编译错误消息。可以使用标准方法对资源文件进行本地化(创建第二个资源文件 (MyResources.fr.resx) 并像以前一样编辑 .csproj)。

这是一个 95% 的解决方案的原因是用于初始化表单的资源(例如表单标题、按钮文本)不能以相同的方式轻松处理 - 最简单的方法似乎是用来自卫星组装。

【问题讨论】:

  • 这个问题对我来说有点不清楚。我真的建议您查看您最初的问题并使用 Visual Studio 中的技术术语重新发布,并隔离您的问题。
  • 添加了说明。目前正在考虑根据构建配置编辑 .csproj 文件以包含替换资源文件。维护混乱。有人知道这是否实用吗?
  • 还考虑创建一个工具来比较资源以确保两个文件中存在相同的资源集以减少回归测试...有人知道这样的工具是否存在吗?
  • 如果资源丢失,95% 的解决方案会出现编译错误...现在如果我能强制表单设计器为选定的资源使用单独的资源文件...谢谢大家的帮助。

标签: c# .net visual-studio-2008


【解决方案1】:

您可以向 MSBuild 文件中的元素添加条件。例如,如果您有“Debug”资源和“Release”资源,您可以将它们放在两个单独的文件夹中(例如 Debug 和 Release)。然后,在您的 MSBuild 文件中,您可能有:

  <ItemGroup>
    <Compile Include="Debug\Resource1.Designer.cs" Condition=" '$(Configuration)' == 'Debug' ">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resource1.resx</DependentUpon>
    </Compile>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Queue.cs" />
    <Compile Include="Release\Resource1.Designer.cs" Condition=" '$(Configuration)' == 'Release' ">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Resource1.resx</DependentUpon>
    </Compile>
    <Compile Include="Stack.cs" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="XMLFile1.xml" />
  </ItemGroup>
  <ItemGroup>
    <EmbeddedResource Include="Debug\Resource1.resx" Condition=" '$(Configuration)' == 'Debug' ">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resource1.Designer.cs</LastGenOutput>
      <CustomToolNamespace>Resources</CustomToolNamespace>
    </EmbeddedResource>
    <EmbeddedResource Include="Release\Resource1.resx" Condition=" '$(Configuration)' == 'Release' ">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resource1.Designer.cs</LastGenOutput>
      <CustomToolNamespace>Resources</CustomToolNamespace>
    </EmbeddedResource>
  </ItemGroup>

如果您对资源的所有访问都通过 Resources.Resource1 类,那么您将获得两组不同的资源用于调试和发布版本。显然,这可以扩展到更多的配置。

不幸的是,我认为您不能强制资源使用相同的 baseName(提供给 ResourceManager 构造函数),因为它基于项目中的路径,而我找不到覆盖它的方法。如果您确实需要它们使用相同的名称(例如,如果您正在手动创建 ResourceManagers),那么我建议在项目的顶层使用 Resources1.resx(加上相关的 cs 文件),而不是在源头控制。作为预构建事件,根据需要从 Debug 或 Release 目录中复制所需的 .resx 文件。 (在这种情况下,您可能希望强制它不编译子目录中的 .Designer.cs 文件。

编辑

忘记提及(尽管在上面的 MSBuild 文件摘录中可以看到)您必须将每个 .resx 文件上的自定义工具命名空间设置为相同的值(例如资源),否则它也默认包含文件夹名字。

编辑 2

响应有关检查每个资源文件是否包含相同资源的查询 - 如果您使用 Resource 类(例如 Resources.Resource1.MyFirstStringResource)来访问您的资源,那么如果需要,切换配置将导致构建错误资源不存在,所以你会很快找到。

对于真正的偏执狂(即,如果您的构建过程需要 3 天来构建所有配置,或者同样疯狂),归根结底,.resx 文件只是 XML 文件 - 您只需要检查每个具有相同文件名的 .resx 文件包含相同数量的 元素,具有相同的名称属性。

【讨论】:

  • 我想这就是我要找的东西 - 只是想知道如何从这里到达那里......(除了 MSBuild 格式的参考文档之外找不到其他的,并且不确定它与IDE的交互。)看起来我应该为每个产品(在IDE中)创建1个额外的资源文件,每个都在一个单独的目录中,只在程序代码中引用资源,然后按照上面的编辑.csproj文件只在每个配置中编译/嵌入适当的资源。我的理解正确吗?
  • @Mike - 好吧,我的“游戏区”.csproj 文件的上述摘录只用了五分钟就完成了。它本身没有 IDE 支持,但在 IDE 中的配置之间切换时它编译得很好。一旦您创建了两个同名的资源文件(在不同的目录中)并更改了它们的命名空间,您至少会遇到构建错误,直到您对 csproj 文件进行了条件编辑。
【解决方案2】:

这是我的解决方案。最初的问题是 Autodesk 每 3 年发布一次具有不同内容的每个 AutoCAD dll 并破坏兼容性。但我们的代码其实是一样的。这是开发-TFS-TFSbuild-DomainControllerInstallationOfApps 方式中的一个痛点。

解决方法如下:

  1. 使用所有结构制作您的 MOTHER 项目,比如说 AutoCAD 2012 的参考。
  2. 然后在同一解决方案中为 AutoCAD 2013 创建第二个项目并引用其自己的 DLLS。
  3. 现在您必须为每个项目设置一个编译符号 - 第一个项目为 acad2012,第二个项目为 acad2013。
  4. 然后在 2013 项目中转到 Add Existing Item 并指向 2012 项目的源,然后单击 Add 按钮右侧并指向 Add As Link
  5. 如果在 2012 年和 2013 年之间存在任何库差异,您可以用#if acad2012 #endif or #if acad2013 #endif 将它们括起来

这样你将只需要维护一个源代码并且必须单独编译模块:)

更新

我需要生产多个产品/安装程序,它们都具有相似的功能,它们是从同一组程序集构建的,但其中一个程序集使用的资源集不同。这样做的最佳方法是什么?

  1. 设置安装程序项目:这正是我们所做的,也正是您所需要的。在第 5 步之后,您必须创建两个安装程序项目。我的意思是Windows Installer Xml projects or WiX projects 可以说第一个是 2012 年的,一个是 2013 年的。下一个问题是生成包含所有构建组件的 XML 文件。我使用带有以下脚本的 WiX 工具包:

    heat.exe dir "$(TargetDir)" -cg MyAppComponents -dr INSTALLFOLDER -sfrag -srd -var "var.FilesPath" -out ".\MyAppComponents.wxs"

此脚本使用您需要的所有文件构建 MyAppComponents.wxs。如果您想熟悉 Wix,请阅读这本书:“WiX 3.6: A Developer's Guide to Windows Installer XML”。看完后我做了所有需要的东西。

  1. 将 MyAppComponents.wxs 直接添加到您的主安装程序项目(让它适用于 2012)和任何其他项目“添加为链接”(让它适用于 2013 dll)。如果您按照步骤 5 中的说明进行操作,那么您的构建脚本已经针对不同版本的依赖程序集进行编译。您想要针对两种不同的依赖项编译一个源代码吗?这样你就拥有了它 - 一个包含所有内容的 .wxs,在 .cs 文件中你有指导编译的编译指示。

现在,当您将新文件添加到您的解决方案时,您需要将其添加到您的母项目中,然后“添加为链接”到您的其他项目,并仅在一处添加 .wxs 中的组件注册,以便分发在所有安装程序中。

使用第 6 步中的脚本,您将在一个 .wxs 中拥有所有版本化的 dll,这意味着它们将在一个安装程序中编译。当然,您可以为所有版本制作多个不同的安装程序,但从长远来看,制作一个安装程序并在安装期间决定将哪个版本的 .dll 复制到磁盘或决定运行时加载哪个 .dll 会更简单记忆。我选择决定运行时,它对我来说很好。

  1. 准备好两个安装项目区域后,您可以使用 TFS Build 进行集成。您可以更进一步,在构建过程中添加像 Smart Assembly 之类的混淆器,以便最终拥有两个或多个单独的安装程序,在包含混淆程序集的不同依赖库上分别构建 dll。

上述所有内容都直接来自战壕,并且有效。如果我没有清楚地列出所有步骤,请给我发送消息以澄清它们并进一步详细说明。

我放弃了构建配置,因为其中的 IF 和条件过多,当“又一个 Autodesk dll 出现”时会随着时间累积。我对此不太确定。也许这是一个解决方案,但您必须非常确定自己在做什么,以免在需要时阻止 TFS 构建。

【讨论】:

    【解决方案3】:

    您的方法 - 使用 multiple, separate resource-only projects(和程序集) - 是合理的,并且在本地化应用程序和其他场景中是典型的。您需要注意一些命名空间和范围问题 - VS2005 总是将资源类型生成为内部(或 VB 中的朋友),而 VS2008 允许您改变它。您必须做正确的事情才能从您的主程序集中访问多个附属程序集——公开确定资源类型的范围。

    一旦有了主 DLL 和各种资源 DLL,就可以选择部署。一种是分别部署不同的 DLL,并在运行时加载正确的 DLL。假设您的主 EXE 名为 app.exe;然后,您还将拥有所有附属程序集的 Sat1.dll、sat2.dll、sat3.dll 等。您的安装项目将只包含任何合适的 DLL。

    另一个选项是使用 ILMerge 合并 DLL。在这种情况下,您将 app.exe 与 sat1.dll 合并,获得 app1.exe。同样 app.exe+sat2.exe => app2.exe。

    要在这方面进行谷歌搜索,请使用“本地化”和“卫星程序集”。

    【讨论】:

      【解决方案4】:

      您正在寻找多种配置吗?看一看:Build Configurations

      【讨论】:

      • 我看不到根据构建配置改变项目中使用的资源的方法 - 或者我错过了什么?
      • 我认为你错过了一些东西。在 VS 2008 中,单击 并创建一个新配置“Test”并复制您的 Release of Debug 配置。然后点击 <...properties> 这将弹出一个多选项卡对话框,让您更改资源、添加构建后命令(可以是您喜欢的任何程序)、设置、文件路径等。所以假设您想要 4 种不同的配置,将它们称为 B1、B2 ..B4。它们都可以构建到不同的目标目录,从不同的目录链接等。
      • 只有“构建”选项卡似乎允许每个配置选项 - 这限制了我更改输出目录和定义条件定义。问题在于 4 组不同的资源或设置 - 似乎都不对选择的配置敏感。
      猜你喜欢
      • 2012-12-31
      • 2018-07-09
      • 2014-04-08
      • 1970-01-01
      • 1970-01-01
      • 2017-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多