为了将项目设置合并到 C++ 和 C# 项目的属性表中,构建了以下属性表:
<?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 可执行项目并不好,因为使用上面的属性表运行应用程序会导致臭名昭著:
IOException was unhandled "Cannot locate resource 'app.xaml'."
为什么更改输出路径会导致此错误?以及如何确定原因是项目构建输出路径?这可以在生成的代码中看到吗?我找不到?这不是一个错误吗?
注意:使用以下属性表有效,但前提是IntermediateOutputPath包含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 属性或类似属性。
更新另一个程序集中的 XAML 样式:如果这些 - 例如 Brushes.xaml - 位于另一个程序集中并且此程序集也更改了 OutputPath,这同样适用于 xaml ResourceDictionary,这也会引发异常:
XamlParseException was unhandled for set property Source
with InnerException "Cannot locate resource 'Brushes.xaml'"
因此,总而言之,输出位置似乎改变了 xaml 资源名称,因此无法在运行时以某种方式发现这些资源名称。奇怪的是它在设计时不是问题......
更新:重现异常的最小步骤:
打开 Visual Studio 2013
创建新的 C# 项目 WPF 应用程序,例如 XamlIntermediateOutputPathBug
卸载项目
编辑项目文件
在第一个 PropertyGroup 之后插入新的 PropertyGroup 为:
<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>
删除OutputPath
剩余 PropertyGroups 中的属性,例如
<OutputPath>bin\Debug\</OutputPath>
和:
<OutputPath>bin\Release\</OutputPath>
这应该会引发IOException
. mainwindow.xaml
这是由于$(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 资源,这似乎是不可能的......
查看了我注意到我在and/
末尾使用的配置,删除这些似乎可以工作,见下文:OutputPath
IntermediateOutputPath
<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
andOutDir
必须有一个尾随反斜杠,否则您将收到有关此的警告。