Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm trying to load assemblies into another appdomain (to be able to unload them when not needed) but also I want to be able to inspect types in the loaded assembly and create instances of my types (via Activator). Also I don't want the assemblies to get locked when loaded.

I don't see how this is possible. Things I have tried:

  • Loading Assembly.Load(File.ReadAllBytes(path)) helps me load an assembly without locking it, so it can get deleted/moved. This loads assembly into the current appdomain, so it won't be possible to unload it.

  • Creating another AppDomain and using AppDomain.Load() loads the assembly into a new AppDomain, but it is not possible to check all the types that are in the loaded AppDomain. I can't create anything, unless I know type fully qualified type name, and even then they have to either be Serializable or derive from MarshallByRef. Another AppDomain stuff just works via Proxies so it is harder to create things without having a Contract/Shared Interface, etc.

  • MEF also loads assemblies into the current AppDomain, so it is basically doing the same thing.

  • Mono.Cecil allows type inspection on a loaded assembly, but then I can't create types using TypeReference. Maybe if there was a way to convert TypeReference to Type?

I have checked how ILSpy does this, and to no surpirse it uses Mono.Cecil to load/unload the assembly, but then again, it does not create instances of any types, just does the type inspection which is possible going via Mono.Cecil route.

Now the question is, is this even possible? Am I missing anything?

share|improve this question
add comment

1 Answer

up vote 1 down vote accepted

If you load the assembly into another appdomain then you should of course create instances there and use them there. Other than that, not sure what problem you have run into.

Updates

  1. added code to pick type from list
  2. changed to only pass type names across the domains as strings

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;

public class MainClass : MarshalByRefObject
{
    static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("usage: {0} assembly", Path.GetFileName(Assembly.GetExecutingAssembly().Location));
            return;
        }
        AppDomain other = AppDomain.CreateDomain("other");
        Type myType = typeof(MainClass);
        // create a loader instance in the new domain
        MainClass loader = (MainClass)other.CreateInstanceAndUnwrap(myType.Assembly.FullName, myType.FullName);
        string assemblyName;
        string[] types = loader.LoadAssembly(args[0], out assemblyName);
        Console.WriteLine("Loaded assembly {0}.", assemblyName);
        Console.WriteLine("Types:");
        for(int i = 0; i < types.Length; i += 1)
        {
            Console.WriteLine("[{0}] {1}", i, types[i]);
        }
        Console.Write("Enter index of type to create, -1 to exit: ");
        int create = Int32.Parse(Console.ReadLine());
        if (create < 0)
        {
            return;
        }
        Console.WriteLine("Creating instance of type {0}", types[create]);
        Console.WriteLine("Type of the created instance was: {0}", loader.CreateInstance(assemblyName, types[create]));
    }

    string[] LoadAssembly(string path, out string assemblyName)
    {
        Console.WriteLine("LoadAssembly executing in appdomain {0}", AppDomain.CurrentDomain.FriendlyName);
        Assembly assembly = Assembly.Load(File.ReadAllBytes(path));
        assemblyName = assembly.FullName;
        return Array.ConvertAll<Type, string>(assembly.GetExportedTypes(), x => x.FullName);
    }

    string CreateInstance(string assemblyName, string typeName)
    {
        Console.WriteLine("CreateInstance executing in appdomain {0}", AppDomain.CurrentDomain.FriendlyName);
        object instance = Activator.CreateInstance(assemblyName, typeName).Unwrap();
        // do something useful with the instance here
        return instance.GetType().FullName;
    }

    public override object InitializeLifetimeService()
    {
        return null;
    }
}

Sample output for log4net.dll:

$ mono main.exe log4net.dll
LoadAssembly executing in appdomain other
Loaded assembly log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=a5715cc6d5c3540b.
Types:
[0] log4net.Core.SecurityContext
[1] log4net.Core.LoggerWrapperImpl
[2] log4net.Core.LogImpl
[3] log4net.Core.DefaultRepositorySelector
...
[160] log4net.Appender.AspNetTraceAppender
[161] log4net.Appender.FileAppender
...
[197] log4net.Repository.LoggerRepositoryConfigurationResetEventHandler
[198] log4net.Repository.LoggerRepositoryConfigurationChangedEventHandler
Enter index of type to create, -1 to exit: 161
Creating instance of type log4net.Appender.FileAppender
CreateInstance executing in appdomain other
Type of the created instance was: log4net.Appender.FileAppender
share|improve this answer
    
Pointing it to a 3rd party assembly, I get a FileNotFoundException (even thought the file IS there). The problem is that I don't want to load the assembly that contains any specific type, but want to load them up, and inspect the available types and upon some criteria create one/some of them. The loaded assemblies can be anything (take your own pick from any 3rd party assemblies). Also the assembly that is being loaded can be anywhere on the hard drive, not in any application specific folder. –  Hadi Eskandari Dec 3 '12 at 23:56
    
Works fine here, with anything I have thrown at it. –  Jester Dec 3 '12 at 23:59
    
Can't say I can. I can see the assembly being loaded into the other AppDomain and the LoadAssembly method is being executed finding all the types, but the types are not returned back to the main AppDomain and I get a FileNotFoundException, the reason being the assembly only loaded in the second AppDomain and does not exist in the first. –  Hadi Eskandari Dec 4 '12 at 0:13
    
It's only the Type that's marshalled across the domains and for that you don't need the assembly loaded. I have updated the answer showing it works here. You might try returning type names in a string array then, that should certainly work. –  Jester Dec 4 '12 at 0:17
    
Thanks Jester, marshalling type names in strings made it works, it is just that I can not create instances of objects and passing them back to the first AppDomain (which also sounds logical since the Assembly is only loaded in second AppDomain). I'll accept your solution though. –  Hadi Eskandari Dec 4 '12 at 1:02
show 2 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.