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.
Related
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.
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
How can I unload a DLL from memory that belongs to another process, or the process had ended but the DLL is still loaded into memory?
I know how to find an existing process and even list all the loaded DLL's for that, but what I can't seem to get is how to find loaded DLL's and unload them from memory.
Everything I've read talks about AppDomains, but I'm not sure if this case would apply or not to that method since I want to close DLL's that aren't called by my app.
Any help would be appreciated.
I don't think you can unload a single assembly. But you can unload an AppDomain that contains the assembly.
A shared DLL loaded in domain neutral manner cannot be unloaded unless the Process is exited. Domain specific DLLs are unloaded alongwith Domain. There is however no way to unload individual DLLs even if the DLL has been loaded for reflection only.
Our .NET 3.5 C# application creates multiple appdomains. Each appdomain loads the same unmanaged 3rd party dll. This dll reads a configuration file upon initialization. If the configuration changes during runtime, the dll must be unloaded and loaded again. This dll is not in our scope to rewrite correctly.
Does each appdomain have access to a separtate copy of this unmanaged dll, or does Windows keep one copy of the dll and maintain a usage count? If the latter is is the case, how do we get each instance of the unmanaged dll to reflect its unique configuration?
I think unmanaged dlls are loaded only once per process by the OS, so every app-domain will have the same loaded instance. To unload a dll, use the FreeLibrary function. However, since multiple app-domains are likely to have loaded the dll, there is no guarantee that FreeLibrary from one app-domain will actually free/unload the dll.
As BillW says, this seems like a design nightmare to me too!
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.