How can I load an older version of a .NET assembly? - c#

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?

By default, you cannot load assemblies made for previous versions of the .NET framework on the .NET framework version 4.
However, there is a configuration attribute that allows that: http://msdn.microsoft.com/en-us/library/bbx34a2h.aspx
Put the following lines in your app.config file:
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true|false" >
</startup>
</configuration>
There are reasons why this is not active by default, but I believe that it should be appropriate for your use.

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.
A possible example might be:
public DoSomething()
{
//Add the handler
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve
//Load your assembly...
}
private System.Reflection.Assembly CurrentDomain_AssemblyResolve(Object sender, ResolveEventArgs e)
{
foreach (System.Reflection.Assembly a In AppDomain.CurrentDomain.GetAssemblies())
{
if (a.FullName == <<The one you need>>) return a
}
}
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.

You can use the AssemblyBinding property in your app.config or web.config, whichever is appropriate.
For instance, I'm interfacing with Matlab, so I need this...
<loadFromRemoteSources enabled="true" />
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MWArray" publicKeyToken="E1D84A0DA19DB86F" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.10.0.0" newVersion="2.10.0.0" />
</dependentAssembly>
</assemblyBinding>
Because the Matlab engine I work with is compiled (some crazy how) on a machine using a different copy of the runtime.
You should be able to modify this code to suit your needs.

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 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)
};

Related

"Method not found" exception. Why AppDomain.CurrentDomain.AssemblyResolve doesn't work?

There is an application (executor.exe) that invokes methods from class library (lib.dll) using reflection.
executor.exe has assembly Newtonsoft.Json version 8.0 as embedded resource.
lib.dll has reference to Newtonsoft.Json version 9.0.
lib.dll has reference to system.net.http.formatting version 4.0.0.21112, which in turn refers to Newtonsoft.Json 4.5.
I don't have the opportunity to modify executor.exe.config (except for testing).
What do I want to get:
new JsonMediaTypeFormatter().SerializerSettings;
Invoked from lib.dll. But it fails with:
Method not found: 'Newtonsoft.Json.JsonSerializerSettings
System.Net.Http.Formatting.JsonMediaTypeFormatter.get_SerializerSettings()'
What I was trying to do:
Handling AppDomain.CurrentDomain.AssemblyResolve (subscribed correctly, using ModuleInitializer). But it doesn't rise. After crash have 2 Newtonsoft.Json (with different versions) loaded to AppDomain.
Binding in app config:
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed"
culture="neutral" />
<bindingRedirect oldVersion="4.0.0.0-5.0.0.0" newVersion="9.0.0.0" />
Yes, it works. But I can't use this solution. After passing have 2 Newtonsoft.Json (with different versions) loaded to AppDomain.
I don't understand why this works (oldVersion="8.0.0.0-9.0.0.0") but:
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="8.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
exception "Method not found" doesn't throw. After passing have 1 Newtonsoft.Json (9.0) loaded to AppDomain. But not suitable for me.
Why AppDomain.CurrentDomain.AssemblyResolve doesn't work? I guess the problem is in 2 loaded assemblies but I can not change this behavior.
Why AppDomain.CurrentDomain.AssemblyResolve doesn't work?
Event AppDomain.CurrentDomain.AssemblyResolve is fired if assembly resolution fails. It's not your case since you see Newtonsoft.Json assembly already loaded in the application domain.
You catch MissingMethodException because System.Net.Http.Formatting.JsonMediaTypeFormatter.get_SerializerSettings() returns JsonSerializerSettings declared in Newtonsoft.Json version 4.5.
Unfortunatelly, for soulless CLR JsonSerializerSettings from Newtonsoft.Json 4.5 is not the same at all as JsonSerializerSettings from Newtonsoft.Json 9.0.
To fix this problem, mechanism for redirection of assembly versions was introduced (bindingRedirect that you refer to).
exception "Method not found" doesn't throw. After passing have 1
Newtonsoft.Json (9.0) loaded to AppDomain. But not suitable for me.
Actually that's the solution you should stick to. Your best option is to have only one Newtonsoft.Json assembly loaded into application domain and have version redirection configured.
Why would you reinvent the wheels and try to find solution other than platform offers? If for some strange reason you're prohibited to modify application config, you could add assembly redirection on machine level
See this article for details. But assembly version redirection is the way how eternal DLL hell problem is fixed in .Net. Using any other workarounds (even if you manage to find them) will make you no good.
If you cannot apply binding redirects you can also load correct assembly on application startup use Assembly.LoadFrom method. In main method find Newtonsoft.Json dlls and load one with the version you need. That should avoid loading of assembly with incorrect version. Let me know if you want me to provide a code snippet for you.

Cannot load a reference assembly for execution from a Web-Site project

Using .NET 4.6.2 and an older Web-Site (not Web-Application) project. If I clear the BIN directory and then build and run it works, but sometimes after multiple builds and runs, it fails with this error.
Server Error in '/' Application.
Cannot load a reference assembly for execution.
....
[BadImageFormatException: Cannot load a reference assembly for execution.]
[BadImageFormatException: Could not load file or assembly 'netfx.force.conflicts' or
one of its dependencies. Reference assemblies should not be loaded for execution.
They can only be loaded in the Reflection-only loader context.
(Exception from HRESULT: 0x80131058)]
....
Is there any trick to getting Web-Site projects to work correctly when the libraries they use are beginning to pull in netstandard 2.0?
Also, it seems that this assembly binding redirect is necessary to get it to run but nuget adds a redirect to an older version 4.1.1.0. Any suggestions on how to fix that other than manually editing this line after most nuget updates?
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
Would all these issues go away if the project was migrated to a Web-Application project?
Most likely you are encountering this error because of a mismatch between x86 and x64 compiled assemblies or similarly a mismatch between .NET Framework and .NET Core.
Here are some troubleshooting options for you that you may not have tried.
Option #1
In the AppDomain there is an event handler for when the program is trying to resolve an assembly that is reference. If you subscribe to it, you should be able to get more information about what is missing. This is how the implementation would look:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Event Handler:
private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
System.Diagnostics.Trace.TraceInformation($"Trying to resolve assebly: {args.Name} requested by {args.RequestingAssembly.FullName}");
// This event handler allows you to help the find the assembly for the CLR.
// you can dynamically load the assembly and provide it here.
return null;
}
Option #2
There is also a tools in the .NET SDK for troubleshooting binding issues.
Here is more information about the Assembly Binding Log Viewer
You need to enable it before it will emit any interesting information, but this should get you to the root of your problem, if the AssemblyResolve event doesn't help.
Yes, you can to convert web-site to web application:
If you can't to see this configuration - probably you have problem with names of classes, for example:
/Directory/Index.aspx.cs
/Directory2/Index.aspx.cs

PowerShell Binary Module assembly dependency error

I am developing PowerShell binary module. It uses Json.NET and other libraries.
I am getting this exception "Could not load file or assembly 'Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.'
On hard drive I have an updated version of it (version 7.0.2)
Problems like that are easily solved in console, web or desktop application, with app.config or "web.config" via lines like this
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
How can I do something similar for PowerShell binary module?
After coming across this issue myself while developing a PowerShell module that uses multiple 3rd party libraries (Google API, Dropbox, Graph, etc) I found the following solution was the simplest:
public static Assembly CurrentDomain_BindingRedirect(object sender, ResolveEventArgs args)
{
var name = new AssemblyName(args.Name);
switch (name.Name)
{
case "Microsoft.Graph.Core":
return typeof(Microsoft.Graph.IBaseClient).Assembly;
case "Newtonsoft.Json":
return typeof(Newtonsoft.Json.JsonSerializer).Assembly;
case "System.Net.Http.Primitives":
return Assembly.LoadFrom("System.Net.Http.Primitives.dll");
default:
return null;
}
}
Note in the method, I've got two possible ways to reference the assembly, but both of them do the same thing, they force the current version of that assembly to be used. (Regardless if it is loaded via class reference or dll file load)
To use this in any cmd-let add the following event handler in the BeginProcessing() method of the PSCmdLet.
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_BindingRedirect;
The closest i've found so far is:
Add the problem assembly to the manifest's RequiredAssemblies - this causes it to be loaded into the AppDomain when the module is loaded.
Use the code from this SO answer - it adds an AssemblyResolve handler to the current AppDomain, which searches for assemblies already loaded and returns ones which match by strong name and PublicKeyToken
After using the module, you have to do the following to avoid stack overflows when exiting: [System.AppDomain]::CurrentDomain.remove_AssemblyResolve($OnAssemblyResolve)
Steps 1 and 2 could both be encapsulated in the module, but step 3 can't, which means this isn't suitable as a general solution - the caller has to know about it. So I'm still searching for a better way.
You need to add a manifest to your module.
The simplest way is:
New-ModuleManifest -RequiredAssemblies:"path\to\newtonSoft.dll"
Then modify the manifest file manually for any other tweaks.
If the manifest doesn't solve the problem, you may need to pull out the nuclear hammer and set the binding redirects for ALL of powershell as mentioned in Powershell - Assembly binding redirect NOT found in application configuration file
For binary modules, there are a few keys that need to be populated in the manifest file for successful import/functionality:
RootModule = <'binaryModule.dll'>
RequiredAssemblies = <'binaryModule.dll'>
CmdletstoExport = '*' <--no need to restrict anything here, as
only the public functions you've developed in the assembly
will be exported to the user.
Those are the only keys you need to populate for the module to 'work' -- though I highly suggest combing through the .psd1 file generated byNew-ModuleManifest -path MyNewModule.psd1 to what other metadata values would help enrich the functionality of your module.
Also, ensure the names of the directory structure, .psd1 file, and assembly are all consistent.
SampleModule\
SampleModule\SamleModule.dll
SampleModule\SampleModule.psd1
...that should do it.

Can I have my assembly reference any version of another assembly? [duplicate]

This question already has answers here:
Is it possible to replace a reference to a strongly-named assembly with a "weak" reference?
(3 answers)
Closed 4 years ago.
I am developing a class library (MyClassLibrary).
I depend on a third party class library (ThirdPartyClassLibrary).
I need to use the same version of ThirdPartyClassLibrary as my users. e.g., if I set a static value in ThirdPartyClassLibrary the user needs to see that change.
Users of my class may be depending on any one of 4 different versions of ThirdPartyClassLibrary.
ThirdPartyClassLibrary is large, I do not want to distribute it with my software.
I have reflected on all 4 versions of ThirdPartyClassLibrary and validated that the things I will be doing with them are compatible across all versions (interfaces are the same, methods signatures are the same, etc.).
I need calls into ThirdPartyClassLibrary to be performant! I can't reflect on everything every time I need to call something.
MyClassLibrary will be loaded at runtime, so I can't expect users to mess with assembly binding redirects or other develop-time settings (or any settings at all, my users are resistant to doing anything).
I would like to benefit from compile-time checking of my code, so ideally no reflection at all.
How can I write MyClassLibrary such that when it is loaded into the process everything works correctly with whichever version of ThirdPartyClassLibrary the user has loaded?
One workaround would be to use the AppDomain.AssemblyResolve event at runtime. This fires whenever the resolution of an assembly fails. You can use this to load a different version of an assembly to that which the CLR is trying to load.
I've added a very simple demo on GitHub here:
https://github.com/danmalcolm/AssemblyResolutionDemo
This is set up as follows:
The main application App.exe directly references assembly ThirdPartyLibrary.dll version 2.0.0.0.
It also references MyLibrary, which references an older version of ThirdPartyLibrary version 1.0.0.0.
The AppDomain.AssemblyResolve event is used to redirect to the version used by the application when version 1.0.0.0 fails to load
AssemblyResolve is handled as follows:
public static void Initialise()
{
AppDomain.CurrentDomain.AssemblyResolve += ResolveThirdPartyLibrary;
}
private static Assembly ResolveThirdPartyLibrary(object sender, ResolveEventArgs args)
{
// Check that CLR is loading the version of ThirdPartyLibrary referenced by MyLibrary
if (args.Name.Equals("ThirdPartyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fbcbfac3e44fefed"))
{
try
{
// Load from application's base directory. Alternative logic might be needed if you need to
// load from GAC etc. However, note that calling certain overloads of Assembly.Load will result
// in the AssemblyResolve event from firing recursively - see recommendations in
// http://msdn.microsoft.com/en-us/library/ff527268.aspx for further info
var assembly = Assembly.LoadFrom("ThirdPartyLibrary.dll");
return assembly;
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
return null;
}
We need to bind to the event before ThirdPartyLibrary is loaded, hence the explicit Initialise method.
Note also that the event only fires when the resolution of an assembly fails. If the version of ThirdPartyLibrary referenced by MyClassLibrary (1.0.0.0) were available in the GAC, then it would be loaded successfully and AssemblyResolve wouldn't fire. There would then be 2 different versions in use.
I'm demonstrating here that this mechanism could be used, I'm not saying it's a good idea. There are several things you'd need to take into account based on the environment in which your app is running and how it is set-up / installed / maintained etc.
No, you can't build MyClassLibrary with a reference to ThirdPartyClassLibrary in a way that says "just use whatever version of ThirdPartyClassLibrary.dll is available at runtime".
When you build your library, the version number of any referenced assemblies are included in the assembly manifest. Running the ILDASM tool against your assembly would show something like this:
...
.assembly extern ThirdPartyClassLibrary
{
...
.ver 1:0:0:0
}
...
Both the name and version of ThirdPartyClassLibrary are specified. At runtime, the CLR will attempt to load ThirdPartyClassLibrary.dll when it first runs instructions in MyClassLibrary.dll that reference it. It will look specifically for version 1.0.0.0 of ThirdPartyClassLibrary.dll (and will also require a matching public key if it's a strong-named assembly).
Here's a quick overview of how the CLR locates and binds to assemblies at runtime (full details at http://msdn.microsoft.com/en-us/library/yx7xezcf(v=vs.110).aspx):
Step 1 - Determine the correct assembly version by examining configuration files - we'll return to this below, but for now, if you don't tell it otherwise, the CLR will attempt to load the exact version specified in the referencing assembly, so it will be looking for version 1.0.0.0.
Step 2 - Check whether the assembly name has been bound to before and, if so, uses the previously loaded assembly. Note that "assembly name" in this context includes the name and version, public key token etc, not just the name of the dll file.
Step 3 - Check the Global Assembly Cache GAC (strong-named assemblies only)
Step 4 - Locate the assembly through codebases or probing - essentially the CLR looks in different places to try to find (the specific version of) AssemblyB.dll somewhere. An error will occur if it can't find the specific version. It won't automatically fall back to an earlier or later version.
Unfortunately, this means that things won't "just work" and support what you describe above. If an application that references MyClassLibrary itself references a later version (2.0.0.0) of ThirdPartyClassLibrary, some bad things could happen when resolving MyClassLibrary's reference to ThirdPartyClassLibrary:
The CLR can't find version 1.0.0.0 of AssemblyB used by Assembly A and an error occurs
Version 1.0.0.0 happens to be installed in the GAC and is loaded successfully. While the code in the application is using ThirdPartyClassLibrary version 2.0.0.0, your library is using ThirdPartyClassLibrary version 1.0.0.0.
One thing that you can do is configure the application using your library so that the CLR will unify references to different versions of ThirdPartyClassLibrary.dll to a single version. This brings us back to step 1 of the assembly binding process outlined above - we essentially change the version of ThirdPartyClassLibrary that the CLR is looking for.
Binding redirects (http://msdn.microsoft.com/en-us/library/twy1dw1e.aspx) are designed to channel references to different versions of an assembly to a single version. These are usually defined within an application's configuration file (Web.config, MyApp.exe.config), but can also be defined globally at machine level (machine.config).
Here's an example of a binding redirect that redirects all earlier versions of ThirdPartyClassLibrary.dll to version 2.0.0.0:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="AssemblyB" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Note that this can be automatically handled by Visual Studio 2013, which detects cases where different versions of an assembly are referenced and adds the binding redirects for you. There's also an Add-BindingRedirect command available in the NuGet Package Manager Console.
Binding redirects are a possible solution and could be a pragmatic choice in some scenarios. However, they could also confuse users of your library. If practical, you should consider distributing different versions of your library, built against the different versions of the third party library.

Dynamic Assembly Loading in .Net 4.0

My problem begins with moving a .Net 2.0 application to .Net 4.0. The reason I had to do this was that Windows 8 does not enable the earlier .Net versions by default and my application cannot ask the user to enable it.
The application is a NPAPI plugin which uses .Net components via UnmanagedExports. I designed it as a low integrity application and therefore it has to reside in the users 'LocalLow' directory.
In my application I used a dynamic assembly loading mechanism to load several assemblies at runtime. I used the following method to load an assembly,
MyInterface Instance;
Assembly assembly = Assembly.LoadFrom(AssemblyFile);
Type type = assembly.GetType(Identifier); // Identifier is implementing the MyInterface
Instance = Activator.CreateInstance(type) as MyInterface;
// Do something with the Instance
After modifying the project to .Net 4.0, I noticed that the plugin crashes when the binaries are placed inside the LocalLow directory (It works in other places). My next step was to create a minimalistic plugin with least possible code to figure out the issue. I noticed that the dynamic assembly loading failed with the following exception,
System.IO.FileLoadException: Could not load file or assembly '<assemblyPath>' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515 (COR_E_NOTSUPPORTED)) --->
System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=131738 for more information.
I tried the following approaches to create a separate domain and load the assemblies but with no luck,
http://blogs.msdn.com/b/shawnfa/archive/2009/06/08/more-implicit-uses-of-cas-policy-loadfromremotesources.aspx
http://www.west-wind.com/weblog/posts/2009/Jan/19/Assembly-Loading-across-AppDomains
Adding the configuration 'loadFromRemoteSources' did not work either. It seems that the .Net component does not load .dll.config files. (Could be because of UnmanagedExporting)
My questions are,
Is it possible to dynamically load an assembly from LocalLow?
Does the new CAS policy in CLR 4.0 apply to LocalLow as well? From what I understood so far it should affect only assemblies loaded over the network
Is there any other way to overcome this issue?
While it doesn't address your LocalLow issue specifically, if you are able to "read a file" from the directory, you might be able to use the "work around" detailed here:
How can I get LabView to stop locking my .NET DLL?

Categories