Dynamically loading multiple versions of an assembly - c#

I am writing a test app to perform some regression tests. The idea is to be able to run test over multiple versions of a library. My goal is to load the dlls up in a Dictionary where the key is the version string (such as "3.0.0.0") and the value is the Assembly instance. I am able to dynamically load one assembly and call a method on it, but when I try to load a second one, I get the following exception:
The located assembly's manifest definition does not match the assembly reference.
I am loading the assemblies with the following line:
asm = Assembly.LoadFrom(lib, hash, System.Configuration.Assemblies.AssemblyHashAlgorithm.MD5);
'lib' is the complete filename and path of the dll.
'hash' is the md5 sum of the dll.
I looks like even though I am telling Windows "use this dll", it looks at the name and says "I already have that one" and uses the previously loaded one and since the hash doesn't match, it fails. Originally, the dlls being loaded did not have an Assembly Version set, so I set it on 4 different versions, but it still threw the same exception.
What is the fix for this?
Jordon

You cannot load more than one version of the same assembly into single AppDomain. Also once loaded, assembly cannot be unloaded from AppDomain (with exception of dynamically created transient assemblies in .NET 4), but it is possible to unload whole AppDomain (which unloads all assemblies, that were loaded in it). Therefore you must load each version of your assembly into separate (newly created) AppDomain. Also be very careful to NOT pass any reference to loaded assembly between individual AppDomains (and especially to main AppDomain, where your testing app resides), because otherwise .NET will try to load assembly into every AppDomain, where reference to this assembly appears and you will end up with the same error again.

You'll need to the assemblies into separate AppDomains.

Related

Load an assembly from plugin

INTRODUCTION
I'm writing a plugin for Navisworks and i'm using the Dropbox api to download/upload documents from a repository.
PROBLEM
Dropbox.Api uses the Newtonsoft.Json.dll version 7.0, the problem is that Navisworks uses the 4.0 version of the same assembly, so I cannot use the Dropbox api because it throws an exception every time:
System.AggregateException: One or more errors occurred. ---> System.IO.FileLoadException: Could not load file or assembly 'Newtonsoft.Json, Version=7.0.0.0, ...
So as I understand the program has the assembly 4.0v so Dropbox.Api cannot execute properly.
By now, what I've done is to use another process which I can load the right assembly and download/upload the files from there, but I would like to avoid using a second process.
I'm trying to use reflection to load the assembly at runtime, but it takes no effect, the program still cannot find the newer assembly.
//Load the assembly at the beginning of the plugin
var ass = System.Reflection.Assembly.Load(Properties.Resources.Newtonsoft_Json);
//Use the Dropbox api
//Exception...
Can I force, somehow, the program to use the newer assembly (temporary)?
Is there some solution that I've missed?
You are encountering this issue because you cannot load two different non-strong-named versions of a .NET assembly (no matter where it is on the file system) into the same AppDomain. By default you start with a single AppDomain called the Primary AppDomain for your process.
A strong-named assembly is one that takes the filename; version; signing key and culture to produce a unique assembly.
By now, what I've done is to use another process which I can load the right assembly and download/upload the files from there, but I would like to avoid using a second process.
No need to create a second process, you can instead create a 2nd AppDomain in the same process. Each AppDomain can load different versions of assemblies including Newtonsoft.Json without conflict.
I'm trying to use reflection to load the assembly at runtime, but it takes no effect, the program still cannot find the newer assembly.
That won't work, it's essentially the same as letting .NET doing it for you automatically.
The only time when you can load multiple versions of .NET assemblies into the same AppDomain is if the assembly (in this case the NuGet package) and dependent assemblies are all strong-named. For some reason I have never been able to fathom is why most open source .NET devs refuse to strong-name assemblies.

.Net Application Memory Management

Could be a very naive question, but was wondering how this stuff works: Lets assume that we have 10 projects in Visual Studio, and 5 of them have references to an external DLL (say Ext.dll), using a relative path.
Now when my application is deployed an running on client machine, would Ext.dll get loaded 5 times in memory? Or would it just get loaded once and gets used by other referencing projects?
Assembly will be loaded only once in memory.
CLR first check if assembly already loaded in current AppDomain, if not than assembly gets loaded under AppDomain otherwise symbols are resolved from the already loaded assembly.
Ofcourse unless you are manually creating another AppDomain which has its own set of assemblies.
Moreover, assembly with same version cannot be loaded in memory at
same time. CLR doesn't allow that. But you can have different versions
of same assembly to be loaded in memory and that too in case
assemblies are strongly signed. But in your case version is same so CLR won't load same assembly twice anyhow.
If you want to check at certain interval time that what assemblies are loaded in a memory, you can use this piece of code to get all loaded assemblies:
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
Clr load assembly in memory just once.
Note :for each instance of application Clr load assembly again.
You can read Clr via c#.In chapter one you can learn many of these Concepts.

C# Load different versions of assembly to the same project

I'm creating some tool what performs several operations like NUnit.
Inside this tool I open .dll assembly and invoke methods form it to run some test.
Everything is going OK till the time I need to reload .dll withour program restart. The idea is that when tool is run we copy required assembly to some temporary folder and invoke from there. If I need to reload I copy another one to another temporary folder and try to load newly copied from another folder and load to previous assembly object
ExecutingAssembly = Assembly.LoadFrom(AssemblyFullPath);
But my problem is that after I change AssemblyFullPath to new one and call Assembly.LoadFrom it returns just old assembly what was loaded first time but not the second one!
Maybe the problem is that we cannot load several assemblies with different versions? What is the solution?
The CLR does support loading multiple versions of strongly named assemblies into the same AppDomain. This only works though if your assemblies are strongly named and each one has a different version than the other.
I'm guessing it's more likely that you are dealing with unsigned assemblies. If that is the case then what you're asking for isn't really possible. Once a given assembly is loaded into an AppDomain it will remain there until the AppDomain is unloaded. To get this to work you will have to abstract out all of the work around the loaded assemblies into a separate AppDomain and use a new AppDomain for every assembly
To expand on JaredPar's answer, you will need to create a new AppDomain and use Remoting to communicate between the two.
Check out http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.85).aspx to help get you started.
Try like this:
string dllFile = "C:\\sample.dll";
Assembly asmLoader = Assembly.LoadFile(dllFile);
Type[] types = asmLoader.GetTypes();
Since all resources from the assembly cannot be reloaded/replaced it's assembly resources while application is still running. It will only be replaced/removed when application is unloaded or the that Assembly that holds it.
Use LoadFile() method. Hope it helps.

FileVersionInfo.GetVersionInfo shows wrong version for a replaced file

Why is it that when a .NET DLL is loaded, replaced from another app domain (DLL is updated with a new version), and then reloaded (using Assembly.LoadFrom) that the version info still reflects the old version?
The same is observed with assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false) or assembly.GetCustomAttributes(typeof(AssemblyVersionAttribute), false).
Is this the normal behavior? If I inspect the file in Explorer, I see the correct version, though.
Is there any way to get the actual version of the DLL?
It isn't very clear, but the term "re-loaded" is a strong indicator for what you see. The CLR will not permit reloading a different version of the same assembly with Assembly.LoadFrom(). This is a strong DLL Hell counter measure and avoids a lot of nasty runtime exceptions. In particular InvalidCastExceptions that say "Cannot cast Foo to Foo". Type identity in .NET includes the [AssemblyVersion] of an assembly. Calling Assembly.LoadFrom() will just return a reference to the previously loaded assembly.
Nor is there a way to unload an assembly from an AppDomain. Only thing you can do is create a new AppDomain.
I should not mention Assembly.LoadFile(), it doesn't perform this check, that's major misery.
When you load an assembly into an AppDomain, you cannot unload it. So replacing the file and reloading it in an AppDomain that already loaded the assembly simply does not work (that's by design). You need a new AppDomain to load the replaced assembly.

Unload AppDomain Assembly

I want to read some information from a .Net assembly, then modify the DLL by appending a short sequence of characters.
The first part works fine, but the second step fails, as the assembly is still in use.
This is the case although I loaded the assembly in its own AppDomain and after I finished step 1 unloaded the AppDomain.
Your best bet is to use something like Cecil that allows you to inspect and rewrite assemblies without loading them in the AppDomain.
References from assemblies loaded into a separate AppDomain have a nasty habit of "leaking" into the parent AppDomain, especially if you're reflecting the assembly and exchanging Type information across the boundary. There are lots of "gotchas" with keeping assemblies isolated in AppDomains truly segregated.
However, there's good news: you probably don't need to worry about the assembly being unloaded in order to modify it on disk - just use shadow copying! Create an AppDomainSetup instance, set its ShadowCopyFiles property to true, and pass it when you create the new AppDomain. This will cause the assembly to be copied to a temporary file before being loaded, keeping the original assembly unlocked and available for modification.
You cannot unload an assembly without unloading the AppDomain:
http://msdn.microsoft.com/en-us/library/ms173101(VS.80).aspx
There is no way to unload an
individual assembly without unloading
all of the application domains that
contain it. Even if the assembly goes
out of scope, the actual assembly file
will remain loaded until all
application domains that contain it
are unloaded.
Are you sure that no other process or AppDomain is using the assembly?

Categories

Resources