C# Avoid file lock when Assembly.Load - c#

My situation is;
I am designing a plugin application, based on dynamically loading plugin assemblies more than one user may run the application that's hosted on a server machine. Application is auto-updating plugin assemblies from my live update server on its startup. So plugin files (and its satellite dlls should not be locked on file system.
byte[] assemblyBytes = File.ReadAllBytes("asm-path");
var assembly = Assembly.Load(assemblyBytes);
as expected not locking the dll file. but what if the dll I am loading has static reference dlls itself? they are locked on file system now.
to name files, lets say;
APP.Exe is my base application;
PL1.dll is my first plugin, APP.exe loads it in a new Appdomain
PL2.dll is my second plugin, APP.exe loads it in a new Appdomain
PL1_S.dll is a static reference dll for PL1.dll plugin, loaded in PL1 AppDomain
PL_COMMON is a static reference dll for both PL1 and PL2 plugins, loaded by PL1 and PL2 AppDomains
PL1_S and PL_COMMON also should not be locked in file like PL1 and PL2 assemblies
Any idea on how to solve that?

There is functionality already built in to do this, as used by e.g. ASP.Net. See Shadow Copying Assemblies:
Shadow copying enables assemblies that are used in an application domain to be updated without unloading the application domain. This is particularly useful for applications that must be available continuously, such as ASP.NET sites.
So just turn that on via the AppDomainSetup.ShadowCopyFiles when creating your AppDomains

Related

Why is an AppDomain relevant to a plugin manager implemented with MEF

I wrote a simple plugin manager class with MEF and FileSystemWatcher for refreshing plugins automatically, but I hear some say that to be able to add and remove assemblies (plugins) on the fly we need to use an AppDomain also.
Can anyone guide me as to when we need to use AppDomain and MEF together (especially for my plugin manager scenario)?
What is the relationship between them ?
In short, an AppDomain is required when you want to overwrite .dlls that are in use by an application, and that AppDomain must specify the ShadowCopyFiles = "true".
Shadow copying files means that the AppDomain will copy the .dlls to a temporary directory and load them from that temporary directory so the original .dlls can be overwritten.
Unfortunately the assemblies which are loaded into any AppDomain cannot be unloaded unless the AppDomain that contains them is unloaded.
With that in mind, refreshing a plugin is difficult because you would have to A) unload the entire AppDomain which necessarily unloads all other .dlls in that AppDomain, or B) allow a new version of the same .dll to be loaded increasing the memory footprint of your application. The second option also requires your plugin .dlls to be strong named and a different version number in order for MEF to recognise a difference and load the new .dll.

Hosted CLR: how to set base directory of default AppDomain?

If I host CLR in my C++ executable, is there any way to set base directory of default AppDomain to something other than location of the executable?
Here's why I need it. I have a rather complex application that loads .NET plugins using a plugin loader executable, PluginBox.exe. The plugins are located outside of the main application folder. We run one instance of PluginBox.exe per plugin.
Currently PluginBox.exe is written in C++. I want to convert it to a managed app. PluginBox uses unmanaged CLR hosting (ICLRMetaHost, ICLRRuntimeInfo, etc.), and locates plugin assemblies by implementing IHostAssemblyStore interface. There is only one AppDomain, and its base directory is the application directory. When searching for assemblies, CLR invokes the assembly store before looking at the application base directory. So, if the plugin and the main app contain an assembly with the same name, the assembly store can load the plugin-specific version.
As a first step towards making PluginBox.exe managed, I would like to get rid of the assembly store and replace it with an assembly resolver. The trouble is, unlike assembly store, assembly resolver is called after the application base directory has been considered. So, if the plugin and the main app contain an assembly with the same name, main app's assembly wins. This disrupts plugin execution.
I would like to switch base directory of the hosted CLR to where the plugin is located. So far, I found only two ways to do that, both of them unacceptabe: move PluginBox.exe to the plugin directory, or create a second AppDomain, which is problematic for a variety of internal reasons. This is a huge application with a lot of history, and any drastic moves are bound to cause problems.
Any thoughts and ideas are appreciated.
PS. Current CLR version is 4.0.

Dynamic type instantiation without locking dll

I have a COM+ Application composed of several serviced components.
One of them instantiates on demand a type from a dll through Activator.CreateInstance(pluginType, args);. The type is not a serviced component itself, it contains just the implementation of certain interface that the component knows about.
I don't have problems with the instantiation itself, the problem is that the dll is getting locked by the dllhost.exe, and I would like, if possible, to be able to replace it without shutting down the COM+ Application.
Is it possible somehow to unlock the dll without shutting down the COM+ App?
Or from another point of view, is there a way I could programmatically unload the dll to unlock it?
NOTE: The dll is located in the COM+ application root directory with rest of the dlls.
Loaded modules are referenced counted in windows. In windows we have loadLibrary and freelibrary. If freelibrary is called and the reference count is zero then the dll will be unloaded and free to replace, delete, etc.
In .NET though once an assembly is loaded into an appDomain it is there until the appdomain dies (unless reflection emit collectable assembly). If you were to spawn another appdomain and do the create instance here when the new appdomain dies the assembly will be unloaded.
Asp.net does something called shadow caching. Asp.net you can replace the dlls at anytime without having to kill the worker process. They do this by not loading the dlls in the bin folder but instead they copy them to a seperate location and load them from there in a seperate appdomain. They watch the bin folder, when a change happens they kill the appdomain, copy the dlls to the temp folder, and create a new appdomain.
Shadow Copying Assemblies
http://msdn.microsoft.com/en-us/library/ms404279.aspx
This is not a specific problem for COM+, it is universal on Windows and fundamental to the way it works. Loading code from an executable file (exe or dll) is done by Windows creating a memory-mapped file for the executable file. Code is only read from the file when a page fault requires actually reading the file and mapping the code into RAM. This will happen repeatedly when other processes compete for RAM and the code gets unmapped.
The MMF puts a lock on the file. Required so that the file cannot be modified while it has code mapped into RAM.
There's no workaround for this, the process that has the DLL loaded must terminate or must cooperate and unload the DLL before the lock is released. At best you can rename the file while it is in use, that doesn't otherwise affect the process.

Delay loading in c#?

Is there such a thing as delay loading a dll in C#?
I know this is possible to do in C++, but what about managed code?
.NET does that automatically, everything is loaded on demand by default.
This article explains in detail how it works in .NET. Summary of key points:
There are a number of different ways that assemblies are loaded in
.NET. When you create a typical project, assemblies usually come from:
The Assembly reference list of the top level 'executable' project
The Assembly references of referenced projects
Dynamically loaded assemblies, using runtime loading via AppDomain or Reflection loading
and
.NET automatically loads mscorlib (most of the System namespace) as part of the .NET runtime hosting process that hoists up
the .NET runtime in EXE apps, or some other kind of runtime hosting
environment (runtime hosting in servers like IIS, SQL Server or COM
Interop).
and
Dependent Assembly References are not pre-loaded when an application starts (by default)
Dependent Assemblies that are not referenced by executing code are never loaded
Dependent Assemblies are just in time loaded when first referenced in code
Once Assemblies are loaded they can never be unloaded, unless the AppDomain that hosts them is unloaded.
Yes it is. You don't include the DLL as a reference in your project and where you want to load/use it, you call the Assembly.LoadFile method.
This blog post does a pretty good job with code to describe how to do it.

Reload a reference on the fly (C# .NET)

Ok, heres the rundown...
I have a service that is running and it references a dll that is changed a lot. This service is hopefully going to have multiple clients hitting it at once so it would be inefficient to shut it down and recomplile/reload with a new reference. I was wondering if there was anyway the program could pretty much auto-detect a dll with a later version and just drop the old one and load the new one without having to be shut down.
This can be difficult to achieve in a .Net application. Once a DLL is loaded into a particular AppDomain there is no way to unload the DLL from the AppDomain. The only way to get the DLL out of the process is to unload the AppDomain itself.
You could achieve what you're trying to accomplish by having the DLL loaded into a secondary AppDomain, and restarting that AppDomain with the new DLL when you detect a change. This also involves using some advanced shadow copy features though to allow the DLL to be deleted while being used by the process.
The only way to do this is move the code that uses the assembly to its own AppDomain. You can shutdown the AppDomain and restart it with the new assembly.
See this question: How to reload an assembly in C# for a .NET Application Domain?
The Managed Extensibility Framework will allow you to do this.

Categories

Resources