Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assembly version in Xaml/Baml resource is removed #205

Open
mzboray opened this issue Oct 14, 2017 · 8 comments
Open

Assembly version in Xaml/Baml resource is removed #205

mzboray opened this issue Oct 14, 2017 · 8 comments
Assignees

Comments

@mzboray
Copy link
Contributor

mzboray commented Oct 14, 2017

I've been using ILRepack for combining some assemblies that have baml resources, and I have a need to be able to load two different versions of the same assembly. However I'm noticing that after running ILRepack on an assembly with baml resources the assembly versions are removed from the resource. I believe this causes the .NET runtime to load an incorrect assembly version in side-by-side scenarios throwing various exceptions (invalid cast or type not found).

Here is the decompiled baml in a non-ILRepacked assembly (via ILSpy)

<UserControl x:Class="MyLib.UserControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:uc="clr-namespace:MyLib;assembly=MyLib,Version=1.0.0.0,Culture=neutral,PublicKeyToken=e103492ae46a5022">
  <Grid>
    <uc:UserControl2 x:Name="ctrl" />
  </Grid>
</UserControl>

Here is the decompiled baml in an ILRepacked assembly:

<UserControl x:Class="MyLib.UserControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:uc="clr-namespace:MyLib;assembly=MyLib">
  <Grid>
    <uc:UserControl2 x:Name="ctrl" />
  </Grid>
</UserControl>

Note how the xml namespace uc has been modified. These types are all in the main assembly.

@mzboray
Copy link
Contributor Author

mzboray commented Oct 14, 2017

After examining this issue for a while, I have an idea on how to resolve this. From what I can tell, the modification is made in BamlResourcePatcher.ProcessRecord(AssemblyInfoRecord record):

        private void ProcessRecord(AssemblyInfoRecord record)
        {
            var assemblyName = new System.Reflection.AssemblyName(record.AssemblyFullName);

            var isMergedAssembly = _otherAssemblies.FirstOrDefault(
                asm => asm.Name.Name == assemblyName.Name || asm.Name.FullName == record.AssemblyFullName) != null;

            // we are interested in the main assembly in order to fix the signing information, when
            // we sign the repacked assembly
            var isMainAssembly = assemblyName.Name == _mainAssembly.Name.Name;

            if (isMergedAssembly || isMainAssembly)
            {
                record.AssemblyFullName = _mainAssembly.Name.Name;
            }
        }

Specifically, I think I can change record.AssemblyFullName = _mainAssembly.Name.Name to record.AssemblyFullName = _mainAssembly.FullName. I am hesitant to make this change without understanding why it is the way that it is right now. Was there a specific reason to remove the assembly version here?

I tried making this change and it does seems to resolve my issue in my test application. I haven't tried it on my full application yet. However, I get one failing test, which I also haven't had a chance to look into yet:

Test Name: GivenSampleApplicationWithMahAppsAndSystemWindowsInteractivityWPF_MergedWPFApplicationRunsSuccessfully
Test FullName: ILRepack.IntegrationTests.Scenarios.GivenSampleApplicationWithMahAppsAndSystemWindowsInteractivityWPF_MergedWPFApplicationRunsSuccessfully
Test Source: C:\source\il-repack\ILRepack.IntegrationTests\Scenarios.cs : line 41
Test Outcome: Failed
Test Duration: 0:00:00.149

Result StackTrace:
at ILRepack.IntegrationTests.Scenarios.RunScenario(String scenarioName) in C:\source\il-repack\ILRepack.IntegrationTests\Scenarios.cs:line 69
at ILRepack.IntegrationTests.Scenarios.GivenSampleApplicationWithMahAppsAndSystemWindowsInteractivityWPF_MergedWPFApplicationRunsSuccessfully() in C:\source\il-repack\ILRepack.IntegrationTests\Scenarios.cs:line 41
Result Message:
Process exited with error
Expected: 0
But was: 1
Result StandardOutput:
Scenario 'WPFSampleApplication' STDOUT: System.Windows.Markup.XamlParseException: Set property 'System.Windows.ResourceDictionary.DeferrableContent' threw an exception. ---> System.IO.FileLoadException: Could not load file or assembly 'WPFSampleApplication, PublicKeyToken=null' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.Assembly.Load(AssemblyName assemblyRef)
at System.Windows.Baml2006.Baml2006SchemaContext.ResolveAssembly(BamlAssembly bamlAssembly)
at System.Windows.Baml2006.Baml2006SchemaContext.ResolveBamlTypeToType(BamlType bamlType)
at System.Windows.Baml2006.Baml2006SchemaContext.ResolveBamlType(BamlType bamlType, Int16 typeId)
at System.Windows.Baml2006.Baml2006SchemaContext.GetXamlType(Int16 typeId)
at System.Windows.Baml2006.Baml2006Reader.Process_OptimizedStaticResource()
at System.Windows.Baml2006.Baml2006Reader.ReadKeys()
at System.Windows.ResourceDictionary.SetDeferrableContent(DeferrableContent deferrableContent)
at System.Windows.Baml2006.WpfSharedBamlSchemaContext.<>c.<Create_BamlProperty_ResourceDictionary_DeferrableContent>b__297_0(Object target, Object value)
at MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst, XamlMember property, Object value)
--- End of inner exception stack trace ---
at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
at WPFSampleApplication.Program.Main() in C:\source\il-repack\ILRepack.IntegrationTests\Scenarios\WPFSampleApplication\Program.cs:line 13

I understand this only affects a narrow set of cases like side-by-side loading that I am trying to do, but if it's useful I can create a pull request for the change.

@timotei
Copy link
Collaborator

timotei commented Oct 19, 2017

@mzboray How are you loading multiple versions of the same assembly? AFAIK this scenario is not supported in .NET (i.e., it will use latest version)

But regarding the fix itself, I think I used the simple name without a specific reason. If the assembly is not signed, you won't have the assembly version in the namespace. I just tried making ClassLibrary signed (the one from the integration tests suite in the project), but it still won't generate a reference with version, which is weird.

I think we can use the full version, but since it will be merged, in the end we'll need to represent the own binary in which we merge it.

@mzboray
Copy link
Contributor Author

mzboray commented Oct 22, 2017

@timotei Loading multiple versions of the same assembly is definitely supported in .NET in general. It requires that you strong name all assemblies which we are doing. In WPF/XAML, in particular, you need to include assembly version information the generated code which is not done normally, but can be forced using the AssemblyVersion property in the csproj file described here.

Ok it's good to know that there was not a specific known reason to only use the simple name. After some experimentation, I found that said test does not fail if I use just the assembly name and version. I'm not sure why the full name causes a failure.

My initial concern was that this seemed like a bug only because resources that do not need to be modified are in fact modified. In my case, I am only merging in some helper classes from another dll. No Baml resources should need modification.

@timotei
Copy link
Collaborator

timotei commented Oct 30, 2017

@mzboray I tried reproducing this on the scenarios integration test: #209

However, it doesn't seem to work as expected, and I have done this:

  • sign ClassLibrary
  • sign LibraryClassUsageInXaml
  • use the classlibrary xaml
  • I get this in the MainWindow.xaml in the app that uses the signed lib. Both libs have that setting set, but to no avail
<Window x:Class="LibraryClassUsageInXAML.MainWindow" Title="MainWindow" Height="350" Width="525" Loaded="MainWindowLoaded" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:dc="clr-namespace:ClassLibrary;assembly=ClassLibrary">
  <Window.Resources>
    <dc:DummyConverter x:Key="DummyConverter" />
  </Window.Resources>
  <TextBlock Text="{Binding Converter={StaticResource DummyConverter}}" />
</Window>

@mzboray
Copy link
Contributor Author

mzboray commented Oct 31, 2017

@timotei I believe this case is slightly different. In my case I have 3 assemblies:

  1. Host application. (I don't control this, and if you can inspect the raw xaml you don't actually need this to demonstrate the issue.)
  2. A library that has some xaml. All the xaml actually is within this assembly itself. We have set the AssemblyVersion property in the csproj file (see here). This is strong name signed.
  3. A class library that contains no xaml. It is signed but will be merged completely into assembly 2.

My issue comes from the fact that after merging assembly 2 and 3. The version number information in assembly 2's xaml resources referring to itself are no longer there. This assembly loads fine by itself. However if it is loaded side-by-side with a later version of itself there are various exceptions like InvalidCastException.

If you still can't repro, perhaps tomorrow I can create a repo with a demostration project I used for basic testing before deploying it on our application.

@timotei
Copy link
Collaborator

timotei commented Oct 31, 2017

@mzboray I'd like to get a repro project, thanks!

@mzboray
Copy link
Contributor Author

mzboray commented Nov 1, 2017

@timotei See my repo with the demo code: https://github.com/mzboray/DemoWpfSideBySide

@rollsch
Copy link

rollsch commented Jun 4, 2019

Is this now resolved or still a problem? I am having issues with missing resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants