为了将项目设置合并到 C++ 和 C# 项目的属性表中,构建了以下属性表:
In an attempt to consolidate project settings into property sheets for both C++ and C# projects, the following property sheet was constructed:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
Trying to support both C++ and C# projects by introducing derived
properties and setting the appropriate output properties.
-->
<PropertyGroup Label="UserMacros">
<ProjectOrAssemblyName Condition="'$(AssemblyName)'==''">$(ProjectName)</ProjectOrAssemblyName>
<ProjectOrAssemblyName Condition="'$(ProjectName)'==''">$(AssemblyName)</ProjectOrAssemblyName>
<ShortPlatform Condition="'$(Platform)'=='Win32'">x86</ShortPlatform>
<ShortPlatform Condition="'$(Platform)'=='x86'">x86</ShortPlatform>
<ShortPlatform Condition="'$(Platform)'=='x64'">x64</ShortPlatform>
<ShortPlatform Condition="'$(Platform)'=='AnyCPU'">AnyCPU</ShortPlatform>
</PropertyGroup>
<PropertyGroup>
<OutputPath>$(OutputRelativePath)/$(ProjectOrAssemblyName)_$(ShortPlatform)_$(Configuration)/</OutputPath>
<BaseIntermediateOutputPath>$(OutputRelativePath)/Obj_Exe/$(ProjectOrAssemblyName)_$(ShortPlatform)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)_$(Configuration)/</IntermediateOutputPath>
<IntDir>$(IntermediateOutputPath)</IntDir>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
</Project>
此属性表会将所有构建输出移动到一个单独的位置OutputRelativePath(在单独的属性表中或直接在项目文件中定义)包含源代码的目录之外,以便于清理等.但是,在设置后这个构建和构建工作正常,所有单元测试工作正常,很明显 WPF 可执行项目并不好,因为使用上述属性表运行应用程序会导致臭名昭著:
This property sheet will move all build output to a separate location OutputRelativePath (defined in separate property sheet or directly in project file) outside directories that contain source code for easier cleanup etc. However, after setting this up and build works fine and all unit tests work fine, it was clear that a WPF executable project was not fine, since running the application with above property sheet results in the infamous:
IOException was unhandled "Cannot locate resource 'app.xaml'."
为什么更改输出路径会导致此错误?以及如何确定原因是项目构建输出路径?这可以在生成的代码中看到吗?我找不到?这不是一个错误吗?
Why does changing the output paths result in this error? And how can it be determined that the cause is project build output paths? Can this be seen in generated code? I could not find it? And isn't this a bug?
注意:使用以下属性表有效,但前提是 IntermediateOutputPath 包含 BaseIntermediateOutputPath.
NOTE: Using the following property sheet works, but only if IntermediateOutputPath contains BaseIntermediateOutputPath.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath>$(OutputRelativePath)/$(AssemblyName)_$(Platform)_$(Configuration)</OutputPath>
<BaseIntermediateOutputPath>$(OutputRelativePath)/Obj_Exe/$(AssemblyName)_$(Platform)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)_$(Configuration)</IntermediateOutputPath>
</PropertyGroup>
</Project>
看来,以某种方式期望输出路径包含 AssemblyName 属性或类似属性.
So it appears, that somehow it is expected that output paths contain the AssemblyName properties or similar.
另一个程序集中的 XAML 样式更新:同样适用于 xaml ResourceDictionary,如果这些 - 例如Brushes.xaml - 位于另一个程序集中,并且该程序集也更改了 OutputPath,这也会引发异常:
UPDATE FOR XAML STYLES IN ANOTHER ASSEMBLY: The same applies to xaml ResourceDictionary if these - e.g. Brushes.xaml - are located in another assembly and this assembly has changed the OutputPath also, this also throws an exception:
XamlParseException was unhandled for set property Source
with InnerException "Cannot locate resource 'Brushes.xaml'"
总而言之,输出位置似乎改变了 xaml 资源名称,因此无法在运行时以某种方式发现这些资源名称.奇怪的是它在设计时不是问题......
So all in all it appears output location changes the xaml resource names so these cannot be discovered at runtime, somehow. The odd thing is it is not a problem at design time...
更新:重现异常的最少步骤:
UPDATE: Minimal steps to reproduce the exception:
打开 Visual Studio 2013
Open Visual Studio 2013
创建新的 C# 项目 WPF 应用程序,例如XamlIntermediateOutputPathBug
Create new C# project WPF Application e.g. XamlIntermediateOutputPathBug
卸载项目
编辑项目文件
在第一个 PropertyGroup 之后插入新的 PropertyGroup 为:
After first PropertyGroup insert new PropertyGroup as:
<PropertyGroup>
<OutputRelativePath>$(ProjectDir)..Build</OutputRelativePath>
<OutputPath>$(OutputRelativePath)/$(AssemblyName)_$(Platform)_$(Configuration)/</OutputPath>
<BaseIntermediateOutputPath>$(OutputRelativePath)/Obj_Exe/$(AssemblyName)_$(Platform)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)_$(Configuration)/</IntermediateOutputPath>
<IntDir>$(IntermediateOutputPath)</IntDir>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
删除剩余 PropertyGroups 中的 OutputPath
属性,例如
Delete OutputPath
properties in remaining PropertyGroups e.g.
<OutputPath>binDebug</OutputPath>
和:
<OutputPath>binRelease</OutputPath>
这应该会在 mainwindow.xaml
的启动时引发 IOException
.这是因为 $(AssemblyName).g.resources
嵌入资源被赋予了以下名称:
This should then throw an IOException
on start for mainwindow.xaml
. This is due to the $(AssemblyName).g.resources
embedded resource is given the following name:
.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 资源,这似乎是不可能的...
as can be seen with ildasm.exe
and opening the MANIFEST
for the assembly. As can also be seen the normal resources also gets a wrong name with the output path prefixed. This can, however, be fixed by setting the LogicalName
in the project file for this resource (see MissingManifestResourceException when running tests after building with MSBuild (.mresource has path in manifest)). This does not appear to be possible for xaml resources...
查看配置后,我注意到我在 OutputPath
和 IntermediateOutputPath
的末尾使用了 /
,删除它们似乎可以工作,见下文:
Having looked at the configuration I noticed I use /
at the end of the OutputPath
and IntermediateOutputPath
, removing these it appears to work, see below:
<PropertyGroup>
<OutputRelativePath>$(ProjectDir)..Build</OutputRelativePath>
<OutputPath>$(OutputRelativePath)/$(AssemblyName)_$(Platform)_$(Configuration)</OutputPath>
<BaseIntermediateOutputPath>$(OutputRelativePath)/Obj_Exe/$(AssemblyName)_$(Platform)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)_$(Configuration)</IntermediateOutputPath>
<IntDir>$(IntermediateOutputPath)/</IntDir>
<OutDir>$(OutputPath)/</OutDir>
</PropertyGroup>
我觉得这很奇怪......任何关于为什么会这样或者这是否真的是真的的见解都值得赞赏.请注意,C++ IntDir
和 OutDir
必须有一个尾随反斜杠,否则您将收到有关此的警告.
I find this rather curious... any insight into why this would be the case or if this is actually true is appreciated. Note that the C++ IntDir
and OutDir
instead must have a trailing backslash, otherwise you will get warnings about this.
将 MSBuild 输出详细程度设置为诊断"很快就揭示了问题的根源:
Setting the MSBuild output verbosity to "Diagnostic" quickly revealed the source of the problem:
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:Usershpass_000ProjectsBuildObj_ExeWpfApplication8_AnyCPU_DebugMainWindow.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 本身知道如何很好地处理路径名中的正斜杠.但这种能力在其他软件中往往是缺乏的,它在资源生成器任务中是缺乏的.这需要真正的反斜杠作为路径分隔符,正斜杠在资源名称中有效.修复:
Note the mix of forward and backward slashes in the path names. Windows itself knows how to handle forward slashes in path names well. But that capability is often lacking in other software, it is lacking in the resource generator task. Which requires a true backslash as a path separator, a forward slash is valid in a resource name. Fix:
<OutputPath>$(OutputRelativePath)$(AssemblyName)_$(Platform)_$(Configuration)</OutputPath>
<BaseIntermediateOutputPath>$(OutputRelativePath)Obj_Exe$(AssemblyName)_$(Platform)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)_$(Configuration)</IntermediateOutputPath>
换句话说,我只是将 /
替换为 .这解决了问题.
In other words, I simply replaced /
with . Which solved the problem.
这篇关于为什么修改项目输出目录会导致:IOException is unhandled "Cannot locate resource 'app.xaml'."的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!