Visual Studio一个项目输出多个dll文件?

30
我正在编写一个支持插件的项目。由于许多插件相对较小(只有一个源文件/类),我希望将它们全部放在Visual Studio中的一个项目中,但要成功实现此目标,需要将每个源文件/类编译成自己的dll文件,这在Visual Studio中是否可能?
如果Visual Studio无法实现此功能,是否可以使用其他构建系统,在使用Visual Studio进行编码和调试的同时实现此目标?
当前,我已经将插件项目的输出类型设置为控制台,并编写了一个main()方法,将源目录中的所有.cs文件编译为dll并将其复制到正确的目录。然后,我将该控制台应用程序设置为插件项目的后生成事件。它能够工作,但看起来像是一个非常丑陋的 hack。
使用Visual Studio 2010。
5个回答

42
你可以为每个插件创建一个项目,并将所有项目分组放在一个解决方案中。
如果你不想为每个插件创建一个项目,可以使用CSC任务使用MSBuild创建自定义构建。
如何为每个插件文件生成dll?
  1. In a project you add all plugins files

  2. Edit the project file to specify which class will generate a plugin library :

    <ItemGroup>
      <Compile Include="Class1.cs">
        <Plugin>true</Plugin>
      </Compile>
      <Compile Include="Class2.cs" />
      <Compile Include="Class3.cs">
        <Plugin>true</Plugin>
      </Compile>
      <Compile Include="Program.cs" />
      <Compile Include="Properties\AssemblyInfo.cs" />
    </ItemGroup>
    
  3. Add a new target in your project file to generate the plugins library

    <Target Name="BuildPlugins">
      <CSC Condition="%(Compile.Plugin) == 'true'"
           Sources="%(Compile.FullPath)"
           TargetType="library"
           OutputAssembly="$(OutputPath)%(Compile.FileName).dll"
           EmitDebugInformation="true" />
    </Target>
    
  4. If you want to create the plugins library after each build, add an after build target :

    <Target Name="AfterBuild" DependsOnTargets="BuildPlugins">
    </Target>
    

1
嗨,感谢您上面的答案...它解决了我一半的问题。但我想要新生成的DLL的版本号和其他程序集相关信息,并且还想将多个文件合并成一个DLL而不是单个文件。如果有人知道相关信息,请帮忙...这对我来说将是巨大的帮助。 - Denish
真棒,你从哪里获得了那么丰富的知识啊?太棒了,恰好就是我想要的。 - franko_camron
2
仅仅是多文件汇编吗?我能否将任何结果dll(没有清单)作为引用分别添加到另一个项目中? - SerG
这是很棒的逻辑,但如果我的项目有其他项目的引用,我会遇到困难。请给我提供编译那个项目的想法。 - Saroop Trivedi
1
正如其他人所提到的 - 当项目引用同一解决方案中的另一个项目时,这种方法是行不通的... - Alex

12

您只需 创建一个Solution,然后添加尽可能多的项目。

您可以拥有5个 Class Library 项目并编译它们,生成5个DLL文件。


1
是的,但是创建每个项目只有一个源文件的10个项目似乎是一种可怕的浪费,我希望有一种将所有源文件编译到一个项目中并分别生成dll的方法。 - Aleksi
10
项目和AssemblyInfo文件可能会浪费几千字节?尊重地说,这是一种错误的经济策略。这就是项目存在的原因,将您的源代码封装到程序集中。 :) - Dan J
1
所以对你来说没有途径! - Sadegh

4
为了进一步解释Julien Hoarau上面的答案,这里提供一个解决方案,可以让你在一个项目中编译多个DLL文件,并从多个CS文件中编译这些DLL文件。只需打开csproj文件,在</Project>标签之前添加以下内容:
  <!-- Plugin Building -->
  <!-- 1. Hardlink to NuGet References - CSC does not read HintPaths, so you will have to create these for all your packages -->
  <ItemGroup>
    <PluginReference Include="..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll" ><InProject>false</InProject></PluginReference>
    <PluginReference Include="..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll" ><InProject>false</InProject></PluginReference>
    <PluginReference Include="..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll" ><InProject>false</InProject></PluginReference>
    <PluginReference Include="..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll" ><InProject>false</InProject></PluginReference>
    <PluginReference Include="..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll" ><InProject>false</InProject></PluginReference>       
  </ItemGroup>
  <!-- 2. Each Plugin CS Files -->
  <!-- You see that each tag in here has it's own name starting with Plugin -->
  <!-- We can reference that later e.g. as @(PluginBlue) to get an array list to pass to the CSC sources, allowing us to have multiple files -->
  <!-- Plugin.Blue\**\*.cs gets all the files in the "Plugin.Blue" folder -->
  <!-- Plugin.Green just has a specific file list -->
  <ItemGroup>
    <PluginBlue Include="Plugin.Blue\**\*.cs"><InProject>false</InProject></PluginBlue>
    <PluginGreen Include="Plugin.Green\File1.cs"><InProject>false</InProject></PluginGreen>
    <PluginGreen Include="Plugin.Green\File2.cs"><InProject>false</InProject></PluginGreen>
  </ItemGroup>
  <!-- 3. Build each Plugin -->
  <Target Name="BuildPlugins">
    <!-- Plugin Blue -->
    <CSC Sources="@(PluginBlue)" References="@(PluginReference)" TargetType="library" OutputAssembly="$(OutputPath)Plugin.Blue.dll" EmitDebugInformation="true" />
    <!-- Plugin Green -->
    <CSC Sources="@(PluginGreen)" References="@(PluginReference)" TargetType="library" OutputAssembly="$(OutputPath)Plugin.Green.dll" EmitDebugInformation="true" />
  </Target>

  <!-- 4. Require Build on Solution Compile -->
  <Target Name="AfterBuild" DependsOnTargets="BuildPlugins">
  </Target>

这是我的方法 - 它可以让你将所有内容都组织在底部,而不是散布在整个项目文件中。使用
<InProject>false</InProject>

这使我们能够将文件从“SolutionExplorer”中隐藏,并拥有一个单独定义的文件列表,而不仅仅是添加插件标签到我们想要的文件中。在您的主解决方案中,请确保将所有编译插件中的文件的“Build Action”设置为“none”,以便主项目文件中没有重复。

关于CSC的更多阅读:

https://msdn.microsoft.com/en-us/library/78f4aasd.aspx 使用csc.exe进行命令行构建

https://msdn.microsoft.com/en-us/library/ms379563(v=vs.80).aspx 使用C# 2.0命令行编译器

https://msdn.microsoft.com/en-us/library/s5c8athz.aspx Csc任务

https://msdn.microsoft.com/en-us/library/7szfhaft.aspx MSBuild 条件

我希望这对某人有用。


注册工具告诉我密钥文件未包含,当我尝试将密钥文件添加到项目文件时,出现错误:无法打开元数据文件“mykey.snk”——PE 映像不包含托管元数据。我该如何解决这个问题? - Tony

2

一些诗:

就我而言 - 我需要为单元测试项目专门构建插件dlls。如果您按照“正常方式”进行,为每个插件创建单独的项目 - 那么您最终会拥有比核心程序集更多与单元测试相关的项目。因此,我认为在某些情况下,在同一项目中进行多次构建是值得的。话虽如此 - 我想提供一个改进版本的已接受答案。

新方法的改进:

  • 支持新的SDK项目(用于.net Core)
  • 支持构建引用解决方案内其他项目的程序集!

创建一个新的空库项目,请确保其结构遵循以下格式:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net472</TargetFramework>
  </PropertyGroup>
</Project>

提示: 例如,您可以创建 .Net Core 项目并将目标更改为 net472

如果需要,添加插件和一些引用,然后根据以下文档添加: https://learn.microsoft.com/en-us/dotnet/core/tools/csproj

<EnableDefaultCompileItems>false</EnableDefaultCompileItems>

这将使手动包含文件成为可能,否则默认情况下构建将包含所有文件。
然后显式添加要编译的项目:
<ItemGroup>
<Compile Include="TestApp1.cs">
  <Plugin>true</Plugin>
</Compile>
</ItemGroup>

接下来,如下面的来源所述 - 您可以将所有引用聚合到单个字符串中:如何在MSBuild中获取所有引用DLL的路径?

  <Target Name="GatherReferences" DependsOnTargets="ResolveReferences">
    <ItemGroup>
      <MyReferencedAssemblies Include="@(ReferencePath)" />
    </ItemGroup>
  </Target>

测试方法:

  <Target Name="TestMessage" AfterTargets="Build" >
    <Message Text="Referenced assemblies: @(ReferencePath)" Importance="high"/>
  </Target>

接下来,您需要添加一个构建目标,该目标在PostBuild时触发:

  <Target Name="BuildPlugins" AfterTargets="PostBuildEvent">
    <CSC Condition="%(Compile.Plugin) == 'true'"
         Sources="%(Compile.FullPath)"
         TargetType="library"
         References="@(ReferencePath)"
         OutputAssembly="$(OutputPath)%(Compile.FileName).dll"
         EmitDebugInformation="true" />
  </Target>

你会注意到这里有一个名为“References”的属性,它将值转换为适当的“-reference”参数,以供CSC编译器使用:https://learn.microsoft.com/en-us/visualstudio/msbuild/csc-task?view=vs-2019 额外提示:你可以将此逻辑纳入你的项目中,而无需显式定义“Plugin”属性。
根据这个主题:MsBuild Condition Evaluate Property Contains,可以在构建元数据上使用正则表达式:https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-well-known-item-metadata?view=vs-2019 因此,允许像这样的东西:
Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(Compile.Filename)', 'Plugin'))"

如果类的 文件名包含特定关键字,那么您可以触发该文件的单独编译。或者如果文件位于特定文件夹中!在上面的例子中,如果文件中有“Plugin”一词,则CSC任务将拾取该文件。建议检查元数据页面以查看所有选项。 奖励内容:如果您像我一样喜欢能够步入代码并能够输出到 Debug 输出窗口,您还可以定义:
  DefineConstants="DEBUG;TRACE"
  DebugType="full"

我的最新配置如下:

  <Target Name="BuildPlugins" AfterTargets="PostBuildEvent">
    <CSC 
      Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(Compile.FullPath)', '.*\\AppUnitTests\\Plugins\\.*.cs'))"
      Sources="%(Compile.FullPath)" 
      TargetType="library" 
      References="@(ReferencePath)"
      OutputAssembly="$(OutputPath)Plugins\%(Compile.FileName).dll" 
      EmitDebugInformation="true"
      DefineConstants="DEBUG;TRACE"
      DebugType="full" />
  </Target>

我希望这对某些人有用;)
附言:感谢原作者提供了一个我可以改进的示例。

-1
如果您想让每个代码文件都成为自己的DLL,那么您必须为每个代码文件创建一个新项目。但是,您可以将多个项目文件(.csproj)放入一个大解决方案中。
如果这不适合您,那么您可以始终使用命令行编译项目。这将允许您自定义构建以满足您的需求。例如,您可以编写批处理脚本或PowerShell脚本,将所有代码文件编译为单独的DLL。

1
请在您的答案中发布示例和/或链接,告诉人们该做什么而不是如何做,这不是本网站的宗旨。 - Hannish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接