1

I want to create multiple instances of a .xaml UserControl named view.xaml, which resides in an assembly dir1\asm.dll and dir2\asm.dll whereas asm.dll is the same assembly that only differs in its version number and the implementation of view.xaml.

I have the following setup:

public void TestCreation() {

    Assembly asm = null;

    asm = Assembly.LoadFile("dir1\asm.dll");
    CreateView(asm); // works!

    asm = Assembly.LoadFile("dir2\asm.dll");
    CreateView(asm); // works!

    asm = Assembly.LoadFile("dir1\asm.dll");
    CreateView(asm); // FAILS!

}

public void CreateView(Assembly assembly)
    {
        Type type = assembly.GetTypes().First<Type>(t => t.Name.Equals("View"));

        UserControl view = (UserControl)assembly.CreateInstance(type.FullName, false, BindingFlags.CreateInstance, null, new object[] { }, null, null);
    }

I am getting the following Exception:

enter image description here

with the Exception detail

enter image description here

I was able to track the problem up to this location in the InitializeComponent() method of my view.xaml:

enter image description here

and more specificially within InitializeComponent():

enter image description here

marc wellman
  • 5,808
  • 5
  • 32
  • 59

2 Answers2

1

Well, this was kind of fun...

Both assemblies have the same resource Uri. It would work if the Uri contained the version but VS doesn't seem to put that in there. Which ever one is loaded last (asm1 or asm2) seems to be able to use the non-versioned Uri without crashing.

If, instead of: "/ProblemEditor;component/problemeditor.xaml"

You had: "/ProblemEditor;v1.0.0.0;component/problemeditor.xaml" and "/ProblemEditor;v2.0.0.0;component/problemeditor.xaml"

Then there wouldn't be a problem.

What I did to recreate your environment was:

  1. Create a usercontrol library with a usercontrol (usercontrol1)
  2. Compile it and copy the dll (signed dll)
  3. Change the version and the user control (textblock says "version 2" instead of "version 1")
  4. Compile it and copy the dll (signed dll)

I then:

  1. Fired up Telerik's JustDecompile with the Reflexil plugin (you can get it from JustDecompile's Plugins Manager).
  2. Loaded the dlls
  3. Found the Uri's in the InitializeComponent methods
  4. Modified the Uri's to include the version that matches the dll
  5. Did a "save as" on the dlls. Since they were signed and we just modified them, Reflexil delay signed it but offered to remove the strong name or re-sign with key (you have to provide the .snk key file, of course). I just re-signed them since I have the key file.

Then your code above works! works! WORKS!

I hope this an acceptable solution for you. If anyone else has a way around this without hacking dlls, I'd be interested to know as well.

J.H.
  • 4,232
  • 1
  • 18
  • 16
  • Thank you very much for your answer and your effort! Unfortunately, decompiling the dll and then modifying it is not an option for me because it would violate my tight project constraints. I need to find a way to manipulate the resource string during the development time of the dll. I think you are right, I "just" need to integrate the assembly version into that resource string ... but how is the question ........ – marc wellman Jan 28 '16 at 06:45
  • I did open a separat thread for this follow-up question [here](http://stackoverflow.com/questions/35054812/set-resource-uri-of-xaml-component). – marc wellman Jan 28 '16 at 07:04
  • Are you saying that you can modify the source of the new version of the dll? If so, perhaps just comment out the call to InitializeComponent and then mimic what it does but using a Uri that you create with the version in it? Then, you just have to make sure to load the old asm last (last loaded doesn't seem to need the version in the Uri). – J.H. Jan 28 '16 at 11:12
0

After a week suffering and laboring with this issue, I finally found both the reason for the problem and its solution.

The problem lies within the auto-generated *.g.i.cs file, which is called by the InitializeComponent() method of a UserControl, as seen by the following:

enter image description here

This file generates a string (a Resource Locator) that expresses the path to that xaml-component, as seen by the following:

enter image description here

Now, if you have multiple versions of the same assembly and both versions include the same xaml-file, WPF does not know what xaml-file to instantiate, because the Resource Locator only references the name of the assembly but not its version.

This results in a TargetInvocationException, saying that

{"The component 'MyNamespace.MyUserControl' does not have a resource identified by the URI '/MyAssembly;comoponent/myusercontrol.xaml'"}

as follows:

enter image description here

The simple (but most definitely not obvious) solution for this is to add the version of the assembly to this Resource Locator. This can be achieved by modifying the build-file of the project by adding the <AssemblyVersion>-tag as follows:

enter image description here

Credits for this go to:

Community
  • 1
  • 1
marc wellman
  • 5,808
  • 5
  • 32
  • 59