53

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.

Ray
  • 187,153
  • 97
  • 222
  • 204
Derik Whittaker
  • 832
  • 1
  • 7
  • 10
  • 1
    Try not to use LoadFrom, it is in a different context than the Load context and can cause problems. – user7116 Sep 24 '08 at 01:09
  • Good question but I cannot see any CLEAR answer about how to resolve var assembly = Assembly.LoadFrom( FilePathHere ); – NoWar Jul 13 '12 at 17:09

8 Answers8

44

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.

Community
  • 1
  • 1
Philip Rieck
  • 32,368
  • 11
  • 87
  • 99
  • I thought this changed with .Net 3.5? – Derik Whittaker Sep 23 '08 at 19:52
  • 1
    There are no plans to allow the unload of an assembly from an app domain (I wouldn't expect them to either, it is very hard to do and those that care will use a safer and cleaner workaround). The best you can do is lightweight code gen which Khoth mentioned – ShuggyCoUk Mar 20 '09 at 14:46
30

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;
James Curran
  • 101,701
  • 37
  • 181
  • 258
Kirk Herron
  • 319
  • 3
  • 3
  • 1
    By "**Cannot use AppDomain**" you mean that you can't initiate one inside that assembly or could you be more specific? What about `AppDomain.CurrentDomain.Load(File.ReadAllBytes(FilePathHere));` is there any real difference from Assembly.Load in this specific scenario? – Prix Oct 23 '16 at 03:29
  • Was struggling around 1 day with marshaling, new AppDomains and stuff. This works great for me! – kungcc Apr 10 '18 at 12:41
  • 1
    Problem whe you need to comunicate two assemblies =( – Zaha May 02 '20 at 00:36
  • I was need to just read assembly data, so this approach helped. Thank You. – SkyDancer May 30 '23 at 14:10
16

You can't unload an assembly without unloading the whole AppDomain. Here's why:

  1. 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.

  2. 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).

  3. 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.

Community
  • 1
  • 1
Mark Cidade
  • 98,437
  • 31
  • 224
  • 236
11

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.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Nipun
  • 1,465
  • 4
  • 20
  • 28
  • 2
    See AppDomain.DoCallback and also see MarshalByRefObject: you create a MarshalByRefObject "invoker" with a method that does what you want and returns something in its mutable fields, you create a temporary AppDomain and call dom.DoCallback(invoker.DoSomething) to make this object do what you want; then you collect the results from invoker in your appdomain and unload the temporary domain. – jkff Jul 19 '11 at 15:23
  • @jkff: Unfortunately if you use the MarshalByRefObject, as in the PingPong example, the Assembly.LoadFrom will execute as if in the main AppDomain, so unloading the temp AppDomain will still yield the old problem of not being able to unload the assembly. I think you may have to somehow serialize the data you want from the temp AppDomain, without marshaling, if you want to access it and still be able to unload the assembly. – user420667 May 08 '13 at 16:30
  • @jkff: You might be right though... but it is not as simple as what is described in the MSDN example. One should probably use tempAppDomain.CreateInstanceAndUnwrap... and it might be necessary even to use AppDomain.GetData and SetData. (This won't really clutter the appdomain if it's only purpose is to be loaded and unloaded.) – user420667 May 08 '13 at 19:57
5

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.

Khoth
  • 13,068
  • 3
  • 28
  • 27
1

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

GenZiy
  • 1,427
  • 2
  • 15
  • 20
1

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.

0

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
}
Gerrie Pretorius
  • 3,381
  • 2
  • 31
  • 34
  • This isn't true, although the assemblies are not considered part of the assembly (hence the result of the `GetAssemblies()` call), they will not be unloaded until the app domain is unloaded, see https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.reflectiononlyloadfrom?view=netframework-4.8 – yoel halb May 22 '19 at 04:16
  • @yoelhalb if you say "this isn't true" what are you referring to? the part before the edit, or after? i am not mentioning "unloading" anywhere in my answer? GetAssemblyName() is not loading the assembly into the app domain, hence you don't have to worry about unloading. ReflectionOnlyLoad() loads it into a different context, where the code can't be executed, so you also don't need to unload it either. – Gerrie Pretorius May 23 '19 at 08:35
  • As the link to MSDN in my first comment specifies ReflectionOnlyLoad actually loads it into the AppDomain just into a different context that doesn't allow to execute code from it, however it can't be unloaded until the AppDomain is unloaded, and I actually tested that and it is the way it works. – yoel halb Jun 16 '19 at 17:01