I have the following project structure:
我有以下项目结构:
Library1 <--[project reference]-- Library2 <--[ref]-- Executable
-------- -------- ----------
ContentFile .cs files .cs files
.cs files
All project references have CopyLocal = true.
所有项目引用都有CopyLocal = true。
When I build the project, the ContentFile
gets copied to Library2
's output directory, but not to Executable
's output directory, meaning that the executable is missing ContentFile
when the application runs.
当我构建项目时,内容文件被复制到Library2的输出目录,而不是可执行文件的输出目录,这意味着当应用程序运行时,可执行文件丢失了内容文件。
Why is the content file being copied to Library2
's output directory, but not Executable
's? Is there a way to also copy it to the latter (I'd like to do that without build events because I'm sure people will forget that)?
为什么内容文件被复制到Library2的输出目录,而不是可执行文件?是否有一种方法也可以将它复制到后者(我希望在不构建事件的情况下这样做,因为我确信人们会忘记这一点)?
I am looking for a reasonable and maintainable solution; one that requires minimal effort when adding new projects or new indirectly referenced content files, so that it is as unlikely as possible to simply forget doing it.
我正在寻找一个合理的、可维护的解决方案;在添加新项目或新间接引用的内容文件时,只需要很少的努力,因此不太可能简单地忘记这样做。
Using Post-Build events (like xcopy ../Library/bin/Debug/SomeDll.dll bin/Debug
); and manually setting the output directory of projects to $(SolutionDir)/bin/...
instead of the default (per-project basis), have both quickly become a huge mess. Adding the content files as links in the "main project" was too tedious as well. It would be fine if C# had the same default setting for the output file as Visual C++ does (that is, $SolutionDir/$Platform/$Configuration
), but it doesn't.
使用后构建事件(比如xcopy ./Library/bin/Debug/SomeDll)。dll bin /调试);并手动将项目的输出目录设置为$(SolutionDir)/bin/…而不是默认的(基于每个项目),两者都迅速变成了一个巨大的烂摊子。在“主项目”中添加内容文件作为链接也太繁琐了。如果c#为输出文件设置了与Visual c++相同的默认设置(即$SolutionDir/$Platform/$ $ $Configuration),那就没有问题了,但事实并非如此。
I've also considered not using the standard MSBuild procedure at all and write a custom build target (like using gcc in Atmel Studio), but I didn't get far at all. Furthermore, I want Visual Studio's standard "Build" and "Debug" commands to work as they usually do.
我还考虑过不使用标准的MSBuild过程,并编写一个自定义的构建目标(比如在Atmel Studio中使用gcc),但是我根本没有得到什么。此外,我希望Visual Studio的标准“构建”和“调试”命令像通常那样工作。
UPDATE:
Here is more detail on what I am doing.
以下是我正在做的事情的更多细节。
I have a solution with an Executable
project. Furthermore, there is a bunch of projects that you could call Plugin
s. Executable
references all those Plugin
s via a managed project reference.
我有一个可执行项目的解决方案。此外,还有许多项目可以称为插件。可执行文件通过托管项目引用引用引用所有这些插件。
Since the plugin projects are tailored to the executable, but may have reusable components, the main functionality is often implemented in an External
project, leaving the Plugin
project a mere wrapper (not always though).
由于插件项目是为可执行文件定制的,但是可能有可重用的组件,所以主要的功能通常是在外部项目中实现的,因此插件项目仅仅是一个包装器(并非总是如此)。
Said External
projects sometimes use native DLLs provided by third parties. Those DLLs are then added to the External
project as a content file and have Copy to output dir
set to Copy always
. So the structure looks like the diagram above:
所述外部项目有时使用第三方提供的本地dll。然后将这些dll作为内容文件添加到外部项目中,并将复制到输出目录集设置为总是复制。所以结构看起来像上面的图:
External <---------------[is not aware of]--------------------+
-------- |
.cs files <--[reference]-- PluginWrapper <--[reference]-- Executable
"Native DLL" ------------- ----------
.cs files .cs files
The weird thing is, that the "Native DLL" gets copied to External
's output directory (obviously), as well as PluginWrapper
's, but not Executable
's.
奇怪的是,“本机DLL”被复制到外部的输出目录(显然),以及PluginWrapper的,而不是可执行的。
The developer's workflow would then be to write an External
that works as a completely isolated entity (they are being reused very often), wrap it with a PluginWrapper
, and then only add a project reference to the PluginWrapper
to Executable
. I find it odd that this is apparently such an uncommon thing to do.
然后,开发人员的工作流程将是编写一个作为一个完全独立的实体(它们经常被重用)工作的外部实体,使用PluginWrapper包装它,然后只向PluginWrapper添加一个项目引用到可执行文件中。我觉得奇怪的是,这显然是如此罕见的事情。
I thought that maybe editing Executable
's MSBuild target XML (to also include indirectly referenced content files) could have solved the problem.
我认为编辑可执行文件的MSBuild目标XML(也包括间接引用的内容文件)可能已经解决了这个问题。
I might want to look into adding the DLLs to the projects as embedded resources as suggested, but embedding native DLLs like that seems weird to me. In the end, changing the developer's workflow by stripping the '[is not aware of]' from the above diagram and adding a project reference to External
, as Brenda Bell suggested, might be the most sensible solution, even if not ideal.
我可能想按照建议将dll作为嵌入式资源添加到项目中,但像这样嵌入原生dll对我来说似乎有些奇怪。最后,改变开发人员的工作流程,从上面的图中去掉“[不知道]”,并像Brenda Bell建议的那样,向外部添加项目引用,这可能是最明智的解决方案,即使不理想。
Update 2
更新2
Note that the embedded resouce idea may not work in all cases. If it is necessary to place a dependency in the executable's directory (not cwd or anything), then this may not work because of missing administrator privileges in the installation folder (to unpack the file from the embedded resource). Sounds weird, but this was a serious issue with one of the 3rd party libraries we were using.
注意,嵌入式resouce思想可能并非在所有情况下都有效。如果需要在可执行文件的目录中放置依赖项(而不是cwd或其他),那么由于安装文件夹中缺少管理员权限(从嵌入式资源中解压缩文件),这可能无法工作。听起来很奇怪,但是对于我们使用的第三方库之一来说,这是一个严重的问题。
4 个解决方案
#1
18
Add Library1 reference to the Executable project.
将Library1引用添加到可执行项目。
#2
5
EDIT:
编辑:
You can put all content in a separate project, set all its entries to "content" and "copy always" and reference that project from External and Executable
您可以将所有内容放在一个单独的项目中,将其所有条目设置为“内容”和“复制”,并从外部和可执行文件中引用该项目。
-
- - - - - -
IMO you're looking for embedded resource, not for content files.
您正在寻找的是嵌入式资源,而不是内容文件。
When you compile Library 1 the content files are placed in its bin folder. When Library 2 is compiled the compiler recognizes referenced code and includes it (Library 1.dll) but the content files aren't recognized since they aren't mentioned anywhere in Library 2. The same goes when linking Library 2 to the executable.
在编译库1时,内容文件放在其bin文件夹中。当库2被编译时,编译器识别引用的代码并包含它(库1.dll),但是内容文件不被识别,因为它们在库2中没有被提及。当将库2链接到可执行文件时也是如此。
If your content files are relatively small (icons, templates etc) and you don't envision need to edit them if you were to lose the source code then you can embed them as resources and provide a public method to return the contents, such as:
如果您的内容文件相对较小(图标、模板等),如果您丢失了源代码,您就不需要编辑它们,那么您可以将它们嵌入到资源中,并提供一个公共方法来返回内容,例如:
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;
}
If your content is bound to change, such as templates etc, then consider including a copy with the executable project
如果您的内容被绑定到更改,比如模板等,那么考虑包含一个可执行项目的副本。
#3
3
Another option would be to embed ContentFile as a resource inside the Library1 assembly and extract it using Assembly.GetManifestResource().
另一个选项是将ContentFile作为资源嵌入到Library1程序集中,并使用assembly . getmanifestresource()提取它。
See these links for more info:
更多信息请参见这些链接:
http://www.attilan.com/2006/08/accessing-embedded-resources-using.html
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
#4
0
One variation on Brenda's answer would be to add the Library1's content file(s) as Link(s) in the Executable project. You'd still have the entries in the project but you wouldn't need manage multiple copies of each file.
Brenda的回答的一个变体是在可执行项目中添加Library1的内容文件作为链接。项目中仍然有条目,但不需要管理每个文件的多个副本。
#1
18
Add Library1 reference to the Executable project.
将Library1引用添加到可执行项目。
#2
5
EDIT:
编辑:
You can put all content in a separate project, set all its entries to "content" and "copy always" and reference that project from External and Executable
您可以将所有内容放在一个单独的项目中,将其所有条目设置为“内容”和“复制”,并从外部和可执行文件中引用该项目。
-
- - - - - -
IMO you're looking for embedded resource, not for content files.
您正在寻找的是嵌入式资源,而不是内容文件。
When you compile Library 1 the content files are placed in its bin folder. When Library 2 is compiled the compiler recognizes referenced code and includes it (Library 1.dll) but the content files aren't recognized since they aren't mentioned anywhere in Library 2. The same goes when linking Library 2 to the executable.
在编译库1时,内容文件放在其bin文件夹中。当库2被编译时,编译器识别引用的代码并包含它(库1.dll),但是内容文件不被识别,因为它们在库2中没有被提及。当将库2链接到可执行文件时也是如此。
If your content files are relatively small (icons, templates etc) and you don't envision need to edit them if you were to lose the source code then you can embed them as resources and provide a public method to return the contents, such as:
如果您的内容文件相对较小(图标、模板等),如果您丢失了源代码,您就不需要编辑它们,那么您可以将它们嵌入到资源中,并提供一个公共方法来返回内容,例如:
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;
}
If your content is bound to change, such as templates etc, then consider including a copy with the executable project
如果您的内容被绑定到更改,比如模板等,那么考虑包含一个可执行项目的副本。
#3
3
Another option would be to embed ContentFile as a resource inside the Library1 assembly and extract it using Assembly.GetManifestResource().
另一个选项是将ContentFile作为资源嵌入到Library1程序集中,并使用assembly . getmanifestresource()提取它。
See these links for more info:
更多信息请参见这些链接:
http://www.attilan.com/2006/08/accessing-embedded-resources-using.html
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
#4
0
One variation on Brenda's answer would be to add the Library1's content file(s) as Link(s) in the Executable project. You'd still have the entries in the project but you wouldn't need manage multiple copies of each file.
Brenda的回答的一个变体是在可执行项目中添加Library1的内容文件作为链接。项目中仍然有条目,但不需要管理每个文件的多个副本。