shadow copying of assemblies

  • Thread starter Thread starter Mr 071
  • Start date Start date
M

Mr 071

I am trying to use shadow copying provided by the framework, but I am not
able to get it to work. More specifically, after a new app domain is created
and the assembly loaded in it, it is still not possible to replace the
original DLL although it has been shadow copied to the cache folder. I need
to be able to replace plug-in DLLs while the server is running. I guess that
is what shadow copying exists for, but the DLL is locked until the app
domain has been unloaded (which fully beats the purpose of this "exercise").
Furthermore, if I comment out the line that adds the 'bin' folder to current
domain's list of private paths the assembly is not found at all, although
the new domain I am trying to load it in has its privatebinpath set to
include the 'bin' folder. Weird.

Someone please enlighten me. Thanks.

Code:
//----------------------------------------------------
cut --------------------------------------------
using System;
using System.IO;
using System.Reflection;
using System.Security.Policy;

namespace Test
{
class shadowCopying
{
public static void Main()
{
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase =
"d:\\development\\c#\\test\\shadowCopying\\";
setup.ApplicationName = "ShadowCopy";

setup.PrivateBinPath = setup.ApplicationBase + "bin";
setup.ShadowCopyDirectories = setup.ApplicationBase + "bin";
setup.ShadowCopyFiles = "true";

AppDomain.CurrentDomain.AppendPrivatePath("bin");

Evidence evidence = new
Evidence(AppDomain.CurrentDomain.Evidence);
AppDomain domain = AppDomain.CreateDomain("DynamicLoad",
evidence, setup);

string cachePath = domain.BaseDirectory + "cache";

DirectoryInfo dir = new DirectoryInfo(cachePath);
if(dir.Exists)
dir.Delete(true);

domain.SetCachePath(cachePath);

Assembly assembly = domain.Load("dynamicLoad");

Console.WriteLine("Waiting for any key....");
Console.ReadLine();
}
}
}
//---------------------------------------------
cut --------------------------------------------------
 
Make sure you are loading the dll in the context of the new appdomain and
not in the default appdomain. One way to check is to write a routine that
enumerates all the loaded assemblies in the current appdomain and writes
them to the trace output; invoke this from both the default appdomain and
the second appdomain and see which appdomain your plugin is loaded in.
 
Thanks for the post.

Not sure what you could mean by "context of the new domain" other than to
load the assembly into the newly created domain. As you can see from the
code below I load the assembly into new domain:

Assembly assembly = domain.Load("dynamicLoad");

where 'domain' is the new app domain I just created.

My guess is that something else happens behind the scene regarding the
default domain. As I already tried to explain if I remove the following line
from the code

AppDomain.CurrentDomain.AppendPrivatePath("bin");

the load fails with "file not found" although the new domain is "aware" of
the bin folder (i.e. setup.PrivateBinPath = setup.ApplicationBase + "bin"; )

That's the confusing part. Thanks.

Mr071 , MCSD.NET, MCAD.NET
 
The problem with the line of code you provided is that the code is being
executed in the context of the default appdomain, and even if it loads the
assembly into the newly created appdomain, when that method returns a
reference to that assembly to the default (the calling) appdomain it causes
the assembly to be loaded into both appdomains.

What you need to do to completely isolate the assembly from the default
appdomain is to create a remote object derived from MarshalByRefObject in
the new appdomain and invoke a remoted method call in that object that will
load the assembly; do not return a reference to that assembly to the calling
appdomain.

Here's some simple code that illustrates this

namespace MyTest
{
public class AsmLoader : MarshalByRefObject
{
public bool LoadFrom(string fullPath)
{
Assembly a = Assembly.LoadFrom(fullPath);
return (a != null); // you may need to save this reference away in a
hash table
}
}
}

and in the calling code
void main()
{
AppDomainSetup ads = new AppDomainSetup();
// ... setup ads
AppDomain ad = AppDomain.CreateDomain(...);
AsmLoader al =
(AsmLoader)ad.CreateInstanceFromAndUnwrap("asmName.exe","MyTest.AsmLoader");
al.LoadFrom("PathToDll"); // this is a remoted method call
}
obviously there's more to do but this illustrates the idea. You keep all
references to the loaded assembly completely within the context of the
second appdomain. Any references to that assembly in the default appdomain
will cause it to get loaded into both appdomains.
 
Thanks a lot! I really appreciate the insight. Getting down to implementing
it right away.
:-)
 
Back
Top