通过MSBuild任务自动复制DLL文件

在Visual Studio中使用第三方库的时候,若第三方库没有通过NuGet分发,则通常需要自行将DLL文件复制到生成目录才能正常运行程序。通过将文件直接包含到项目中可以自动完成该任务,但会在解决方案资源管理器中看到大量DLL文件。本文介绍一种方法,通过在MSBuild文件(csproj、vcxprj等)中添加目标和任务,实现生成时自动将DLL文件复制到生成目录。

Visual Studio中无论是C#项目、VB项目还是C++项目,都是使用MSBuild来生成可执行文件的(参见 MSBuild – Visual Studio | Microsoft Docs)。MSBuild所执行的操作均在MSBuild文件(也就是.csproj、.vcxproj等项目文件)中定义。以下为一个MSBuild文件的部分内容:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <OpenCascadeDLLs Include="C:\OpenCASCADE-7.4.0-vc14-64\opencascade-7.4.0\win64\vc14\bin\*.dll" />
    <OpenCascadeDLLs Include="C:\OpenCASCADE-7.4.0-vc14-64\tbb_2017.0.100\bin\intel64\vc14\*.dll" />
  </ItemGroup>
  <Target Name="CopyOpenCascadeDlls" AfterTargets="AfterBuild">
    <Copy SourceFiles="@(OpenCascadeDLLs)" DestinationFolder="$(TargetDir)" SkipUnchangedFiles="true" />
  </Target>
  ...
</Project>

上面展示的MSBuild文件在生成时,会将C:\OpenCASCADE-7.4.0-vc14-64\opencascade-7.4.0\win64\vc14\bin目录和C:\OpenCASCADE-7.4.0-vc14-64\tbb_2017.0.100\bin\intel64\vc14目录下的所有DLL文件复制到输出文件夹。

MSBuild文件是一个XML文件,其根节点为Project节点,根节点下有TargetItemGroup等节点。 ItemGroup内的节点表示“MSBuild 项”(参见MSBuild 项 – Visual Studio | Microsoft Docs),也就是MSBuild的输入(通常表示文件)。节点的名称表示项的类型,节点的Include属性指定包含的文件。上方展示的代码将我们所需的DLL文件设置为OpenCascadeDLLs类型的输入文件。

Target节点表示生成过程中需要完成的一系列任务(参见MSBuild 目标 – Visual Studio | Microsoft Docs)。其必须有一个名字(Name属性),其中的子元素为生成期间具体需要执行的任务;AfterTargets="AfterBuild"属性表示在AfterBuild目标完成之后运行该目标。

Copy子节点指的是“复制”任务,SourceFiles="@(OpenCascadeDLLs)"表示,复制我们之前指定的每一个 OpenCascadeDLLs类型的输入文件;DestinationFolder="$(TargetDir)"表示将文件复制到“输出文件夹”;SkipUnchangedFiles="true"表示若输入文件没有被更改,那么无需再次复制相同的文件。

综上所述,我们只需要在项目文件中</Project>前的位置插入如下代码:

  <ItemGroup>
    <{A} Include="{B}" />
  </ItemGroup>
  <Target Name="{C}" AfterTargets="AfterBuild">
    <Copy SourceFiles="@({A})" DestinationFolder="$(TargetDir)" SkipUnchangedFiles="true" />
  </Target>

其中{A}是我们自己命名的输入类型名称,{B}是DLL文件路径,{C}是我们自己命名的MSBuild目标名称,就能在每次生成时自动将DLL文件复制到输出文件夹中了。

已知问题

  1. 若项目B引用了项目A,项目A使用自定义MSBuild目标复制了文件,则复制的文件不会自动出现在项目B的输出目录中。
  2. 若项目B引用了项目A,项目B使用自定义MSBuild目标复制了文件,且项目A使用自定义MSBuild目标以通配符方式复制项目B复制的文件,则第一次生成时可能会缺失部分文件。

留言

有想法?请给我们留言!您的留言不会直接显示在网站内。