相同的源,具有不同资源的多个目标(Visual Studio .Net 2008)

时间:2010-03-01 19:33:30

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

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

即。所有产品基本上由图形,字符串和其他资源数据定制的相同核心功能组成,以形成每个产品。 想象一下,您正在创建一组产品,如“Excel for Bankers”,Excel for Gardeners“,”Excel for CEOs“等。每个产品都具有相同的功能,但名称,图形,帮助文件,包括在内模板等。

构建这些环境的环境是: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,你就完成了......
  12. 这将创建一个每配置资源文件,可以(可能)进一步本地化。为缺少资源的任何配置生成编译错误消息。资源文件可以使用标准方法进行本地化(创建第二个资源文件(MyResources.fr.resx)并像以前一样编辑.csproj)。

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

4 个答案:

答案 0 :(得分:11)

您可以向MSBuild文件中的元素添加条件。因此,例如,如果您具有“调试”资源和“释放”资源,则可以将它们放在两个单独的文件夹中(例如,调试和发布)。然后,在您的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文件包含相同数量的&lt; data&gt;元素,具有相同的名称属性。

答案 1 :(得分:3)

这是我的解决方案。最初的问题是Autodesk每个AutoCAD dll都会发布不同的东西,并且每3年就会破坏兼容性。但是我们的代码实际上是一样的。这对Develop-TFS-TFSbuild-DomainControllerInstallationOfApps来说是一个痛苦的问题。

解决方案如下:

  1. 制作包含所有结构的MOTHER项目,并举出AutoCAD 2012的参考资料。
  2. 然后在同一解决方案中为AutoCAD 2013创建第二个项目,并引用其自己的DLLS。
  3. 现在你必须为每个项目设置一个编译符号 - 第一个项目中为acad2012,第二个项目为acad2013。
  4. 然后在2013项目中转到添加现有项目并指向2012项目的来源,然后点击添加按钮右侧的点击并指向添加为链接
  5. 如果2012年至2013年期间存在任何图书馆差异,您可以使用#if acad2012 #endif or #if acad2013 #endif
  6. 围绕它们

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

    <强>更新

      

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

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

      heat.exe目录“$(TargetDir)” - cg MyAppComponents -dr INSTALLFOLDER -sfrag -srd -var“var.FilesPath”-out“。\ MyAppComponents.wxs”

    2. 此脚本使用您需要的所有文件构建MyAppComponents.wxs。如果您想熟悉Wix,请阅读本书:“WiX 3.6:Windows Installer XML开发人员指南”。我读了之后就做了所需的一切。

      1. 将MyAppComponents.wxs直接添加到您的主安装程序项目(让它用于2012)和任何其他项目“Add As Link”(让它用于2013 dll)。如果您按照步骤5中的说明进行操作,那么您的构建脚本已经针对不同版本的依赖项程序集进行编译。您想要针对两个不同的依赖项编译一个源代码吗?这样你就可以了 - 一个包含所有内容的.wxs,在.cs文件中你有编译引导编译。
      2. 现在,当您向解决方案中添加新文件时,您需要将其添加到您的母亲项目中,然后将“添加为链接”添加到您的其他项目,并在.wxs中仅在一个地方添加组件注册,以便将其分发在所有安装程序中。

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

        1. 在准备好两个安装项目区域后,您可以使用TFS Build进行集成。您可以更进一步,在构建过程中添加一个类似于Smart Assembly的混淆器,这样最终您就会有两个或多个单独的安装程序,这些安装程序在包含混淆程序集的不同依赖库上单独构建dll。
        2. 上面描述的所有内容都直接来自战壕并且有效。如果我没有明确列出所有步骤,请给我发一条消息来澄清它们并进一步阐述。

          我从构建配置转移了,因为它们中的IF和条件太多会随着“另一个Autodesk dll出现”而累积。我对此不太确定。也许它是一个解决方案,但你必须非常确定你正在做什么,以便在需要时不要制动TFS Build。

答案 2 :(得分:1)

使用multiple, separate resource-only projects(和程序集)的方法是合理的,对于本地化应用程序和其他方案来说是典型的。您需要注意一些命名空间和范围问题 - VS2005始终生成内部资源类型(或VB中的朋友),而VS2008允许您改变它。为了能够从主程序集访问多个附属程序集,您必须做正确的事情 - 公开调整资源类型的范围。

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

另一种选择是将DLL与ILMerge合并。在这种情况下,您将app.exe与sat1.dll合并,获取app1.exe。同样app.exe + sat2.exe =&gt; app2.exe。

要了解这一点,请使用“本地化”和“卫星装配”。

答案 3 :(得分:0)

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