6

I have created a test library

public class Test
{
    public int Add(int val1, int val2)
    {
        return val1 + val2;
    }
}

And a project to call it :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;

namespace Loader
{
    class Program
    {
        static void Main(string[] args)
        {
            String path = @"...Lib.dll"; // path to lib
            var name = Path.GetFileName(path);

            AppDomainSetup setup = new AppDomainSetup  
            {
                ApplicationBase = @"...", // directory where Lib.dll is 
                ShadowCopyFiles = "true",
                ShadowCopyDirectories = @"..."// directory where Lib.dll is 
            };  

            var appdomain = AppDomain.CreateDomain("Loader." + name, null, setup);
            Assembly ass = Assembly.LoadFile(path); // <--- I think here is the problem, here where assembly is locked
            Assembly assembly = appdomain.Load(ass.FullName);

            dynamic a = assembly.CreateInstance("Lib.Test");

            Console.WriteLine(a.Add(1, 5));
        }
    }
}

Please help to find what I have done wrong ? Why my assembly is locked ?

EDIT : with hardcoded assembly name :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;

namespace Loader
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomainSetup ads = new AppDomainSetup();

            String fullPath = @"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Lib.dll";

            ads.ShadowCopyFiles = "true";
            ads.ApplicationName = "AppName";
            ads.ShadowCopyDirectories = Path.GetDirectoryName(fullPath);
            //ads.ApplicationBase = Path.GetDirectoryName(fullPath);
            //ads.PrivateBinPath = Path.GetDirectoryName(fullPath);
            ads.CachePath = @"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Cache\";

            AppDomain ad = AppDomain.CreateDomain("myName" + ads.ApplicationName, null, ads);

            ad.AssemblyResolve += new ResolveEventHandler( ad_AssemblyResolve );

            Console.WriteLine(ad.ShadowCopyFiles);
            Console.WriteLine(ad.SetupInformation.ShadowCopyDirectories);

            try
            {
                //Assembly assembly = ad.Load(AssemblyName.GetAssemblyName(fullPath));
                //dynamic obj = ad.CreateInstanceAndUnwrap(assembly.GetName().Name, "Lib.Test");

                dynamic obj = ad.CreateInstanceAndUnwrap("Lib", "Lib.Test");

                Console.WriteLine(obj.Add(1, 7));

                Console.ReadKey();

                Console.WriteLine(obj.Add(1, 90));
            }
            catch( Exception e)
            {
                Console.WriteLine( e.Message );
            }
        }

        static Assembly ad_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return Assembly.LoadFile(@"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Lib.dll");
        }
    }
}

It is interesting that cached assebly is also locked.

EDIT 2:

Here is the line of code to block the assembly

Console.WriteLine(obj.Add(1, 7));

So once a method in Lib.Test is accessed the assenbly is blocked.

What can be the solution ?

NDeveloper
  • 1,837
  • 4
  • 20
  • 34
  • Please post the exact error you are getting. – Scott Chamberlain Jun 27 '11 at 18:38
  • The exact problem as I have said is that Lib.dll assembly is locked. For example I can't replce it once application is running. – NDeveloper Jun 27 '11 at 18:58
  • You need to use an interface in a 3rd assembly to prevent your code from loading the constructed type into the primary appdomain. Don't use LoadFile. – Hans Passant Jun 27 '11 at 20:26
  • @Hans ad_AssemblyResolve is never called, so there LoadFile is never called. What must be done in this case ? – NDeveloper Jun 27 '11 at 20:33
  • @Hans, I would've thought that dynamic in this case could reflect over the transparent proxy type without having to load the wrapped type's assembly into the local AppDomain, but it was probably not considered to be an important requirement for 'dynamic'. The transparent proxy lies when GetType() is called and says it's the wrapped type (being transparent and all), so a straightforward implementation based on Reflection will load the type. – Dan Bryant Jun 27 '11 at 23:10
  • @NDeveloper: Check this out. http://stackoverflow.com/questions/13003806/activator-createinstance-causing-nunit-to-lock-which-prevents-visual-studio-buil/13004183 – Valamas Oct 22 '12 at 02:20

2 Answers2

1

You're loading the assembly into your main AppDomain when you run Assembly.LoadFile(), which locks the file. If you want to load it directly into your AppDomain, you need to use AppDomain.Load() directly, allowing the AppDomain to resolve it based on the parameters you specified. Note that, since the assembly is not loaded in your main AppDomain, that type is inaccessible from your Main method. Your Lib.Test will need to derive from MarshalByRefObject so that the marshalling system can create a proxy (which further gets wrapped into a dynamic proxy when you assign it to dynamic.)


EDIT:

Upon further thought, I suspect the issue here is that, in order to call the method on the dynamic proxy, it has to Reflect on the object (which is itself a transparent proxy for the instance in your other AppDomain.) If it ends up reflecting over the original class type (and not the 'hidden' transparent proxy type), this will cause it to load a copy of the assembly in the local AppDomain (thus locking it.) This is one reason why, when using AppDomain isolation, you usually cast the object to an interface defined in a separate assembly that both the 'main' application and the hosted AppDomain can reference. They will each get their own copy of the interface assembly loaded, but now the 'implementation' assembly is isolated.

Despertar
  • 21,627
  • 11
  • 81
  • 79
Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • Please see the edited code... there is no Assembly.LoadFile. Also I have derived Lib.Test from MarshalByRefObject and set [Serializable], but still the same .... I can't imagine what I need to do else. – NDeveloper Jun 27 '11 at 20:02
  • @NDeveloper, in this new case, you're forcing the AppDomain to resolve the Assembly by calling LoadFile, which bypasses the shadow copying and loads the file directly. If you want it to load the file using the shadow copying mechanism you need to pass the FullName of the assembly to CreateInstanceAndUnwrap and allow the AppDomain to do its own file resolution. – Dan Bryant Jun 27 '11 at 20:11
  • But ad_AssemblyResolve never called, so everything as you have described. – NDeveloper Jun 27 '11 at 20:21
  • And even I have removed that part, but still the issue in place. – NDeveloper Jun 27 '11 at 20:22
0

You have two options, you need to look up the assembly name before run time and have it hard-coded, or if you do not know the before run time you need to write a second library that you do know the FullName and load that in the AppDomain. The second library will be one class that has one function

public Assembly GetAssemblyForOtherAppDomain(string path) 
{ 
    return Assembly.LoadFile(path) 
}

This should load your target in the new AppDomain without locking it as should open it with a shadow copy.

EDIT: as to why your method did not work, you where right about that load locking it and once a assembly is loaded in to a AppDomain it can not be unloaded.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431