views:

844

answers:

2

Hey there

I need to execute a method in an assembly loaded during runtime. Now I want to unload those loaded assemblies after the method call. I know that I need a new AppDomain so I can unload the libraries. But here, the problem arises.

The assemblies going to load are plugins in my plugin framework. They have no entry point at all. All I know is that they contain some types which implement a given interface. The old, non-AppDomain-code looks like this (slightly shortened):

try
{
    string path = Path.GetFullPath("C:\library.dll");
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    Assembly asm = Assembly.LoadFrom(path);
    Type[] types = asm.GetExportedTypes();
    foreach (Type t in types)
    {
        if ((t.GetInterface("IStarter") != null) && !t.IsAbstract)
        {
            object tempObj = Activator.CreateInstance(t);
            MethodInfo info = t.GetMethod("GetParameters");
            if (info != null)
            {
                return info.Invoke(tempObj, null) as string;
            }
        }
    }
}
catch (Exception ex)
{
    MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name.StartsWith("MyProject.View,"))
    {
        string path = Path.GetFullPath("C:\view.dll"));
        return Assembly.LoadFrom(path);
    }
    return null;
}

Now I want them to load in an own AppDomain:

try
{
    string path = Path.GetFullPath("C:\library.dll");
    AppDomain domain = AppDomain.CreateDomain("TempDomain");
    domain.AssemblyResolve += CurrentDomain_AssemblyResolve;  // 1. Exception here!!
    domain.ExecuteAssembly(path);  // 2. Exception here!!
    domain.CreateInstanceFrom(...);  // 3. I have NO clue, how the type is named.
    domain.Load(...);  // 4. I have NO clue, how the assembly is named.
    domain.DoCallBack(...); // 5. Exception here!!
    // ...
}
catch (Exception ex)
{
    MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

As you can see, I have put in 5 cases.

  1. If I set the event handler, I get an exception that the assembly (it's an management console (mmc.exe) SnapIn. could not be found/loaded.

  2. ExecuteAssembly does not find an entry point (well, there is none).

  3. I have no clue how the type is named. How to load by interface?

  4. Similar to 3. How to get the name of an assembly?

  5. Same error as in 1.

I think the problem could be the managment console somehow or I have just no clue what I'm doing wrong. Any help is appreciated.

UPDATE 1

I have now tried using the posted proxy-solution.

AppDomain domain = AppDomain.CreateDomain("TempDomain");
InstanceProxy proxy = domain.CreateInstanceAndUnwrap(Assembly.GetAssembly(
    typeof(InstanceProxy)).FullName, typeof(InstanceProxy).ToString()) as InstanceProxy;
if (proxy != null)
{
    proxy.LoadAssembly(path);
}
AppDomain.Unload(domain);

public class InstanceProxy : MarshalByRefObject
{
    public void LoadAssembly(string path)
    {
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        Assembly asm = Assembly.LoadFrom(path);
        Type[] types = asm.GetExportedTypes();
        // ...see above...
    }
}

This does not work either. When trying to create the proxy object, I get an exception:

Could not load file or assembly 'MyProject.SnapIn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

The file in the error message is the on loaded into mmc (the SnapIn). Any idea how to fix this error? AppDomain.AssemblyResolve is not called (neither in the old or new domain).

UPDATE 2

I have now tried the solution with the AppDomainSetup. Now, the exception has changed to:

Could not load file or assembly 'file:///C:/Development/MyProject/bin/SnapIn/MyProject.SnapIn.DLL' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)

Any idea?

+1  A: 

Take a look into this previous answer: How to load an assembly into different AppDomain on Windows Mobile (.NET CF) ?. That answer creates a proxy class which runs into new AppDomain context so, there, you can have full control of your initialization.

You could to create a Start() method into ServiceApplicationProxy class and just call it normally from your hoster with a proxy.Start().

Rubens Farias
Thanks for your answer. I've tried this, but I'm still getting errors. See my update in the original question.
Scoregraphic
+1  A: 

Try this:

namespace SeperateAppDomainTest
{
    class Program
    {
        static void Main(string[] args)
        {
            LoadAssembly();
        }

        public static void LoadAssembly()
        {
            string pathToDll = Assembly.GetExecutingAssembly().CodeBase;
            AppDomainSetup domainSetup = new AppDomainSetup { PrivateBinPath = pathToDll };
            var newDomain = AppDomain.CreateDomain("FooBar", null, domainSetup);
            ProxyClass c = (ProxyClass)(newDomain.CreateInstanceFromAndUnwrap(pathToDll, typeof(ProxyClass).FullName));
            Console.WriteLine(c == null);

            Console.ReadKey(true);
        }
    }

    public class ProxyClass : MarshalByRefObject { }
BFree
Thanks for the hint with the AppDomainSetup, but it does not solve the issue. See my updated question
Scoregraphic