wpf: For non-deterministic builds WPF application crashes on resource dictionary load
If project is set to be non-deterministic and AssemblyVersion uses wildcards, the application fails while loading resource dictionaries. It seem to try to parse Version object from wildcard string from meta (?) instead of determining concrete running assembly version, either that or there is a bug in Version parser. Note: I understand there are benefits to deterministic, but that is not the topic, non-deterministic should not be a showstopper for wpf core, especially when porting existing applications and follow-up processes.
Steps to reproduce:
- Create .net core WPF application
- Modify csproj and switch to wildcard non-deterministic build
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<!-- non-deterministic with wildcards -->
<Deterministic>False</Deterministic>
<AssemblyVersion>1.0.*</AssemblyVersion>
</PropertyGroup>
</Project>
- Add any resource file to project (e.g. test.xaml), contents is irrelevant.
- Load resource from App.xaml
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Test.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
- Build&Run app
Expected behavior A working application?
Actual behavior Application crashes with unhandled exception immediately on start. Stack trace is:
Application: WpfApp1.exe
CoreCLR Version: 4.700.19.56402
.NET Core Version: 3.1.0
Description: The process was terminated due to an unhandled exception.
Exception Info: System.FormatException: Input string was not in a correct format.
at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type)
at System.Version.TryParseComponent(ReadOnlySpan`1 component, String componentName, Boolean throwOnFailure, Int32& parsedComponent)
at System.Version.ParseVersion(ReadOnlySpan`1 input, Boolean throwOnFailure)
at System.Version.Parse(String input)
at System.Version..ctor(String version)
at System.Windows.Navigation.BaseUriHelper.GetLoadedAssembly(String assemblyName, String assemblyVersion, String assemblyKey)
at MS.Internal.AppModel.ResourceContainer.GetResourceManagerWrapper(Uri uri, String& partName, Boolean& isContentFile)
at MS.Internal.AppModel.ResourceContainer.GetPartCore(Uri uri)
at System.IO.Packaging.Package.GetPartHelper(Uri partUri)
at System.IO.Packaging.Package.GetPart(Uri partUri)
at System.Windows.Application.GetResourceOrContentPart(Uri uri)
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
at WpfApp1.App.InitializeComponent() in C:\playground\WpfApp1\WpfApp1\App.xaml:line 1
at WpfApp1.App.Main()
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 24 (12 by maintainers)
Here is a summary of what we know and what has been discussed.
Issue
MarkupCompilePass1
consumes theAssemblyVersion
build property and uses it to generate the resource URI in the code forInitializeComponent
. This code is generated any time BAML is needed. https://github.com/dotnet/wpf/blob/7e336618311d1f6e1076c800623b01505b8590d5/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs#L3001-L3003AssemblyVersion
property), this will cause a crash at runtime. In the call tree fromInitializeComponent
, WPF is callingVersion.Parse
with the wildcard string. Since this is not a valid version string, an exception is thrown.Workaround
GenerateAssemblyVersionAttribute
toFalse
in your project and then add the wildcard as anAssemblyVersionAttribute
to yourAssemblyInfo.cs
. (Example Here)AssemblyVersion
property passed to WPF is an empty string and the generated code forInitializeComponent
does not have a version in the resource URI.AssemblyVersionAttribute
) as .NET Framework.Potential Fix
GenerateInitializeComponent
and ensuring the version string used in the resource URI is empty if a wildcard is in use.AssemblyVersion
build property or theAssemblyVersionAttribute
in AssemblyInfo.cs) are not affected.@weltkante Yes, I was getting a bit stream of consciousness above so probably a mess of half-conclusions.
My current thinking (have to validate with binlogs) is:
AssemblyVersionAttribute
and not in theAssemblyVersion
property. Using the property there also breaks WPF in the exact same fashion.AssemblyVersionAttribute
to theAssemblyVersion
property and that this step occurs prior toPresentationBuildTasks
usingAssemblyVersion
.AssemblyVersion
remains empty even when overriding the default generatedAssemblyInfo.cs
. While the wildcard is filled in when the assembly is built,PresentationBuildTasks
sees an empty string. This fixes the originally reported issue, but breaks my workaround since the resource URI is no longer valid to load the resource.AssemblyVersion
property,PresentationBuildTasks
sees not the filled in version, but the original wildcard string. Meaning it was always too early in the build process when using the property.Again, this is just from experimentation right now, I have to comb through logs to see what is happening when (unless someone knows offhand).
Hopefully that condenses and clarifies the word salad I was typing last night : - )