I have a MEF-based application that can be customized with plugins. This application has several imported parts, and I want to remove some of them at runtime (to be able to delete the .dll that contains them) when a user decides to get rid of that plugin.
CompositionBatch would do what I need, but it needs ComposablePart instances as input parameters for RemovePart() method, and I only have plain objects that implement ISomething interface, or ComposablePartDefinition instances in the AggregateCatalog. So the my question is:
How can I find the ComposablePart
instance that represents the imported
object that I want to get rid of?
Or
alternatively: how do I get the list
of ComposablePart objects that belong
to a certain .dll?
I would use something like follows:
var parts = Container.Catalog.Parts
.Where(p => iDontNeed(p))
.Select(p => howDoIConvertComposablePartDefinition2ComposablePart(p));
var batch = new CompositionBatch();
parts.ToList().ForEach(part => batch.RemovePart(part));
Thank you
how do I get the list of ComposablePart objects that belong to a certain .dll?
To remove the parts from a particular assembly, you could just remove that AssemblyCatalog by calling AggregateCatalog.Catalogs.Remove. You'll have to design your parts to allow for Recomposition though.
However, this will not help you to delete the plugin assembly. The assembly will still be loaded and you cannot change or delete a .NET assembly while it is loaded. The only way to unload an assembly without stopping the process is by unloading the AppDomain in which it loaded. But if you introduce a separate AppDomain for the plugins then you'll basically have to communicate with those plugins via remoting, etcetera.
It's probably much simpler, safer and effective to just stop the application, delete the plugin assembly, and restart.
edit: actually, there is a way to delete the plugin dll file without stopping the process or unloading the entire appdomain: you can enable shadow copying for the app-domain to instruct .NET to make a copy before loading the assembly. The copy will remain loaded, but at least you can delete or replace the original file.
Could MAF help you here? I am not an expert but I understand that with MAF the addins remain in their own process and can be unloaded at run time. I'd guess that this would not give the ideal performance as you are communicating cross process but if that is not a major issue it might be worth taking a look at.
Related
I have an executable that depends on a library. I also have a "loader" application, which loads the executable into a seperate AppDomain (my "sandbox") and runs it there. The library needs initialization from outside the sandbox, so I need to load the library before I load the executable. This works as expected, but when I now load the executable, the library is loaded another time, and the executable uses the uninitialized copy.
This only occurs if I load the library like this:
Assembly.Load(File.ReadAllBytes(assemblyFile.FullName));
instead of this:
Assembly.LoadFrom(assemblyFile.FullName);
However, using LoadFrom locks the file. I need to be able to delete/write the file, because my application needs to be able to reload the entire sandbox and all assemblies in it.
I also tried registering AppDomain.AssemblyResolve, but it is only called if it does not find the file, which isn't exactly what I want ...
Example log output:
Loading library X <- manual load
Initializing library X
Loading executable Y
Loading library X <- bad, i want to use the loaded X!
So my question is: How do I force .net to use the already loaded assembly instead of loading a duplicate?
From the remarks on Assembly.Load(Byte[]):
Note that this method overload always creates a new Assembly object with its own mapping.
You can't use that overload if you want to reuse your loaded assemblies (without extra work anyway - the Framework won't automatically do it for you here).
You might be able to use the Assembly.Load(string) or just Type.GetType(string) methods here, but I suspect that's still going to end up locking files you want to modify. I'm not really sure how to make sense of modifying those files at run time to be honest though - what's the expected behavior if you delete or change a loaded assembly? Reload it? Have the modified code entered into memory?
You might need to create some kind of assembly caching mechanism of your own. If the assemblies aren't that large and you can afford to keep them in memory, it might be something as simple as a Dictionary<string, Assembly> - and just check if the dictionary has your assembly before loading it, otherwise load it using Assembly.Load(Byte[]) the way you are now.
I ended up modifying my AppDomainSetup of the sandbox:
domainSetup.DisallowApplicationBaseProbing = true;
Now, AssemblyResolve will be called everytime (no autodiscover for assemblies). Now I can just load assemblies from a byte[] and cache them (thanks to #DanField who suggested caching assemblies)
I'm trying to load an assembly into a separate applicaiton domain.
I don't want to create any instances or execute the assembly. So I'm not using CreateInstanceAndUnwrap method. I just want to load the dll file (not creating any types from it etc..)
var cat = new AggregateCatalog();
pluginApplicationDomain = AppDomain.CreateDomain("pluginApplicationDomain", AppDomain.CurrentDomain.Evidence,
appDomainInfo);
cat.Catalogs.Add(new AssemblyCatalog(pluginApplicationDomain.Load(File.ReadAllBytes(plugin))));
and plugin is a relative path to the dll file.
../../Extensions/Plugins\MyAssembly.dll
This throws 'System.IO.FileNotFoundException'
The path is correct, and if I do Assembly.LoadFrom(plugin), it doesn't throw any exceptions, so I'm guessing it's not the path that's incorrect.
Other solutions all used CreateInstanceAndUnwrap, but what if the assembly and its types are black box to me? I know it impements one interface and that's it?
Load dll into another domain exception
would this work? However my method's signature doesn't match that one of CrossAppDomainDelegate
MEF is more about extensibility and it seems like you actually looking for [MAF]1. Check out this SO question. From my personal experience if your plugins going to invoke some native code (interact with hardware for example) AppDomains will not prevent crashes. It's safer to host such plugins in separate processes.
This might look like a duplicate but it isn't, its actually a different question!
The scenario is this: I have a web app that iterates through all the dlls dynamically loaded into the system to access a method from it and get data. These dlls are added by users from the website.
Pseudo code would be something like this:
pulic void GetNews(){
foreach(var i in ListOfDllPaths){
Assembly dll = Assembly.Load( File.ReadAllBytes(i));
SomeInterface if = CreateInstance(dll); //This methods does all the validation and such
if.DoMethod();
}
}
Now the problem is that on each call to GetNews() the same dlls are loaded all over again, having the same dlls loaded multiple times.
I could use Assembly.LoadFrom or LoadFile, to avoid this problem, but then i would lock the file which i don't want to, because i want to be able to delete it.
Another option would be to use a new app domain to load them, call the method and unload them, but then i would also have to load the interface dll in that domain which i really don't know the path to it in the web app. Not to mention moving data from one app domain to the other is a pain and too hard to accomplish.
One third option would be to use a shadow copy, but then i wouldn't be able to delete the shadow copy same results.
Options? Basically what i need is, load the dll, use it, unload it, move on.
If you need to really unload it you have no choice but to use a seperate app domain (see How to unload an assembly from the primary AppDomain? for example)
If you need only load each assembly once, but don't care about unloading old ones you need to manage keeping track of which assemblies are loaded yourself. Any easy way to do this would be to take a hash of each file before you load it. Something Like:
Dictonary<string,bool> loadedAssemblies = new Dictonary<string,bool>();
using(SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
{
var hash = Convert.ToBase64String(sha1.ComputeHash(byteArray));
if(loadedAssemblies.ContainsKey(hash)) continue;
loadedAssemblies[hash] = true;
Assembly dll = Assembly.Load( File.ReadAllBytes(i));
SomeInterface if = CreateInstance(dll); //This methods does all the validation and such
if.DoMethod();
}
If you want to version items another approach would be to add some metadata to your interface.
I am trying to make a C++/MFC application (so called "Update Application") that can extract AssemblyVersion information from other applications written in C#/.NET 3.5.
So I can update my C# program according to the version information I can get.
I am pretty sure this is possible, but I don't know which way would be the best way.
I would like to know some techniques or keywords that I can search on the web.
A direct explanation would be appreciated, too.
Here's roughly how we do something similar in a native C++ app.
Compile with /clr. You can do this project-wide or just on selected C++ files, but as far as I remember there were complications doing it selectively and so we just did it project-wide. Also #include <vcclr.h> wherever appropriate.
You'll need to learn about app domains. The main thing here is that once you've loaded an assembly into a domain, you can't unload the assembly except by unloading the entire domain. Since you want to load an assembly, query its version, and then let it go, you'll probably want to create a temporary domain and load into this.
In our system we have a managed C++ class called ModelLoader to load object models, query their version info, and discard them - just like what you want to do. This class is the pivotal element in our managed/unmanaged marshaling.
The code in the ModelLoader has to execute in the temporary domain, because we want it to load the target assemblies there and then unload the domain However, the main app is already running in the main domain and so it needs to be able to marshal method calls across to the ModelLoader in the temp domain. So ModelLoader inherits System::MarshalByRefObject, which allows the .NET runtime to do all the marshaling magic.
So the basic steps are something like this:
Load the assembly that contains the code for ModelLoader. In our system this is built into our main unmanaged .EXE and so we just use Reflection::Assembly::GetExecutingAssembly() to get a handle to it. If your equivalent of ModelLoader is in a separate assembly then you'll have to load it somehow. But since you probably won't need to unload this assembly you can load it into the main domain.
Create a temporary domain.
Create an instance of your ModelLoader class (obviously it will have a different name in your system) within the temporary domain.
Marshal a handle to that new instance back to your main domain.
Use the marshaled handle from within your main domain to execute code in the temp domain.
Unload the temporary domain.
So, in code:
AppDomain ^domain = AppDomain::CreateDomain(L"temp domain");
Assembly ^assembly = Assembly::GetExecutingAssembly();
ObjectHandle ^handle = domain->CreateInstanceFrom(
assembly->Location,L"ModeLoader");
Object ^o = handle->Unwrap();
ModelLoader ^loader = dynamic_cast<ModelLoader^>(o);
loader->GetAssemblyVersion(...);
AppDomain::Unload(domain);
To save you some head-scratching, the namespaces involved are:
System::AppDomain
System::Reflection::Assembly
System::Runtime::Remoting::ObjectHandle
System::Object
Within your ModelLoader, you'll want to load the target assembly and query its version info. Compared to all the other stuff, this is straightforward:
void ModelLoader::GetAssemblyVersion(const wchar_t *filename, AssemblyName ^name)
{
Assembly ^assembly = Assembly::Load(gcnew String(filename));
name = assembly->GetName();
}
(I made this function up just now, so it might not be quite right.)
Another thing to watch out for is assembly resolution. This is how the assembly loader resolves assembly names to DLL files. This is quite a large field in its own right, so I won't talk any more about it right now. (And in any case I'm no expert.) To get started, just make sure that all the assemblies you want to load are in your main app directory and I think you'll be more or less OK. Then when you have the basic loading working, you can worry about more sophisticated resolution.
I have some C# code (let's call it "script") I am compiling at runtime. It uses an interface in my main program that I use to access its functions. Once compiling is done I have CompilerResults.CompiledAssembly in which case I can CreateInstance(Type).
Once I am done using the script I would like to unload completely. From what I understand, I can only do this if I create a separate app domain:
Loading DLLs into a separate AppDomain
I had some questions specific to my implementation:
If I have multiple scripts to compile and want to unload them independently, do I have to create separate app domains for each?
What app domain names should I use? Would GUIDs be a good idea? Are there any names I should avoid that may conflict?
If the assembly is in a separate app domain, will it have any issues accessing the interface in the main program? I am currently doing ReferencedAssemblies.Add(typeof(Interface).Assembly.Location) before I compile.
Can I use CompilerParameters GenerateInMemory=true, or do I have to save it somewhere?
Answers in order:
Yes, if you want to unload them independently you'll need separate app domains.
Use whatever. It may help you debugging if you can identify it back to script, but that would be more true for the executing Thread.
Not so long as you set the domain setup's base path to your own.
AppDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
No you don't need to save it and it doesn't sound like it would benefit you.