What determines when and if an assembly gets loaded into the AppDomain.
I guess I am trying to understand if:
I am guaranteed that all project referenced assemblies will be
loaded at the start of my application.
If not when exactly do
they get loaded? Is it going to be the first time I use a
class/function from that assembly?
Is there a way to tell at
compile time if an assembly must be loaded first, before execution
of the first line in my code? (I know I can use reflection, but I
want to know if I can configure this at compile time)
Assuming I
can control when an assembly gets loaded, what about the dependent
assembly? Can I tell the execution to only load up to 1st/2nd degree
and let the rest be loaded dynamically?
no, you are guaranteed the opposite: only assemblies directly needed at start are loaded.
yes, there will be attempt to load assembly as soon as some class will require type information from that assembly or code that needs that assembly will be JITed.
no, there is no way at compile time to force assembly load sequence short of referencing something from each assembly in your Main (note that usually goal is opposite - to delay loading of as many assemblies as possible to speed up loading of an app).
no, you can't control automatic loading (as Simon Edström points out there Assembly Resolve event fired when CLR decides it needs an assembly). You can always pre-load assemblies yourself if you know dependencies.
Note: assemblies don't "depend" on each other directly, just classes/methods in each depend on each other.
I don't know the details but I know that there is event thorwn when an assemblies can't be loaded. So I guess they are loaded at runtime and you could add routines to it.
I think this will help you a bit about Assembly Resolve http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve.aspx
And this about Assembly Load Events http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyload.aspx
Related
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.
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.
I want to add some behavior to a certain class at runtime. I know how to subclass at runtime using Reflection.Emit but that's not enough. Depending on some external configuration I need to inject opcodes in a method on a type T so all classes that inherit from it automatically gain this behavior. (I can't use the .NET Profiling API).
Can something like this be done with Mono.Cecil?
If it isn't possible to modify code on a loaded assembly, it is fine If I can make the modifications before the assembly is loaded and then load the modified assembly in memory, but I don't know how I can control assembly loading.
Nope, Cecil can not modify a loaded assembly. You have to instrument assemblies before they are actually loaded.
You don't have much control over how assemblies are resolved. You can hook into AppDomain.AssemblyResolve if you hide the assemblies in a private folder of yours, and instrument then before loading them.
As JB Says above-
You can create a Resolve Event handler - which would be like PSeudoHooking.
And before the assembly is loaded, you make your changes, and then once the changes are done, the Resolve Assembly then continues on to load the changed assembly.
I use this method for resolving Embedded Dll's from Memory Streams.
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...