0

I am working on a project where allowing 3rd-party plugins is required. I have worked with plugins before and I never had a problem.

I'm sure my problem is because WPF doesn't like me using Assembly.LoadFile(file) & Activator.CreateInstance(t)!

The error I encounter is:

The component 'Servus.Forms.MainWindow' does not have a resource identified by the URI '/Servus;component/forms/mainwindow.xaml'.

which shows in my MainForm constructor at: InitializeComponent();

If I load the plugins after loading the MainForm it loads without issues, however when opening any other forms(there are many in my application) I experience the same issue as about but with the relevant error for that particular form.

I have also tried to load the plugins in there own AppDomain like this:

PluginDomain temp = new PluginDomain();
PluginBase tempPlug = temp.GetPlugin(file);

With the following classes:

public class PluginDomain
{
    public AppDomain CurrentDomain { get; set; }
    public ServusAssemblyLoader CurrentAssemblyLoader { get; set; }

    private readonly Random _rand = new Random();

    public PluginDomain()
    {
    }


    public PluginBase GetPlugin(string assemblyName)
    {
        try
        {
            string appBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            var ads = new AppDomainSetup { ApplicationBase = appBase, PrivateBinPath = appBase, ShadowCopyFiles = "true" };
            CurrentDomain = AppDomain.CreateDomain("ServusDomain_Plugin_" + _rand.Next(0, 100000), null, ads);

            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
            CurrentAssemblyLoader = (ServusAssemblyLoader)
            CurrentDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(ServusAssemblyLoader).FullName);

            AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;

            return CurrentAssemblyLoader.Load(assemblyName);
        }
        catch (Exception e)
        {
            CConsole.WriteLine("Error: " + e.Message);
        }
        finally
        {
            CurrentAssemblyLoader = null;
            AppDomain.Unload(CurrentDomain);
        }

        return null;
    }

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        string[] parts = args.Name.Split(',');
        string file = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + parts[0].Trim() + ".dll";

        return Assembly.LoadFrom(file);
    }
}

public class ServusAssemblyLoader : MarshalByRefObject, IAssemblyLoader
{
    public PluginBase Load(string file)
    {
        Assembly asm = Assembly.LoadFrom(file);

        foreach (Type t in asm.GetTypes())
        {
            if (t.IsSubclassOf(typeof(PluginBase)))
            {
                return (PluginBase)Activator.CreateInstance(t);
            }
        }

        return null;
    }
}

public interface IAssemblyLoader
{
    PluginBase Load(string file);
}

This returns an TransparentProxy object like this:

{System.Runtime.Remoting.Proxies.__TransparentProxy}

However I am unsure how to use this as I was expecting it to return a PluginBase Object.

I have read that many people have also have this issue, they have answers that say to use a new AppDomain, but as you can see this doesn't help me right now.

I hope I have provided you enough information, can anyone help?

Ryuk
  • 95
  • 1
  • 5
  • 16
  • Possible dupe?http://stackoverflow.com/questions/4993098/wpf-control-throwing-resource-identified-by-the-uri-missing-exception – ywm Feb 21 '13 at 17:33
  • No this isn't a dupe. I am not using controls except what is provided those provided by .Net & WPF. My problem is that it wont load my form that is in my "plugin loader" if I load any plugins. – Ryuk Feb 21 '13 at 17:42
  • Perhaps it may be better to use a dependency injecting library such as MEF, to handle the loading of plugins? – ywm Feb 21 '13 at 17:52
  • Thanks for the suggestion, but it seams I fixed my issue. Iv'e answered my own question. – Ryuk Feb 21 '13 at 18:01

1 Answers1

0

It turns out I had a few things wrong in my PluginDomain Class.

Fix #1:

Replace:

return (PluginBase)Activator.CreateInstance(t);

With:

(PluginBase)asm.CreateInstance(t.ToString());

Fix #2:

Remove:

AppDomain.Unload(CurrentDomain);

Fix #3: (Purely for debugging)

Replace:

return CurrentAssemblyLoader.Load(assemblyName);

With:

PluginBase obj = CurrentAssemblyLoader.Load(assemblyName);
return obj;

EDIT:

It should be noted that the new AppDomain wont be able to access objects in the old one; so my problem is only half fixed.

Ryuk
  • 95
  • 1
  • 5
  • 16