Visual Studio:如何在不复制文件夹结构的情况下“复制到输出目录”?

188

我的项目文件夹中的 \lib 文件夹中有几个 dll 文件。在 dll 的属性页中,我已选择 "Build Action" 为 "Content" 并将 "Copy to Output Directory" 设置为 "Copy always"。

构建后,我确实得到了复制的 dll,但它们在 \bin\Release\lib 中而不是在 \bin\Release 中。

是否有一种方法可以将 dll 文件复制到 \bin\Release(而不是 \bin\Release\lib)而不编写后生成脚本或使用 nant 等工具呢?

15个回答

379

使用<ContentWithTargetPath>代替<Content>,并指定目标路径,像这样:

而不是<Content>,使用<ContentWithTargetPath>,并指定目标路径,例如:

<ItemGroup>
  <ContentWithTargetPath Include="lib\some_file.dat">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <TargetPath>some_file.dat</TargetPath>
  </ContentWithTargetPath>
  <None Include="lib\some_file.dat" />
</ItemGroup>

请注意,这个条目在Visual Studio(2012, 2015, 2017)中可能不可见,但一旦手动添加到csproj中,就会出现在Visual Studio中。然而,目标路径将无法通过UI进行编辑。

为文件添加<None>条目可以确保它仍然显示在Visual Studio的UI中。


4
在VS 2015中,我没有看到ContentWithTargetPath作为构建操作选项。是否有办法添加它? - Kim
1
一旦我在.csproj文件中手动添加了条目,它就会在IDE中出现为一个选项。然而,我仍然无法从IDE中编辑目标路径。 - Kim
13
我唯一的担忧是随着MSBuild/.NET/Visual Studio/其他软件版本的更新,这可能会变得不再被支持,因为VS2015的用户界面没有显示此选项或TargetPath属性。 - MarioDS
4
иҜ·жіЁж„ҸпјҢдҪҝз”ЁContentWithTargetPathдјҡз ҙеқҸеўһйҮҸзј–иҜ‘пјҲеңЁVS 2017 15.9.9дёҠжөӢиҜ•иҝҮпјүгҖӮ - Mads Ravn
1
ContentWithTargetPath也可以通过以下方式处理多个文件: <ItemGroup> <ContentWithTargetPath Include="lib\**"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <TargetPath>%(Filename)%(Extension)</TargetPath> </ContentWithTargetPath> </ItemGroup> - Robin Güldenpfennig
显示剩余17条评论

31
将它们放在$(ProjectDir)\Lib中,但是将这些文件"作为链接"添加到.csproj根目录。现在它们将被复制到bin\Debug(或任何其他输出文件夹)中,而不在lib中。
注:此答案是在我使用的VS/MSBuild版本中ContentWithTargetPath还不可用时编写的。将此答案保留给那些可能必须使用旧版VS的人。请不要再评论了,我们现在都知道有更好的方法了。

4
谢谢ananthonline。我尝试了你提供的步骤,但没有帮助。也许我做错了什么。以下是我正在进行的操作,请检查是否有错误:
  1. 从项目中排除这些dll文件,但让它们保留在lib文件夹中。
  2. 右键单击项目并选择“添加现有项”。从lib文件夹中选择这些dll文件,并作为链接添加它们。
  3. 再次右键单击这些dll文件,在“复制到输出目录”中选择“始终复制”。
  4. 清理和重新生成。
结果:我在\bin\release\lib文件夹中再次得到了这些dll文件。
- Andy
1
请在配置完成后发布您的解决方案文件夹截图。 - Ani
3
如果我尝试为已存在于项目树中的文件添加链接,它会拒绝并将该文件再次包含到项目中... - Nyerguds
2
和 @Nyerguds 一样,我也发现无法添加一个已经在项目树中的文件链接,所以这个答案并没有解决问题。 - Tore Østergaard
1
它不会在解决方案资源管理器中淹没项目目录的根目录吗?有许多这样的文件可能会成为一个问题。通常,项目目录的根目录已经包含了太多不同的文件。 - Alex34758
显示剩余2条评论

26
如果您的主要目的是在不混乱项目根目录的情况下包含DLL,另一种解决方案是将DLL移动到单独的共享项目中,并将其作为参考添加到原始项目中。
(请注意,此帖子并没有直接回答这个问题,因为它没有保留文件夹和项目结构,但我发现这种方法很有用,因为我能够在我的情况下重新组织我的项目,并且因为我想避免其他方法的某些缺点。)
步骤:
右键单击您的“解决方案->添加->新项目->共享项目”
将DLL添加到此项目中(位于该项目的根目录中,而不是“lib”子文件夹中)
(检查DLL文件属性是否设置正确,例如“生成操作:内容”和“复制到输出目录:始终复制”)
右键单击原始项目的“引用->添加引用->共享项目”
选择之前创建的共享项目
设置如下:

solution-explorer-screenshot


5
保持项目整洁的最简单优雅的解决方案。 - Ravi Ganesh
我无法让它与UAP和*.bin文件一起工作。 - Matteo

15

如果您需要将文件从Libs目录复制到根文件夹VS2017:

<ItemGroup Condition="'$(Platform)' == 'x64'">
    <None Include="Libs\x64\**" Link="\%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup Condition="'$(Platform)' == 'x86'">
    <None Include="Libs\x86\**" Link="\%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

移动到任何其他文件夹,包括Libs(RecursiveDir)文件夹

<ItemGroup Condition="'$(Platform)' == 'x86'">
    <None Include="Libs\x86\**" Link="mycustomfolder\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

7

将dll文件添加为项目的引用,并在引用中将“复制本地”设置为true。


1
谢谢Erik。那个完美地运作了,除了一个我无法添加为引用的dll。在添加引用时,我遇到的错误是“无法添加对'libeay32.dll'的引用。请确保该文件可访问,并且它是有效的程序集或COM组件。” - Andy
7
只有.NET程序集或COM互操作程序集可以作为项目引用添加;本地DLL则无法添加。你需要找到其他方法将这个DLL复制到\bin\Release目录下。 - Michael Liu
感谢Erik、Michael和ananthonline。抱歉,由于我没有足够的声望点数,无法为您的答案和评论点赞。 - Andy
1
对于非托管 DLL,您需要使用我下面建议的方法。 - Ani

6

在这里加入我的意见,如果您想包含整个目录的内容,并且不想在 Visual Studio 中跟踪每个单独的文件,那么您可以在项目文件中添加以下内容(对于我来说,这是 UWP C++ 项目的 .vcxproj 文件):

<ItemGroup>
    <Content Include="Content\**">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
</ItemGroup>

请注意,Content目录必须与项目文件在同一个目录下,才能保留目录结构。

3

在VisualStudio 2015中,如果您正在“使用链接添加”的dll文件位于同一项目的子文件夹中,它们将自动被放置在一个文件夹中,并且输出也会像您看到的那样放置在一个文件夹中。

如果这些dll文件不是在项目的子文件夹中,而是在磁盘上的另一个项目或目录中,您可以使用“链接添加”,它们将被正确地放置在根目录中。


在VS2012中也是一样的。它拒绝将它们作为链接,只是将它们添加为内容。最后,可悲的是,最简单的解决方案似乎是将它们丢在项目根目录中。 - Nyerguds

2
我使用这个工具与VS2022一起使用:
<ItemGroup>
    <ContentWithTargetPath Include="LibFolder\**">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        <TargetPath>%(Filename)%(Extension)</TargetPath>
    </ContentWithTargetPath>
</ItemGroup>

2
上述解决方案在我的Visual Studio 2019 Professional v16.8.2中并不可靠。有时这些文件会复制,有时则不会。经过多次尝试,感觉最新的VS更新中可能出现了一些问题。
这个答案展示了如何使用后置构建脚本...但正如提问者所说,他不想这么做!因此,此答案仅适用于那些(像我一样)无法使传统方法运行的人。
  • 右键单击项目,选择添加 > 现有项...
  • 导航到 lib 文件夹并选择要添加的项目
  • 添加 右侧,单击向下箭头,并选择 添加为链接
  • 右键单击您项目中新建的 "lib" 文件夹中的每个文件,并将 "复制到输出目录" 设置为 "不复制"
  • 打开项目属性 构建事件 并添加以下后置构建事件。

,

rem Copy 3rd party DLL(s) to the output directory on successful build
COPY $(ProjectDir)lib\Something.dll $(TargetDir)
COPY $(ProjectDir)lib\SomethingElse.dll $(TargetDir)

请注意,您可以在后期构建事件中使用通配符来复制多个文件。

1
这是我在工作项目中看到的最常见的方法。 此外,您可以使用带有/d /y标志的xcopy,这样它只会复制文件,如果它们是较新的。 对于构建事件,xcopy还有一些其他选项可能会很有用。 - mickeymicks
使用copy或xcopy是一种过时的方法。最近我们正在清理所有项目,不再使用后期构建事件块中的复制命令。它们往往会破坏增量构建。可以通过内置方法来实现,就像上面提到的那个方法一样。 - videoguy
@videoguy:不是神秘的,而是简单并且运行良好。如果您有更复杂的要求,请使用其他工具。 - AlainD

2

扩展已接受的答案,由Daniel Zeitlin提供:

如果您想将不仅一个单独文件复制到输出目录,而是将所有文件夹中的所有文件复制到输出目录中,您可以使用以下代码片段添加到您的.csproj文件中:

<None Update="my-directory/*">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <TargetPath>%(Filename)%(Extension)</TargetPath>
</None>

这将在编译时直接从$(ProjectDir)/my-directory复制所有文件到$(OutputDir)(而不是$(OutputDir)/my-directory),前提是相应的文件比$(OutputDir)中的文件更新。


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