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.
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
}
Our code uses a "Plugin" model, that remotely loads dll's conforming to a predefined structure IPluginModel that is defined in the main program. The main program itself has several localized forms where the text and placement of the labels in the UI are all changed based on the different localization Thread.CurrentThread.CurrentUICulture.
One thing that we noticed is that any forms or reports from the remotely loaded dll's will never be properly localized. It does not seem to matter where the localization dll's containing the different resources for the plugin are located, either next to the main form, next to the plugin dll, or anywhere else. How would one cause the Assembly to correctly locate its localized resources when the assembly itself is loaded during RunTime of the main program?
I do have code that rather resembles the method used Here, but dont want to have to manually implement the resx against the form itself if at all possible. The code I have is located directly inside the plugin itself and is called whenever the CurrentUICulture is NOT "en-US".
Ideally speaking, what Im looking for is a way to load the pluginName.resources.dll which is directly associated with the plugin I just loaded. I do see the different folders in my main projects bin folder, the es folder contains a main.resources.dll, but simply placing the plugin dll's in that folder did not seem to work last time I tried it, although in theory that could have changed...or I may have done it wrong at the time...
Loading the assembly [code snippet]
private void LoadPlugin(string filePath)
{
bool isValidPlugin = false;
Assembly asm = null;
try
{
asm = Assembly.LoadFrom(Path.Combine(Environment.CurrentDirectory, filePath));
//Do some type checking to make certain this is in fact one of OUR plugins
var p = (IFTLPlugin)Activator.CreateInstance(types[x]);
_plugins.Add(p.Prefix, p);
}
}
Edit: More thoughts on the subject
Is there some way to intercept the loading structure that attempts to resolve the satellite assemblies, an event that I can implement or a function I can override, where I can manually point the code to the correct resource assembly?. I found some things about assembly resolution, but that was for direct loading, not for satellite resources.
As you know perfectly well already, the runtime loads localized resources using a convention, by looking for satellite assemblies (e.g. fr/pluginName.resources.dll).
I think you may be right to think that this issue may be due to a failure to find those satellite assemblies . You could try confirming this using the .NET Framework diagnostics tool fuslogvw.exe. You can also try to deploy your satellite assemblies to the Global Assembly Cache (GAC) as a quick fix (see Installing a Satellite Assembly in the Global Assembly Cache on this page).
If that works, and to help figure out other suitable locations (other than the GAC), the locations inspected by the runtime to look for satellite assemblies are documented as the fallback process here:
http://msdn.microsoft.com/en-us/library/sb6a8618%28v=vs.71%29.aspx
The .NET system, when loading resource assemblies, uses the Thread.CurrentThread.CurrentUICulture, as you pointed out in your question background.
The problem is that each new thread defaults to CultureInfo.InstalledCulture until it is told otherwise.
Have your main code pass the desired culture to the library in your initialization call.
Then have the library set its Thread.CurrentThread.CurrentUICulture (and .CurrentCulture if appropriate) before you load any resources. That would be before any call to InitializeComponent() or TryFindResource(), for example.
This gets the library's thread's cultures aligned with the main program's thread's cultures, regardless of what the OS's ideas about it are.
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 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...
If you create a class library that uses things from other assemblies, is it possible to embed those other assemblies inside the class library as some kind of resource?
I.e. instead of having MyAssembly.dll, SomeAssembly1.dll and SomeAssembly2.dll sitting on the file system, those other two files get bundled in to MyAssembly.dll and are usable in its code.
I'm also a little confused about why .NET assemblies are .dll files. Didn't this format exist before .NET? Are all .NET assemblies DLLs, but not all DLLs are .NET assemblies? Why do they use the same file format and/or file extension?
ILMerge does merge assemblies, which is nice, but sometimes not quite what you want. For example, when the assembly in question is a strongly-named assembly, and you don't have the key for it, then you cannot do ILMerge without breaking that signature. Which means you have to deploy multiple assemblies.
As an alternative to ilmerge, you can embed one or more assemblies as resources into your exe or DLL. Then, at runtime, when the assemblies are being loaded, you can extract the embedded assembly programmatically, and load and run it. It sounds tricky but there's just a little bit of boilerplate code.
To do it, embed an assembly, just as you would embed any other resource (image, translation file, data, etc). Then, set up an AssemblyResolver that gets called at runtime. It should be set up in the static constructor of the startup class. The code is very simple.
static NameOfStartupClassHere()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver);
}
static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args)
{
Assembly a1 = Assembly.GetExecutingAssembly();
Stream s = a1.GetManifestResourceStream(args.Name);
byte[] block = new byte[s.Length];
s.Read(block, 0, block.Length);
Assembly a2 = Assembly.Load(block);
return a2;
}
The Name property on the ResolveEventArgs parameter is the name of the assembly to be resolved. This name refers to the resource, not to the filename. If you embed the file named "MyAssembly.dll", and call the embedded resource "Foo", then the name you want here is "Foo". But that would be confusing, so I suggest using the filename of the assembly for the name of the resource. If you have embedded and named your assembly properly, you can just call GetManifestResourceStream() with the assembly name and load the assembly that way. Very simple.
This works with multiple assemblies, just as nicely as with a single embedded assembly.
In a real app you're gonna want better error handling in that routine - like what if there is no stream by the given name? What happens if the Read fails? etc. But that's left for you to do.
In the rest of the application code, you use types from the assembly as normal.
When you build the app, you need to add a reference to the assembly in question, as you would normally. If you use the command-line tools, use the /r option in csc.exe; if you use Visual Studio, you'll need to "Add Reference..." in the popup menu on the project.
At runtime, assembly version-checking and verification works as usual.
The only difference is in distribution. When you deploy or distribute your app, you need not distribute the DLL for the embedded (and referenced) assembly. Just deploy the main assembly; there's no need to distribute the other assemblies because they're embedded into the main DLL or EXE.
Take a look at ILMerge for merging assemblies.
I'm also a little confused about why .NET assemblies are .dll files. Didn't this format exist before .NET?
Yes.
Are all .NET assemblies DLLs,
Either DLLs or EXE normally - but can also be netmodule.
but not all DLLs are .NET assemblies?
Correct.
Why do they use the same file format and/or file extension?
Why should it be any different - it serves the same purpose!
You can embed an assembly (or any file, actually) as a resource (and then use the ResourceManager class to access them), but if you just want to combine assemblies, you're better off using a tool like ILMerge.
EXE and DLL files are Windows portable executables, which are generic enough to accomodate future types of code, including any .NET code (they can also run in DOS but only display a message saying that they're not supposed to run in DOS). They include instructions to fire up the .NET runtime if it isn't already running. It's also possible for a single assembly to span across multiple files, though this is hardly ever the case.
Note ILMerge doesn't work with embedded resources like XAML, so WPF apps etc will need to use Cheeso's method.
There's also the mkbundle utility offered by the Mono project
Why do they use the same file format and/or file extension?
Why should it be any different - it serves the same purpose!
My 2ยข bit of clarification here: DLL is Dynamic Link Library. Both the old style .dll (C-code) and .net style .dll are by definition "dynamic link" libraries. So .dll is a proper description for both.
With respect to Cheeso's answer of embedding the assemblies as resources and loading them dynamically using the Load(byte[]) overload using an AssemblyResolve event handler, you need to modify the resolver to check the AppDomain for an existing instance of the Assembly to load and return the existing assembly instance if it's already loaded.
Assemblies loaded using that overload do not have a context, which can cause the framework to try and reload the assembly multiple times. Without returning an already loaded instance, you can end up with multiple instances of the same assembly code and types that should be equal but won't be, because the framework considers them to be from two different assemblies.
At least one way that multiple AssemblyResolve events will be made for the same assembly loaded into the "No context" is when you have references to types it exposes from multiple assemblies loaded into your AppDomain, as code executes that needs those types resolved.
https://msdn.microsoft.com/en-us/library/dd153782%28v=vs.110%29.aspx
A couple of salient points from the link:
"Other assemblies cannot bind to assemblies that are loaded without context, unless you handle the AppDomain.AssemblyResolve event"
"Loading multiple assemblies with the same identity without context can cause type identity problems similar to those caused by loading assemblies with the same identity into multiple contexts. See Avoid Loading an Assembly into Multiple Contexts."
I would suggest you to try Costura.Fody. Just don't forget to Install-Package Fody before Costura.Fody (in order to get the newest Fody!)