如何让Visual Studio将DLL文件复制到输出目录?

128
我有一个依赖于外部DLL文件的Visual Studio C++项目。在构建项目时,如何使Visual Studio自动将该DLL文件复制到输出目录(debug/release)中?
8个回答

100

在您的项目中使用后构建操作,并添加命令以复制有问题的DLL。后构建操作是编写为批处理脚本。

输出目录可以引用为$(OutDir)。项目目录可用作$(ProjDir)。尽可能使用相对路径,在适用的情况下,这样您就可以复制或移动项目文件夹而不会破坏后构建操作。


30
值得一提的是,他可以通过选择“项目”>“属性”>“生成事件”>“后期生成事件”来设置后期生成事件。 - Phil Booth
24
sample:http://eyeung003.blogspot.com/2009/11/visual-studio-post-build-event-to-copy.html - AntonioR
43
如果链接失效:“xcopy /y“$(ProjectDir)*.dll”“$(OutDir)” - ace
8
在xCopy命令中加入/d标志,以防止不必要的文件再次复制到输出目录中。 - Zoey
2
$(TargetDir) rather than $(OutDir) - Ouroborus
显示剩余3条评论

61

$(OutDir) 在 VS2013 中是一个相对路径,所以我不得不将其与 $(ProjectDir) 组合起来实现期望的效果:

xcopy /y /d  "$(ProjectDir)External\*.dll" "$(ProjectDir)$(OutDir)"

顺便提一句,你可以在脚本开头加上“echo”,然后观察构建输出窗口中的扩展文本来轻松调试脚本。


6
$(TargetDir) 可以替代 $(ProjectDir)$(OutDir),因为它实际上是两者的组合。 - person27
1
在我的情况下,如果没有使用 /d 参数,它会抛出“访问被拒绝”的错误。但是根据文档,/d 参数是用于日期的。不确定它们之间的联系是什么。 - Ravi C
3
如果源文件的时间戳比已存在的目标文件要老或者相同,加上/d可以防止覆盖。如果目标文件被其他进程锁定,则可能会出现访问被拒绝的错误。 - Rich Shealer
我建议添加/f参数,例如xcopy /y /f,以获取有关源和目标目录的详细输出。文档:https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/xcopy - MakotoE

17
要使用GUI完成此操作,首先将文件添加到项目中:右键单击项目,选择“添加...”,然后选择“现有项目”,浏览到您想要添加的文件或文件,然后单击“添加”。接下来,告诉Visual Studio在构建时复制该文件:右键单击要复制的文件,选择“属性”。您将看到一个属性列表,包括“项类型”。将“项类型”更改为“复制文件”。单击“确定”即可完成。
这是文件属性对话框:

File Properties Dialog

在*.vcxproj文件中查找,上述步骤会添加类似以下内容的东西:
<ItemGroup>
    <CopyFileToFolders Include="libs\a.dll" />
    <CopyFileToFolders Include="libs\a.dll" />
</ItemGroup>

我找不到关于<CopyFileToFolders>的官方文档,但显然它是受支持的,否则GUI不会使用它。但是,如果您手动操作并且未经记录的项目类型使您感到不舒服,您始终可以使用众所周知但略微冗长的<Content>类型:

<ItemGroup>
    <Content Include="libs\a.dll" >
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="libs\b.dll" >
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
</ItemGroup>

3
这应该是被接受的答案。这是正确的方法,因为 DLLs 包含在解决方案文件列表中,所以你会知道它们是否丢失。它将允许你复制不同的 DLL,具体取决于配置和平台,而且它不需要手动编辑项目文件,也不依赖于外部命令进行复制。 - Igor Levicki
2
同意这应该被接受。此外,“项目类型”对于所有配置文件都是全局的,而不是每个配置文件都有一个。要按不同方式处理 x86 和 x64 配置文件(例如),“从构建中排除”字段是针对特定配置文件的,并且按您预期的工作。 - Kevin Anderson

10

评论区上面的细节对我没有用(在尝试将一个C++项目的输出dll复制到同一解决方案中另一个C#项目的发布和调试文件夹中时,使用VS 2013)。

我不得不添加以下后构建操作(右键单击具有.dll输出的项目,然后选择属性 → 配置属性 → 构建事件 → 后构建事件 → 命令行

现在我添加了这两行代码以将输出dll复制到这两个文件夹中:

xcopy /y $(TargetPath) $(SolutionDir)aeiscontroller\bin\Release
xcopy /y $(TargetPath) $(SolutionDir)aeiscontroller\bin\Debug

7
project.csproj 文件中添加内置的复制功能。
  <Project>
    ...
    <Target Name="AfterBuild">
      <Copy SourceFiles="$(ProjectDir)..\..\Lib\*.dll" DestinationFolder="$(OutDir)Debug\bin" SkipUnchangedFiles="false" />
      <Copy SourceFiles="$(ProjectDir)..\..\Lib\*.dll" DestinationFolder="$(OutDir)Release\bin" SkipUnchangedFiles="false" />
    </Target>
  </Project>

1
Visual Studio存在一个长期的bug,请使用ProjectDir而不是SolutionDir。 - John Jang

4
(此回答仅适用于C#,而非C++,抱歉我误读了原问题)
我以前也遇到过DLL地狱。我的最终解决方案是将非托管的DLL作为二进制资源存储在托管的DLL中,并在程序启动时将它们提取到临时文件夹中,在程序关闭时删除它们。
这应该是.NET或PInvoke基础设施的一部分,因为它非常有用...它使得你的托管DLL易于管理,无论是使用Xcopy还是作为更大的Visual Studio解决方案中的项目引用。一旦你这样做了,你就不必担心后期构建事件。
更新:
我在另一个答案中发布了代码 https://dev59.com/X3RB5IYBdhLWcg3wSVm1#11038376

3
我同意,将静态链接DLL等功能纳入框架是必要的。值得注意的是,在运行时将DLL作为资源存储并提取可能会在某些公司环境中引起问题(特别是如果它们有相当积极的反病毒软件)。 - BrainSlugs83
同意BrainSlug83的观点。我们使用的是McAfee,它是一款付费的恶意软件/垃圾软件防护软件,可以防止dll文件在执行、移动、更改临时目录等方面受到攻击。 - Charles Byrne

2
xcopy /y /d  "$(ProjectDir)External\*.dll" "$(TargetDir)"

你也可以使用相对路径,下面的例子将在项目文件夹上一级的文件夹中查找DLL。如果你有多个项目在单个解决方案中使用该DLL,这将把DLL源代码放在一个公共区域,当你将它们中的任何一个设置为启动项目时,都可以访问到该DLL。"Original Answer"翻译成"最初的回答"。
xcopy /y /d  "$(ProjectDir)..\External\*.dll" "$(TargetDir)"
<最初的回答> /y选项可以不进行确认就复制文件。 /d选项检查目标文件是否存在,如果存在,则只有当源文件的时间戳比目标文件新时才会复制。

我发现在较新的Visual Studio版本中(例如VS2019),$(ProjDir)未定义,必须改用$(ProjectDir)代替。
xcopy命令中省略目标文件夹会默认使用输出目录。这非常重要,因为单独使用$(OutDir)是不够用的。
至少在最近的Visual Studio版本中,$(OutDir)被定义为相对于输出文件夹的路径,例如bin/x86/Debug。如果将其作为目标使用,将会创建一个新的文件夹集,从项目输出文件夹开始。例如:… bin/x86/Debug/bin/x86/Debug
将其与项目文件夹结合使用,就可以正确定位到位置。例如: $(ProjectDir)$(OutDir)
但是,$(TargetDir)将一步提供输出目录。 Microsoft列出了当前和早期版本的Visual Studio的MSBuild宏列表

-1

我有一个类似的问题。在我的项目中,有几个外部 DLL。因此我创建了一个名为“lib”的新文件夹,并将所有外部 dll 复制到这个文件夹中。

  1. 添加对这些 DLL 的引用。
  2. 转到项目引用>dll 属性并更改以下属性 enter image description here

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