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.
Related
Ok so I have a pretty unique problem here. I'm getting an error basically because I'm referencing the latest version of a dll which I still want to keep references to by default because most of the code in my project is supposed to be using this dll. The error occurs because it's trying to use an object that is only available in the older version of the dll. So I want to use this older version of the dll for this particular section of code. I have tried to load this older version of the dll using Assembly.LoadFrom(pathToAssembly) but it still appears to reference the newer version of the dll. Does anyone have any ideas on how I can replace the reference to this dll to the older version?
Only real option you have is to make sure that assembly is strongly signed, make sure there is no assembly binding redirect to newer version and than manually (with Assembly.LoadFrom) load second version into your appDomain. This way code will be able to use precise version of assembly and both assemblies can be loaded into same appDomain at the same time.
Note that this will lead to complete nightmare if you ever need to pass references to such objects between pieces of code linked against different assemblies.
If you want extra painfun - load both assemblies from bytes and use reflection to construct types for each version...
I'd strongly recommend avoiding all the pain by loading code using different versions of assembly to at least separate appDomains, but preferably to separate processes. If you still decide to take adventurous path of loading multiple versions of assembly to same appDomain make sure to read all aassembly loading blog post from https://blogs.msdn.microsoft.com/suzcook/2003/09/19/loadfile-vs-loadfrom/
I would like to know how to unload an assembly that is loaded into the main AppDomain.
I have the following code:
var assembly = Assembly.LoadFrom( FilePathHere );
I need/want to be able to unload this assembly when I am done.
Thanks for your help.
For .net versions core 3.0 and later:
You can now unload assemblies. Note that appdomains are no longer available in .net core. Instead, you can create one or more AssemblyLoadContext, load your assemblies via that context, then unload that context. See AssemblyLoadContext, or this tutorial that simulates loading a plugin then unloading it.
For .net versions before .net core 3, including netframework 4 and lower
You can not unload an assembly from an appdomain. You can destroy appdomains, but once an assembly is loaded into an appdomain, it's there for the life of the appdomain.
See Jason Zander's explanation of Why isn't there an Assembly.Unload method?
If you are using 3.5, you can use the AddIn Framework to make it easier to manage/call into different AppDomains (which you can unload, unloading all the assemblies). If you are using versions before that, you need to create a new appdomain yourself to unload it.
I also know this is very old, but may help someone who is having this issue!
Here is one way I have found to do it!
instead of using:
var assembly = Assembly.LoadFrom( FilePathHere );
use this:
var assembly = Assembly.Load( File.ReadAllBytes(FilePathHere));
This actually loads the "Contents" of the assembly file, instead of the file itself. Which means there is NOT a file lock placed on the assembly file! So now it can be copied over, deleted or upgraded without closing your application or trying to use a separate AppDomain or Marshaling!
PROS: Very Simple to fix with a 1 Liner of code!
CONS: Cannot use AppDomain, Assembly.Location or Assembly.CodeBase.
Now you just need to destroy any instances created on the assembly.
For example:
assembly = null;
You can't unload an assembly without unloading the whole AppDomain. Here's why:
You are running that code in the app domain. That means there are potentially call sites and call stacks with addresses in them that are expecting to keep working.
Say you did manage to track all handles and references to already running code by an assembly. Assuming you didn't ngen the code, once you successfully freed up the assembly, you have only freed up the metadata and IL. The JIT'd code is still allocated in the app domain loader heap (JIT'd methods are allocated sequentially in a buffer in the order in which they are called).
The final issue relates to code which has been loaded shared, otherwise more formally know as "domain neutral" (check out /shared on the ngen tool). In this mode, the code for an assembly is generated to be executed from any app domain (nothing hard wired).
It is recommended that you design your application around the application domain boundary naturally, where unload is fully supported.
You should load your temporary assemblies in another AppDomain and when not in use then you can unload that AppDomain. It's safe and fast.
If you want to have temporary code which can be unloaded afterwards, depending on your needs the DynamicMethod class might do what you want. That doesn't give you classes, though.
Here is a GOOD example how to compile and run dll during run time and then unload all resources:
http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm
I know its old but might help someone. You can load the file from stream and release it. It worked for me. I found the solution HERE.
Hope it helps.
As an alternative, if the assembly was just loaded in the first place, to check information of the assembly like the publicKey, the better way would be to not load it,and rather check the information by loading just the AssemblyName at first:
AssemblyName an = AssemblyName.GetAssemblyName ("myfile.exe");
byte[] publicKey = an.GetPublicKey();
CultureInfo culture = an.CultureInfo;
Version version = an.Version;
EDIT
If you need to reflect the types in the assembly without getting the assembly in to your app domain, you can use the Assembly.ReflectionOnlyLoadFrom method.
this will allow you to look at they types in the assembly but not allow you to instantiate them, and will also not load the assembly in to the AppDomain.
Look at this example as exlanation
public void AssemblyLoadTest(string assemblyToLoad)
{
var initialAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //4
Assembly.ReflectionOnlyLoad(assemblyToLoad);
var reflectionOnlyAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //4
//Shows that assembly is NOT loaded in to AppDomain with Assembly.ReflectionOnlyLoad
Assert.AreEqual(initialAppDomainAssemblyCount, reflectionOnlyAppDomainAssemblyCount); // 4 == 4
Assembly.Load(assemblyToLoad);
var loadAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //5
//Shows that assembly is loaded in to AppDomain with Assembly.Load
Assert.AreNotEqual(initialAppDomainAssemblyCount, loadAppDomainAssemblyCount); // 4 != 5
}
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.
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.
I have a little bit of code that loops through the types currently loaded into an AppDomain that runs in an ASP.NET application. Here's how I get the assemblies:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
When the application first starts up there is no problem and all the types I expect are present. But when I update the Web.config or kill the w3p.exe process (or the process gets recycled for whatever reason) only some of the types I'm expecting are available. When I step through with a debugger I notice that certain assemblies from the private search path (the bin directory of my application) haven't been loaded. I was under the assumption that all assemblies were loaded at application start and restart whether or not they were immediately required. But in the case of restarting this doesn't seem to be happening unless those assembly files have been updated.
What I require is to collect type information at start-up for use later. But since during a restart the types aren't available it reeks havoc later on when the type information needs to be used. So with that in mind how can I solve or work around this deficiency?
Assemblies are loaded on demand, so it could be that you did not used any type contained in these assemblies yet.
You can use
AssemblyName[] assemblies = Assembly.GetCallingAssembly().GetReferencedAssemblies();
This way, you get all assemblies that are referenced from the assembly from which you are calling these method.
As a part of startup, can you explicitly load the assemblies you care about?
You would have to know ahead of time which assemblies you would need.
Scanning the filesystem to find out which assemblies have been shipped along with your app may be a useful idea, but it won't help for GAC loaded assemblies...