I have a WPF/C# application that references .NET 4.0 assemblies. However, within the application is a text editor that needs to display C# intellisense tied to .NET 3.5 assemblies. Thus, I want to be able to load the appropriate .NET 3.5 assemblies at runtime. However, when I try to use:

Assembly.Load()
Assembly.LoadFile()
Assembly.LoadFrom()

I always get the latest version from the GAC. Reading their MSDN pages, it seems like that's unfortunately by design.

I tried following the proposed solutions in this stack overflow post. However, the linked webpages' solutions don't work and a remoting process seems like overkill. Does anyone know a better and/or easier way to load older .NET assemblies at runtime?

5 Answers
5

I've done this before, albeit from assemblies that weren't in the GAC: Mine were being loaded from byte arrays, but I could easily have different versions of the same assembly.

The solution was to handle the AssemblyResolve event of the AppDomain, so that you can ensure that the assembly you require is returned. In your case this could be fairly simple, as you only care when doing this particular invocation. The rest of the time you'd take the default and not handle the even at all.

This is pretty crude, but it'll give a idea of the process - you handle the assemblyresolve, and return what you need. The contents of your handler will likely be different as I'm not sure your assembly will be present in the CurrentDomain.GetAssemblies() list.

There are probably more subtle examples of assmeblyresolve out there that will handle GAC versions for you.

Notes: This was used in .Net 3.5, not 4, but did work for different versions. You may need @Jean's solution to load 3.5 assemblies in 4.

This will allow loading older framework assemblies; but it won't help if there is a newer version in the GAC. The .NET Framework will always prefer to load the latest one it can.
–
vcsjonesOct 31 '11 at 21:35

That startup attribute is only relevant to assemblies that contain native code, C++/CLI creates those. The version 4 CLR has no trouble loading pure MSIL only assemblies that target earlier .NET versions.
–
Hans PassantNov 1 '11 at 0:06

One partial solution I discovered is to use Assembly.ReflectionOnlyLoadFrom(). This method correctly loads older versions of assemblies, although the assembly is loaded into a reflection-only context, meaning you can't execute code in the assembly. While this isn't a true assembly load, it does enable most of the intellisense scenario I'm working on.

The one lingering problem is mscorlib. When trying to use the assembly returned from ReflectionOnlyLoadFrom, for an older version of mscorlib, an exception is thrown: "System.TypeLoadException: Could not load type 'System.Object' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' because the parent does not exist."

I think this is your best bet, but just how you are "trying to use" the assembly is not clear. If you have all of the necessary source code, you should be able to filter out types from mscorlib 4.0 to make things work when an older version is expected - I've posted an answer that shows how I've done that successfully for my own purposes. If you're handing the assembly off to pre-existing code that does reflection on it directly, and can't modify that code, then you're in trouble.
–
Ken BeckettNov 6 '11 at 4:07

@Ken: I'm in the latter case. As stated in the question, the assemblies are used to generated intellisense information. Regarding your approach, while filtering out types is an interesting technique, I also need to filter out methods. For instance, string.IsNullOrWhiteSpace() didn't exist in .NET 3.5 so it shouldn't appear in my intellisense pop-up for the string class.
–
CraigNov 10 '11 at 2:13

I solved this problem using ReflectionOnlyLoadFrom() to load assemblies of whatever version that I need in read-only mode for use with reflection only. When it comes to the fact that you can't do this for mscorlib (you can ask for 2.0, but you'll get 4.0 back from the call), I handled this by filtering the types returned based upon the version that I needed. After all, in this mode, you can only inspect the types in the DLL using reflection, and not really do anything else with them, so you can easily filter the list of types returned as needed.

Here are the types that I've filtered out of mscorlib 4.0 so far so that everything works when an older version of mscorlib is expected:

/// <summary>
/// Types to be hidden from .NET mscorlib 4.0 for older versions.
/// </summary>
protected static readonly HashSet<Type> HideMscorlib4Types = new HashSet<Type>
{
// The Action delegates with 0, 2, 3, 4 type parameters were moved from System.Core 3.5 to mscorlib for 4.0
typeof(Action), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>),
// The Func delegates with 1 to 5 type parameters were moved from System.Core 3.5 to mscorlib for 4.0
typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>), typeof(Func<,,,,>),
// Other types moved from System.Core 3.5 to mscorlib 4.0
typeof(TimeZoneInfo),
// Hide various types that were new in mscorlib 4.0
typeof(Tuple)
};