我有以下项目结构:
Library1 <--[project reference]-- Library2 <--[ref]-- Executable
-------- -------- ----------
ContentFile .cs files .cs files
.cs files
所有项目引用都有CopyLocal = true。
当我构建项目时,ContentFile
被复制到Library2
的输出目录,但不会复制到Executable
的输出目录,这意味着可执行文件丢失{{1当应用程序运行时。
为什么要将内容文件复制到ContentFile
的输出目录,而不是Library2
的?{1}}?有没有办法将它复制到后者(我想在没有构建事件的情况下这样做,因为我相信人们会忘记这一点)?
我正在寻找合理且可维护的解决方案;在添加新项目或新的间接引用的内容文件时,只需要很少的工作,这样就不可能完全忘记这样做。
使用Post-Build事件(如Executable
);并且手动将项目的输出目录设置为xcopy ../Library/bin/Debug/SomeDll.dll bin/Debug
而不是默认(基于每个项目),两者都很快变得非常混乱。将内容文件添加为“主项目”中的链接也太繁琐了。如果C#具有与Visual C ++相同的输出文件默认设置(即$(SolutionDir)/bin/...
),那就没问题了,但事实并非如此。
我还考虑过根本不使用标准的MSBuild程序并编写自定义构建目标(比如在Atmel Studio中使用gcc),但我没有做得太远。此外,我希望Visual Studio的标准“Build”和“Debug”命令能够像往常一样工作。
以下是我正在做的更多细节。
我有一个$SolutionDir/$Platform/$Configuration
项目的解决方案。此外,还有许多项目可以调用Executable
。 Plugin
通过托管项目引用引用所有Executable
。
由于插件项目是针对可执行文件定制的,但可能具有可重用的组件,因此主要功能通常在Plugin
项目中实现,使External
项目仅仅是一个包装器(并非总是如此)
所述Plugin
项目有时使用第三方提供的本机DLL。然后将这些DLL作为内容文件添加到External
项目中,并将External
设置为Copy to output dir
。所以结构如上图所示:
Copy always
奇怪的是,“原生DLL”被复制到External <---------------[is not aware of]--------------------+
-------- |
.cs files <--[reference]-- PluginWrapper <--[reference]-- Executable
"Native DLL" ------------- ----------
.cs files .cs files
的输出目录(显然),以及 External
,但不是 PluginWrapper
。
开发人员的工作流程就是编写一个Executable
作为完全隔离的实体(他们 经常被重用),用External
包装,然后只将项目引用添加到PluginWrapper
到PluginWrapper
。我觉得奇怪的是,这显然是一件不寻常的事情。
我认为编辑Executable
的MSBuild目标XML(也包括间接引用的内容文件)可能已经解决了这个问题。
我可能想要将DLL作为嵌入式资源添加到项目中,但是嵌入这样的本机DLL对我来说似乎很奇怪。最后,正如Brenda Bell建议的那样,通过剥离上图中的'[不知道]'并将项目引用添加到Executable
来改变开发人员的工作流程可能是最明智的解决方案,即使不是理想的。
更新2
请注意,嵌入式资源可能无法在所有情况下使用。如果有必要在可执行文件的目录(不是cwd或任何东西)中放置依赖项,那么由于缺少安装文件夹中的管理员权限(从嵌入式资源解压缩文件),这可能无法工作。听起来很奇怪,但这是我们使用的第三方库之一的一个严重问题。
答案 0 :(得分:19)
将Library1引用添加到Executable项目。
答案 1 :(得分:5)
编辑:
您可以将所有内容放在单独的项目中,将其所有条目设置为“内容”和“始终复制”,并从外部和可执行文件中引用该项目
-
IMO您正在寻找嵌入式资源,而不是内容文件。
编译库1时,内容文件将放在其bin文件夹中。编译库2时,编译器会识别引用的代码并包含它(库1.dll)但内容文件无法识别,因为它们在库2中的任何位置都没有提到。将库2链接到可执行文件时也是如此。 / p>
如果您的内容文件相对较小(图标,模板等),如果您丢失了源代码,则无法编辑它们,那么您可以将它们作为资源嵌入,并提供返回内容的公共方法,例如:
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;
}
如果您的内容必然会发生变化,例如模板等,请考虑在可执行项目中加入一份副本
答案 2 :(得分:3)
另一种选择是将ContentFile作为资源嵌入Library1程序集中,并使用Assembly.GetManifestResource()提取它。
有关详细信息,请参阅以下链接:
http://www.attilan.com/2006/08/accessing-embedded-resources-using.html
答案 3 :(得分:1)
Brenda的答案的一个变体是将Library1的内容文件添加为可执行项目中的链接。您仍然拥有项目中的条目,但您不需要管理每个文件的多个副本。
答案 4 :(得分:0)
在这种情况下,Visual Studio无法识别使用情况,可能是您仅从XAML文件引用Library1。在代码中也添加参考,例如通过应用名称属性