c# wpf xaml visual-studio-2012 msbuild

c# - ¿Por qué la modificación de los directorios de salida del proyecto causa: IOException no fue controlada "No se puede localizar el recurso ''app.xaml''."



wpf visual-studio-2012 (2)

En un intento de consolidar la configuración del proyecto en hojas de propiedades para proyectos C ++ y C #, se construyó la siguiente hoja de propiedades:

<?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>

Esta hoja de propiedades moverá todos los resultados de construcción a una ubicación separada OutputRelativePath (definida en una hoja de propiedades separada o directamente en el archivo de proyecto) fuera de directorios que contengan código fuente para facilitar la limpieza, etc. Sin embargo, después de configurar esto, funciona bien y todas las pruebas unitarias funciona bien, estaba claro que un proyecto ejecutable de WPF no estaba bien, ya que ejecutar la aplicación con la hoja de propiedades anterior da como resultado el infame:

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

¿Por qué el cambio de las rutas de salida produce este error? ¿Y cómo se puede determinar que la causa son las rutas de salida de construcción del proyecto? ¿Se puede ver esto en el código generado? ¿No pude encontrarlo? ¿Y no es esto un error?

NOTA: El uso de la siguiente hoja de propiedades funciona, pero solo si IntermediateOutputPath contiene 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>

Parece que de alguna manera se espera que las rutas de salida contengan las propiedades AssemblyName o similares.

ACTUALIZACIÓN DE LOS ESTILOS XAML EN OTRO ENSAMBLE: Lo mismo se aplica a xaml ResourceDictionary si estos, por ejemplo, Brushes.xaml, están ubicados en otro ensamblaje y este ensamblado también ha cambiado el OutputPath, esto arroja también una excepción:

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

Así que, en general, parece que la ubicación de salida cambia los nombres de los recursos xaml, por lo que no se pueden descubrir en tiempo de ejecución, de alguna manera. Lo curioso es que no es un problema en el momento del diseño ...

ACTUALIZACIÓN: pasos mínimos para reproducir la excepción:

Abra Visual Studio 2013

Cree una nueva aplicación WPF del proyecto C #, p. Ej. XamlIntermediateOutputPathBug

Descargar proyecto

Editar archivo de proyecto

Después de primero PropertyGroup, inserte un nuevo PropertyGroup como:

<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>

Eliminar OutputPath propiedades de OutputPath en los restantes OutputPath propiedades, por ejemplo

<OutputPath>bin/Debug/</OutputPath>

y:

<OutputPath>bin/Release/</OutputPath>

Esto debería arrojar una IOException al inicio de mainwindow.xaml . Esto se debe a que el recurso incrustado $(AssemblyName).g.resources tiene el siguiente nombre:

.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 }

como se puede ver con ildasm.exe y abrir MANIFEST para el ensamblaje. Como también se puede ver, los recursos normales también reciben un nombre incorrecto con la ruta de salida prefijada. Sin embargo, esto puede solucionarse configurando LogicalName en el archivo de proyecto para este recurso (vea MissingManifestResourceException al ejecutar pruebas después de compilar con MSBuild (.mresource tiene ruta de acceso en manifiesto) ). Esto no parece ser posible para los recursos xaml ...

Después de observar la configuración, noté que uso / al final de OutputPath e IntermediateOutputPath , quitándolos parece que funciona, ver a continuación:

<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>

Encuentro esto bastante curioso ... cualquier apreciación de por qué sería este el caso o si esto es realmente cierto es apreciado. Tenga en cuenta que C ++ IntDir y OutDir en OutDir lugar deben tener una barra OutDir invertida, de lo contrario recibirá advertencias al respecto.


Los objetivos de WinFX y Xaml realizan hacks / magic tras bambalinas cuando el Xaml hace referencia a un tipo ubicado dentro del proyecto actual. Durante esta tarea de compilación, wpf.csproj se copia en tempfilename.tmp_proj, se recortan varios nodos relacionados con las referencias de ensamblado y el archivo se compila en IntermediateOutputPath. Esto permite que el compilador Xaml haga referencia a los tipos dentro del ensamblaje de temperatura.


Establecer la verbosidad de salida de MSBuild en "Diagnóstico" rápidamente reveló la fuente del problema:

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''.

Tenga en cuenta la combinación de barras diagonales hacia adelante y hacia atrás en los nombres de ruta. Windows mismo sabe cómo manejar bien las barras diagonales en los nombres de ruta. Pero esa capacidad a menudo falta en otro software, falta en la tarea del generador de recursos. Lo cual requiere una barra invertida verdadera como un separador de ruta, una barra diagonal es válida en un nombre de recurso. Fijar:

<OutputPath>$(OutputRelativePath)/$(AssemblyName)_$(Platform)_$(Configuration)/</OutputPath> <BaseIntermediateOutputPath>$(OutputRelativePath)/Obj_Exe/$(AssemblyName)_$(Platform)</BaseIntermediateOutputPath> <IntermediateOutputPath>$(BaseIntermediateOutputPath)_$(Configuration)/</IntermediateOutputPath>

En otras palabras, simplemente reemplacé / con / . Lo cual resolvió el problema.