.net automatic assembly resolve loads duplicate assembly - c#

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)

Related

Event / detect when your DLL gets loaded?

Imagine there is a MainLibrary.dll that's dynamically loading other helper DLLs. This is a design change for us and for this to work, we need to bootstrap the process by registering the AssemblyResolve callback.
This is simple to do when there is a single point of entry (eg: Main()) but in our case, MainLibrary.dll has multiple entry points. We'd want to avoid introducing an explicit Init() call since that would break the existing client-library API ("You must now call Init() before calling DoSomeWork()").
So is there a way for a library to know when it's been loaded or about to be used? That way we can latch onto that to perform our registration there, maintaining the client-library interface as before this dynamic loading change.
There is no built-in way for assembly to know when it is loaded (unlike in native code where DllMain get called on load).
You will need to reorganize your code to know when to load other binaries if they can't be loaded with default load rules.
Cheap way is to provide (maybe even automatically generated) layer of interface/objects wrapper implementations that will simply enforce loading of dependencies before calling into your actual API.

Assembly.Load(byte[]) load only once

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.

Making a C++/MFC application to extract AssemblyInfo from other applications?

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.

Load an assembly without locking file AND keep the right Binding Context

I've comp up against a formidable conundrum. Here's my situation:
I'm building an application with a plugin framework. There's a base plugin class that all plugins must extend. Within the same same assembly I have a helper class that will serialize and deserialize classes. It's a generic class and it's used all over the place. The structure is like this:
MyApp.dll
|_ App.cs
|_ HelperCollection.cs
|_ PluginBase.cs
MyPlugin.dll
|_MyPlugin.cs (this extends PluginBase)
|_Foo.cs
The Problem
My problem is the assembly loading and locking files. A requirement of the app is that Plugins can be overwritten at any time. If so, they need to be reloaded. It seems the best way to load an assembly so that it's not locked (that is, I can overwrite it or blow away while the app is still running) is this:
byte[] readAllBytes = File.ReadAllBytes("MyPlugin.dll");
Assembly assembly = Assembly.Load(readAllBytes);
Loading the plugin assembly works just fine, no problems there. I get an exception when, from within MyPlugin.cs, which is in the plugin assembly, I tried to use the HelperCollection to do deserialization. An example could be something like this:
// HelperCollection uses XmlSerializer under the covers
List<Foo> settingCollection = HelperCollection<Foo>.Deserialize("mysettings.xml");
It's blowing up and throwing an InvalidCastException saying that it's "Unable to cast object of type 'List[Foo]' to 'List[Foo]'". After much research I finally found why. It's getting loaded in the LoadNeither binding context.
When Foo is loaded (from MyPlugin.dll) it's in the LoadNeither binding context while the assembly containing the type for the type conversion (in my case, MyApp.dll) is loaded in Default context. So even though they have the same name they are not considered the same type. It's doing this because I'm using Assembly.Load(byte[]).
Questions
How can I get around this? How can I,
Load an assembly and not lock the file, and
Provide the right binding context so I can cast objects which are located in the loaded assembly.
Sorry for the wall of text, just wanted to get all the relevant info out there.
Have you tried shadow copying?
When you're using shadow copying, .NET copies the assembly to a temp directory and loads it from there (so it's the temp file that's locked, not the original assembly) -- but all the binding rules are based on the original file location.
Just an idea from the top of my head:
What about having a Plugin directory where the users may drop their Plugins into. Then in your application, you monitor that folder and copy the assemblies to the bin or whatever.
Then you load them the "normal" way, which may lock them.
When the user drops new plugins, you kill the plugin app domain and reload them.
Just an idea.

How to remove MEF plugins at runtime?

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.

Categories