Click here to Skip to main content
15,884,629 members
Please Sign up or sign in to vote.
4.33/5 (5 votes)
See more:
Hi,

I am working on a project where I have to load assemblies (lets call them tasks) at runtime, run the task and then be able to kill off the whole assembly so that the dll can be replaced without interupting the main application.

There are many of these tasks running within the main application, some run sequentially and some run in parallel. Occasionally one or to of these tasks need to be updated and then re-added to the queue. Currently we are stopping the entire application and interupting the other tasks at whatever stage they are at, which is not ideal.

I have figured out that I will have to load each assembly into a seperate AppDomain, but loading the assemblies in those domains is proving difficult, especially when I need to actually run the tasks and receive events from them. I have been looking into this problem for a couple of days and have still not managed to get a working proof-of-concept.

I have an Interface for the tasks which includes a 'run' and 'kill' method (subs) and a 'taskstep', 'complete' and 'killed' event. 'taskstep' returns an object to be cached later, 'complete' fires when the whole task is done and 'killed' fires when it is ready to be unloaded. There should also be a timeout on the whole process of two hours and a timeout of 2 minutes on the killed event incase it gets stuck, at which point I would like to be able to unload it, forcing any threads to terminate (which is what 'kill' should do anyway). Each assembly may contain several tasks, all of which should loadable be unloadable.

I have no problems loading these tasks as 'plugins' but am lost when trying to both use them and unload them. If I have to create some elaborate wrapper then so be it but is what I need even possible?

(PS. I wish I could do this in c# but I have been told that VB is preferable. If it is only possible in c# then all the better for me, but not so much for my boss ^_^)

Many thanks
Andy
Posted
Comments
Sergey Alexandrovich Kryukov 1-Dec-11 14:23pm    
This is an interesting bunch of problems, my 5 for the question. I got considerable experience working with such things, some of it shared in my solution, please see.
--SA
BillWoodruff 1-Dec-11 22:01pm    
+5 for your Excellent question ! A quite possibly dumb question: isn't this type of dynamic loading, execution, and unloading that MEF is designed for ? I once tried using the multiple-AppDomain based technique of loading "plug-ins:" getting objects passed back and forth between the main app and the plug-ins drove me nuts. Trying to give the plug-in access to, and freedom to change, some big data structure in the main app was what caused me to abandon ship on AppDomains.

I've done this sort of thing before a few times. As you say, the only way to unload an assembly is to ditch the whole appdomain it's in. The trick comes because appdomains are isolated from each other (the very purpose) so you need to marshall the data to and from them.

The exact way I've done this I'm struggling to remember, but you'll need to create a host class in each domain and make it inherit from MarshalByRefObject. In your calling (main) domain a proxy is created which mimics this. So this object needs to contain all the methods and things you need to communicate with the domain. Stick those in an interface shared by all domains.

Each domain can have its own seperate base folder containing the assemblies or can have them explicitly loaded. By pointing it at a folder to do resolution though helps with loading dependencies.
 
Share this answer
 
Comments
Andy Lanng 1-Dec-11 7:48am    
ah ha. MarshalByRefObject sounds like a good starting point for the communication aspect. I'm still struggling to load my assemblies in the appdomains, mainly because I don't know the assembly name or class types until runtime so I think more reading is involved ^_^
Rob Philpott 1-Dec-11 7:58am    
This sort of stuff is covered well in the book "Customizing the Microsoft.NET Framework Common Language Runtime". Not an easy read though. Found something pertinent here: http://flylib.com/books/en/4.331.1.40/1/
Using COM objects for this is a very interesting solution and is part of the windows os for quite some time. You can create in process or out of process. Using Excel in an application is an example of usage of out of process (although Excel is not the best example because it is an interactive process and in that way not suitable for server applications). But the idea is about the same. You can request a COM object which will be loaded into memory by the COM server and can then be used. In your case you start a task. You can use an event sink for receiving events and when you are done you can simply discard it. The COM Object will stay in the pool for a while and after some time it is also discarded from memory. The COM server keeps it in a pool for a while just in case you need another quite soon. This means that you don't need to worry about it yourself. But why use this? Well, the most interesting part might be that you can also use transactions. Meaning that you can, for example, make a file delete object. You start a new transaction, create 3 file delete com objects for 3 different files and then commit. The 3 files are then only deleted if all 3 can be deleted. If one cannot be deleted, the transaction will fail and none of the files is deleted. Another nice feature is DCOM which makes it possible to create a COM object that is on another system. Even these objects can be part of a transaction giving you great control of the whole operation. It might be interesting enough to investigate.

http://msdn.microsoft.com/en-us/library/aa645736%28v=vs.71%29.aspx[^]

Good luck!
 
Share this answer
 
Comments
Andy Lanng 1-Dec-11 7:49am    
That sounds like a very interesting possability. I will try solution 1 first as I am more familiar with loading assemblies, but failing that I will try the DCOM thing. ^_^
Hello,

Have you looked at using reflection?

I few years ago, I had to load and use assemblies that were changing at runtime.

This is how I did it:

string pathToAssembly = Path.Combine(Application.StartupPath, "myAssemby.dll");
object[] parameters = null;
Assembly assembly = Assembly.LoadFile(pathToAssembly);
Type type = assembly.GetType("MyAssembly.Form1");
MethodInfo methodInfo = type.GetMethod("ShowDialog", Type.EmptyTypes);
object instance = Activator.CreateInstance(type);

methodInfo.Invoke(instance, parameters);


Valery.
 
Share this answer
 
Comments
Andy Lanng 1-Dec-11 7:46am    
Thanks for your solution but it doesn't entirely apply to the specifics of my problem
The best method if your assemblies are not very large (which will not be the case for plugins etc.) is to do the following :

C#
byte[] b = File.ReadAllBytes(filename);
Assembly a= Assembly.Load(b);
// cache the assembly if needed here


This will not lock your DLL files, and no marshaling is required.
 
Share this answer
 
v2
Comments
Andy Lanng 1-Dec-11 10:21am    
I have been experimenting with this method and it works very well. I will try to combine it with solution 1 as I will still need to unload the assemblies from time to time or I will end up with a whole load of unused assemblies in the main appdomain. Thanks guys. I'll credit the answers when I get a prototype running
Barış Halıcı 3-Mar-21 3:58am    
thanks, it helped me a lot
I provided detailed answers to close questions in near past. Please see:

Create WPF Application that uses Reloadable Plugins...[^],
AppDomain refuses to load an assembly[^],
code generating using CodeDom[^].

—SA
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900