插件系统:如何处理主机应用程序和插件之间的事件

时间:2015-05-26 12:59:26

标签: c# .net events plugins reflection

注意:我需要使用.NET 3.5

我的测试应用程序分为三个部分(不同的项目和不同的dll); host-application,plugin-sdk和test-plugin。

主机应用程序是一个WPF应用程序,其中有一个区域可以从插件中加载视图。插件通过Reflection加载。这部分可以正常运作。

SDK包含插件可以实现的通用接口。我的一个接口有一个插件必须实现的事件,当它被触发时,主应用程序需要监听它,以基于自定义EventArgs执行操作。 SDK是从主机应用程序和插件引用的。

首先是我的通用插件加载器:

public static class GenericPluginLoader<T>
{
    public static ICollection<T> LoadPlugins(string path)
    {
        string[] dllFileNames = null;

        if (Directory.Exists(path))
        {
            dllFileNames = Directory.GetFiles(path, "*.dll");

            var assemblies = new List<Assembly>(dllFileNames.Length);
            foreach (var dllFile in dllFileNames)
            {
                var an = AssemblyName.GetAssemblyName(dllFile);
                var assembly = Assembly.Load(an);
                assemblies.Add(assembly);
            }

            var pluginType = typeof(T);
            var pluginTypes = new List<Type>();
            foreach (var assembly in assemblies)
            {
                if (assembly != null)
                {
                    Type[] types = assembly.GetTypes();

                    foreach (var type in types)
                    {
                        if (type.IsInterface || type.IsAbstract)
                        {
                            continue;
                        }
                        else
                        {
                            if (type.GetInterface(pluginType.FullName) != null)
                            {
                                pluginTypes.Add(type);
                            }
                        }
                    }
                }
            }

            var plugins = new List<T>(pluginTypes.Count);
            foreach (var type in pluginTypes)
            {
                var plugin = (T)Activator.CreateInstance(type);
                plugins.Add(plugin);
            }

            return plugins;
        }

        return null;
    }
}

包含事件的界面我想要我的插件直接工具和自定义事件args

public class JournalLineEventArgs : EventArgs
{
    public string LineNumber { get; set; }
}

public interface IJournalPlugin
{
    event EventHandler<JournalLineEventArgs> LineAdding;
}

我的插件中的事件处理程序

public event EventHandler<JournalLineEventArgs> LineAdding;

在我的主机应用程序中,我将插件加载到列表中,然后添加事件处理程序

private void InitializeIJournalPlugins()
{
    foreach (var plugin in journalPlugins) 
        plugin.LineAdding += OnJournalAdd;
}

这里的主要问题是,当我从插件中引发事件时,它会给我一个空引用异常。我认为这可能是因为主机应用程序知道插件事件但插件并不知道有什么东西正在听它 - 我是对的吗? ;)但是我怎样才能做到这一点?

----更新2015-05-26 15:08 ----

我正在通过一个绑定到WPF按钮的命令调用它:

public ICommand JournalLineCommand
{
    get
    {
        var eventArgs = new JournalLineEventArgs()
        {
            LineNumber = TextField
        };             
        return new RelayCommand(() => LineAdding(this, eventArgs));
    }
}

当我尝试提高它时,事件为空。

2 个答案:

答案 0 :(得分:1)

我建议您重新考虑应用程序的体系结构,以便为您的问题使用标准解决方案。

正如Aron所指出的,您可以使用EventAggregator来发布和订阅整个应用程序中的事件。您可以使用那里的许多实现中的任何一个,这是一个使用轻量级Caliburn.Micro.EventAggregator的简单示例:

示例事件:

public class SampleEvent
{
    public SampleEvent(int lineNumber)
    {
        LineNumber = lineNumber;
    }

    public int LineNumber { get; private set; }
}

示例发布商:

public class Publisher
{
    public Publisher(IEventAggregator eventAggregator)
    {
        EventAggregator = eventAggregator;            
    }

    public void PublishLineNumber(int lineNumber)
    {
        EventAggregator.Publish(new SampleEvent(lineNumber));
    }

    public IEventAggregator EventAggregator { get; private set; }
}

样本订阅者:

public class Subscriber : IHandle<SampleEvent>
{
    public Subscriber(IEventAggregator eventAggregator)
    {
        EventAggregator = eventAggregator;
        EventAggregator.Subscribe(this);
    }
    public IEventAggregator EventAggregator { get; private set; }

    public void Handle(SampleEvent message)
    {
        Console.WriteLine(message.LineNumber);
    }
}

主要方法:

static void Main(string[] args)
    {
        EventAggregator eventAggregator = new EventAggregator();

        Publisher publisher = new Publisher(eventAggregator);
        Subscriber subscriber = new Subscriber(eventAggregator);

        publisher.PublishLineNumber(5);

        Console.ReadLine();
    }

至于加载插件,我建议你使用  MEF导出/导入它们。

您可以将它们与.NET 3.5一起使用

答案 1 :(得分:0)

你不应该重新发明轮子。 Microsoft Patterns团队已经有了一个用于使用插件架构编写WPF应用程序的库。

Install-Package Prism.PubSubEvents

https://msdn.microsoft.com/en-us/library/ff921122.aspx

编辑:对于那些仍在使用.net 2.0的人来说,当然还有Prism 2.x

https://msdn.microsoft.com/en-us/library/ff648611.aspx