Visual Studio不从间接引用的项目复制内容文件

我有以下项目结构:

Library1 <--[project reference]-- Library2 <--[ref]-- Executable -------- -------- ---------- ContentFile .cs files .cs files .cs files 

所有项目引用都具有CopyLocal = true。

构建项目时, ContentFile会被复制到Library2的输出目录,但不会复制到Executable的输出目录,这意味着应用程序运行时可执行文件缺少ContentFile

为什么内容文件被复制到Library2的输出目录,而不是Executable的? 有没有办法将它复制到后者(我想在没有构建事件的情况下这样做,因为我相信人们会忘记这一点)?

我正在寻找一个合理且可维护的解决方案; 在添加新项目新的间接引用内容文件时需要最少的工作,以便尽可能不要忘记这样做。

使用Post-Build事件(如xcopy ../Library/bin/Debug/SomeDll.dll bin/Debug ); 并手动将项目的输出目录设置为$(SolutionDir)/bin/...而不是默认(基于每个项目),两者都很快变得一团糟。 将内容文件添加为“主项目”中的链接也太乏味了。 如果C#具有与Visual C ++相同的输出文件默认设置(即$SolutionDir/$Platform/$Configuration ),那就好了,但事实并非如此。

我也考虑过根本不使用标准的MSBuild程序并编写一个自定义构建目标(比如在Atmel Studio中使用gcc),但我没有达到目标。 此外,我希望Visual Studio的标准“Build”和“Debug”命令能够像往常一样工作。


更新:

以下是我正在做的更多细节。

我有一个Executable项目的解决方案。 此外,还有一些项目可以调用PluginExecutable通过托管项目引用引用所有这些Plugin

由于插件项目是针对可执行文件定制的,但可能具有可重用的组件,因此主要function通常在External项目中实现,而Plugin项目仅仅是包装器(并非总是如此)。

所述External项目有时使用第三方提供的本机DLL。 然后将这些DLL作为内容文件添加到External项目中,并将Copy to output dir设置为Copy always 。 所以结构如上图所示:

 External <---------------[is not aware of]--------------------+ -------- | .cs files <--[reference]-- PluginWrapper <--[reference]-- Executable "Native DLL" ------------- ---------- .cs files .cs files 

奇怪的是,“Native DLL”被复制到External的输出目录(显然), 以及 PluginWrapper但不是 Executable

开发人员的工作流程就是编写一个External ,它作为一个完全隔离的实体(它们经常被重用),用PluginWrapper包装,然后只将PluginWrapper的项目引用添加到Executable 。 我觉得奇怪的是,这显然是一件不寻常的事情。

我认为编辑Executable的MSBuild目标XML(也包括间接引用的内容文件)可能已经解决了这个问题。

我可能想要将DLL作为嵌入式资源添加到项目中,但是嵌入这样的本机DLL对我来说似乎很奇怪。 最后,通过从上图中剥离“[不知道]”来改变开发人员的工作流程,并像Brenda Bell所建议的那样,向External添加项目引用可能是最明智的解决方案,即使不理想。

更新2

请注意,嵌入式资源可能并非在所有情况下都有效。 如果有必要在可执行文件的目录中放置一个依赖项(不是cwd或任何东西),那么由于缺少安装文件夹中的管理员权限(从嵌入式资源解压缩文件),这可能无法工作。 听起来很奇怪,但这是我们使用的第三方库之一的一个严重问题。

将Library1引用添加到Executable项目。

编辑:

您可以将所有内容放在单独的项目中,将其所有条目设置为“内容”和“始终复制”,并从外部和可执行文件引用该项目

IMO你正在寻找嵌入式资源,而不是内容文件。

编译库1时,内容文件将放在其bin文件夹中。 编译库2时,编译器会识别引用的代码并包含它(库1.dll),但内容文件无法识别,因为它们在库2中的任何位置都没有提到。将库2链接到可执行文件时也是如此。

如果您的内容文件相对较小(图标,模板等),并且如果您丢失了源代码,则无法编辑它们,那么您可以将它们作为资源嵌入并提供返回内容的公共方法,例如:

  public static Stream GetResourceContent(string rName){ string resName = System.Reflection.Assembly .GetExecutingAssembly().GetManifestResourceNames() .FirstOrDefault(rn => rn.EndsWith("."+rName)); if(resName!=null) return System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resName); else return null; } 

如果您的内容必须更改,例如模板等,请考虑在可执行项目中包含副本

另一种选择是将ContentFile作为资源嵌入Library1程序集中,并使用Assembly.GetManifestResource()提取它。

有关详细信息,请参阅以下链接

http://www.attilan.com/2006/08/accessing-embedded-resources-using.html

http://blogs.msdn.com/b/alexdan/archive/2007/12/19/loading-embedded-resources-in-c-using-getmanifestresourcestream.aspx

Brenda的答案的一个变体是将Library1的内容文件添加为可执行项目中的链接。 您仍然拥有项目中的条目,但您不需要管理每个文件的多个副本。