Search code examples
c#dllactivator

Calling Activator.Createinstance(...) with DLL that has static variable


I have a DLL, (that I cannot change!), that has static variable. I want to call Activator.CreateInstance(...) but create a brand new Instance rather than share a previously loaded Instance.

// ClassLibrary.dll
namespace ClassLibrary
{
  public class Foo
  {
    private static int Number { get; set; } // This is a static member...
    public Foo()
    {
      Number++;
    }

    public int Bar()
    {
      return Number;
    }
  }
}

// ConsoleApplication.exe
static int InvokeMethod()
{
  var dir = Directory.GetCurrentDirectory();
  var path = Path.Combine(dir, "ClassLibrary.dll");

  var asm = Assembly.LoadFile(path);
  var type = asm.GetType("ClassLibrary.Foo");
  var instance = Activator.CreateInstance(type, new object[] { });
  var method = type.GetMethod("Bar");
  return (int)method.Invoke(instance, null);
}

private static void Main(string[] args)
{
  var val1 = InvokeMethod(); // 1
  var val2 = InvokeMethod(); // 2! I want it to be 1
}

I have tried the same with AppDomain.Load(), but the static value is still being shared.

Any suggestions on how I can load a brand new instance rather than sharing the previously loaded dll.

Edit 1: Here is the code of the AppDomain load, but the result is the same.

static int InvokeMethodDomain()
{
  var dir = Directory.GetCurrentDirectory();
  var path = Path.Combine(dir, "ClassLibrary.dll");

  var dom = AppDomain.CreateDomain(Guid.NewGuid().ToString());
  try
  {
    var asn = new AssemblyName {CodeBase = path};
    var asm = dom.Load(asn);
    var type = asm.GetType("ClassLibrary.Foo");
    var instance = Activator.CreateInstance(type, new object[] { });
    var method = type.GetMethod("Bar");
    return (int) method.Invoke(instance, null);
  }
  finally
  {
    AppDomain.Unload(dom);
  }
}

Solution

  • As you already figured out, you need to load assembly into new app domain to get fresh static values. Sample code:

    // inherit from MarshalByRefObject to enable cross domain communication
    public class AppDomainProxy : MarshalByRefObject {
        public int InvokeMethod() {
            var dir = AppDomain.CurrentDomain.BaseDirectory;
            var path = Path.Combine(dir, "ClassLibrary.dll");
            var asm = Assembly.LoadFile(path);
            var type = asm.GetType("ClassLibrary.Foo");
            var instance = Activator.CreateInstance(type, new object[] { });
            var method = type.GetMethod("Bar");
            return (int) method.Invoke(instance, null);
        }
    }
    

    And then:

    static int InvokeMethod()
    {            
        var appDomain = AppDomain.CreateDomain("Domain", AppDomain.CurrentDomain.Evidence, new AppDomainSetup {                
            ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
        });
        try {
            // first create our proxy
            var instance = (AppDomainProxy) appDomain.CreateInstanceAndUnwrap(
                typeof(AppDomainProxy).Assembly.FullName,
                typeof(AppDomainProxy).FullName);
            // this will run in new app domain
            return instance.InvokeMethod();
        }
        finally {
            AppDomain.Unload(appDomain);
        }
    }