为什么修改项目输出目录会导致:IOException未处理“无法找到资源’app.xaml’。”

为了将项目设置合并到C ++和C#项目的属性表中,构造了以下属性表:

    $(ProjectName) $(AssemblyName) x86 x86 x64 AnyCPU   $(OutputRelativePath)/$(ProjectOrAssemblyName)_$(ShortPlatform)_$(Configuration)/ $(OutputRelativePath)/Obj_Exe/$(ProjectOrAssemblyName)_$(ShortPlatform) $(BaseIntermediateOutputPath)_$(Configuration)/ $(IntermediateOutputPath) $(OutputPath)   

此属性表将所有构建输出移动到单独的位置OutputRelativePath (在单独的属性表中定义或直接在项目文件中定义)外部目录,其中包含源代码以便于清理等。但是,在设置完成后,构建工作正常并且所有unit testing工作正常,很明显WPF可执行项目不是很好,因为使用上面的属性表运行应用程序导致臭名昭着:

 IOException was unhandled "Cannot locate resource 'app.xaml'." 

为什么更改输出路径会导致此错误? 如何确定原因是项目构建输出路径? 这可以在生成的代码中看到吗? 我找不到? 这不是一个错误吗?

注意:使用以下属性表有效,但仅当IntermediateOutputPath包含BaseIntermediateOutputPath时才有效

    $(OutputRelativePath)/$(AssemblyName)_$(Platform)_$(Configuration) $(OutputRelativePath)/Obj_Exe/$(AssemblyName)_$(Platform) $(BaseIntermediateOutputPath)_$(Configuration)   

所以看来,不知何故,输出路径包含AssemblyName属性或类似属性。

在另一个组件中更新XAML样式:如果这些 – 例如Brushes.xaml – 位于另一个程序集中并且此程序集也更改了OutputPath,则同样适用于xaml ResourceDictionary,这也会引发exception:

 XamlParseException was unhandled for set property Source with InnerException "Cannot locate resource 'Brushes.xaml'" 

总而言之,输出位置会改变xaml资源名称,因此无法以某种方式在运行时发现这些名称。 奇怪的是它在设计时不是问题……


更新:重现exception的最小步骤:

打开Visual Studio 2013

创建新的C#项目WPF应用程序,例如XamlIntermediateOutputPathBug

卸载项目

编辑项目文件

在第一个PropertyGroup插入新的PropertyGroup后:

  $(ProjectDir)..\Build $(OutputRelativePath)/$(AssemblyName)_$(Platform)_$(Configuration)/ $(OutputRelativePath)/Obj_Exe/$(AssemblyName)_$(Platform) $(BaseIntermediateOutputPath)_$(Configuration)/ $(IntermediateOutputPath) $(OutputPath)  

删除其余PropertyGroup中的OutputPath属性,例如

 bin\Debug\ 

和:

 bin\Release\ 

然后,这应该在mainwindow.xaml start上抛出IOException 。 这是由于$(AssemblyName).g.resources嵌入式资源的名称如下:

 .mresource public 'Build/Obj_Exe/XamlIntermediateOutputPathBug_AnyCPU_Debug/XamlIntermediateOutputPathBug.g.resources' as Build_Obj_Exe_XamlIntermediateOutputPathBug_AnyCPU_Debug_XamlIntermediateOutputPathBug.g.resources { // Offset: 0x00000000 Length: 0x000003BC } .mresource public 'Build/Obj_Exe/XamlIntermediateOutputPathBug_AnyCPU_Debug/XamlIntermediateOutputPathBug.Properties.Resources.resources' as Build_Obj_Exe_XamlIntermediateOutputPathBug_AnyCPU_Debug_XamlIntermediateOutputPathBug.Properties.Resources.resources { // Offset: 0x000003C0 Length: 0x000000B4 } 

可以看到ildasm.exe并打开程序集的MANIFEST 。 还可以看出,正常资源的输出路径前缀也是错误的名称。 但是,可以通过在此资源的项目文件中设置LogicalName来修复此问题(请参阅使用MSBuild构建后运行测试时的MissingManifestResourceException(.mresource在清单中有路径) )。 这似乎不适用于xaml资源……

看了我注意到我在OutputPathIntermediateOutputPath末尾使用/的配置,删除它们似乎工作,见下文:

  $(ProjectDir)..\Build $(OutputRelativePath)/$(AssemblyName)_$(Platform)_$(Configuration) $(OutputRelativePath)/Obj_Exe/$(AssemblyName)_$(Platform) $(BaseIntermediateOutputPath)_$(Configuration) $(IntermediateOutputPath)/ $(OutputPath)/  

我发现这很奇怪……任何洞察为什么会是这种情况或者如果这是真的,我们将不胜感激。 请注意,C ++ IntDirOutDir必须具有尾部反斜杠,否则您将收到有关此内容的警告。


将MSBuild输出详细程度设置为“Diagnostic”可以快速显示问题的根源:

 1> (TaskId:21) 1> Microsoft (R) Build Task 'ResourcesGenerator' Version '4.0.30319.33440 built by: FX45W81RTMREL'. (TaskId:21) 1> Copyright (C) Microsoft Corporation 2005. All rights reserved. (TaskId:21) 1> 1> (TaskId:21) 1> Generating .resources file: '..\Build/Obj_Exe/WpfApplication8_AnyCPU_Debug/WpfApplication8.g.resources'... (TaskId:21) 1> Reading Resource file: 'C:\Users\hpass_000\Projects\Build\Obj_Exe\WpfApplication8_AnyCPU_Debug\MainWindow.baml'... (TaskId:21) 1> Resource ID is 'mainwindow.baml'. (TaskId:21) 1> Generated .resources file: '..\Build/Obj_Exe/WpfApplication8_AnyCPU_Debug/WpfApplication8.g.resources'. 

请注意路径名称中正向和反向斜杠的混合。 Windows本身知道如何很好地处理路径名中的正斜杠。 但是在其他软件中通常缺乏这种能力,缺乏资源生成器任务。 这需要一个真正的反斜杠作为路径分隔符,正斜杠在资源名称中有效。 固定:

  $(OutputRelativePath)\$(AssemblyName)_$(Platform)_$(Configuration)\ $(OutputRelativePath)\Obj_Exe\$(AssemblyName)_$(Platform) $(BaseIntermediateOutputPath)_$(Configuration)\ 

换句话说,我只需用\替换/ 。 这解决了这个问题。

当Xaml引用当前项目中的类型时,WinFX和Xaml目标会执行一些幕后黑客/魔法。 在此构建任务期间,将wpf.csproj复制到tempfilename.tmp_proj,修剪与程序集引用相关的几个节点,并将该文件编译到IntermediateOutputPath中。 这允许Xaml编译器引用临时程序集中的类型。