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

33

我有以下的项目结构:

Library1 <--[project reference]-- Library2 <--[ref]-- Executable
--------                          --------            ----------
ContentFile                      .cs files           .cs files
.cs files
所有项目引用的都是CopyLocal = true。 当我构建项目时,ContentFile会被复制到Library2的输出目录,但不会被复制到Executable的输出目录,这意味着当应用程序运行时,可执行文件缺少ContentFile。 为什么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项目。此外,还有一堆你可以称之为插件的项目。Executable通过托管项目引用引用所有这些插件。 由于插件项目是针对可执行文件定制的,但可能具有可重用组件,因此主要功能通常在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

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

开发者的工作流程是编写一个完全隔离的实体External(它们经常被重用),用PluginWrapper包装它,然后只将PluginWrapper的项目引用添加到Executable。我觉得这样做似乎很少见。

我想也许可以通过编辑Executable的MSBuild目标XML(包括间接引用的内容文件)来解决问题。

虽然我可能会考虑像建议的那样将DLL添加到项目中作为嵌入式资源,但是嵌入本机DLL看起来很奇怪。最终,像Brenda Bell建议的那样去掉上图中"[is not aware of]"并添加对External的项目引用,可能是最明智的解决方案,即使不是理想的。

更新2

请注意,嵌入资源的想法可能并非在所有情况下都适用。如果需要将依赖项放置在可执行文件的目录中(而不是cwd或其他任何地方),则可能由于安装文件夹中缺少管理员权限(以从嵌入式资源中解压文件),而无法实现。听起来很奇怪,但这是我们使用的第三方库之一的严重问题。


请问您能否更具体地说明您已经考虑或排除了哪些解决方案?如果有人建议您已经因某种原因而拒绝的事情,那将是浪费时间。 - Jens H
@JensH 抱歉,我忘记添加了。使用后构建事件;并手动设置项目的输出目录为 $(SolutionDir)/bin/... 而不是默认的(按项目基础),这两者都很快变得一团糟。在“主项目”中添加内容文件作为链接也太繁琐了。 - dialer
你尝试过将这三个项目放入同一个解决方案中,并根据需要设置它们的项目依赖关系吗? - StarPilot
@StarPilot 是的,那正是我在问题中描述的情况。出于某些我不知道的原因,它无法工作。 - dialer
1
您也可以将所有内容放在一个单独的项目中,并从需要的任何地方引用它。不过我仍然会选择嵌入式资源。 - Sten Petrov
5个回答

23

将Library1的引用添加到可执行项目中。


3
我本来更喜欢完全透明的模块的想法,因此,我认为EmbeddedResource的想法很有趣,但从长远来看,我觉得这是最明智和实用的方法。虽然如此,我仍然觉得奇怪,为什么VS没有将所有内容文件放在主项目的输出目录中。 - dialer
1
我最终使用了不同的解决方案。我的资源文件是模板(cshtml文件),我真的想将它们保留为物理文件以便于编辑和语法检查。我刚刚从另一篇帖子中发现,您可以将它们添加为项目资源文件(右键单击项目,资源,添加现有文件)。现在我可以从代码中访问和读取它们!因此,在我的情况下,我相信其他人也是如此,这是一个很好的解决方案。一旦将它们作为资源,您就可以像这样读取它们:System.Text.Encoding.UTF8.GetString(Resources.YourResourceFilename); - Tim
2
@dialer:我也觉得很难理解(为什么VS会这样表现)。stackoverflow.com/a/7306075在一定程度上帮助了我理解。 - robert4
7
我的个人看法是,这个答案更像是一种“权宜之计”而非真正的解决方案。我自己也遇到了同样的问题,但我没有更好的答案。不过这个修复方法比较丑陋。 - Wagner Leonardi
2
今天我在解决这个问题时感到非常困惑。最终,我在MSBuild的GitHub存储库中发现了这个未解决的问题,我相信这是问题的关键所在:https://github.com/Microsoft/msbuild/issues/1054 - Rafael Dowling Goodman
显示剩余2条评论

5

编辑:

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

-

我认为您正在寻找嵌入式资源,而不是内容文件。

当编译Library 1时,内容文件会被放置在其bin文件夹中。当编译Library 2时,编译器会识别到引用的代码并包含它(Library 1.dll),但是由于Library 2中没有提及这些内容文件,因此无法识别它们。连接Library 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;
}

如果您的内容可能会发生变化,例如模板等,则考虑将其与可执行项目一起包含在内。

3

1
一种与 Brenda 的答案有所不同的方法是将 Library1 的内容文件作为链接添加到可执行项目中。这样你仍然可以在项目中看到条目,但你不需要管理多个文件副本。

0

可能是您只从XAML文件中引用了Library1,因此Visual Studio无法识别使用情况。也可以通过应用名称属性在代码中添加引用,例如


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