1

我有一个主应用程序,它将插件加载到单独的应用程序域中。在该插件中,我订阅了从主应用程序传递的对象上的事件。在单独的应用程序域中创建插件会将插件 dll 加载到该域中,但订阅主应用程序对象上的事件将导致插件 dll 也被加载到主应用程序域中。

我希望能够卸载插件域(有效),但我还需要能够替换我目前无法执行的插件 dll,因为该 dll 已加载到主应用程序域中(因为该订阅)。

这是一个类似于我的实际代码的示例代码(为简洁起见):

我有一个名为 IContract 的接口,用于实例化插件:

namespace PluginShared
{
    public interface IContract
    {
        void Init(IHostObject hostObject);
        void Shutdown();
    }
}

它驻留在一个单独的 PluginShared.dll 中。该接口定义了 2 个方法,Init 用于传递在主应用程序中创建的对象,而 Shutdown 用于在插件的应用程序域卸载之前在插件中进行必要的清理。

IHostObject 位于同一个 PluginShared.dll 中,如下所示:

namespace PluginShared
{
    public interface IHostObject
    {
        event Action ValueChanged;
    }
}

该接口由主应用程序中的 HostObject 实现:

public class HostObject : MarshalByRefObject, IHostObject
{
    public event Action ValueChanged;

    public void Foo()
    {
        if (ValueChanged != null)
            ValueChanged();
    }
}

主应用程序代码加载插件,调用 Init 并将 HostObject 实例传递给插件。然后在插件上调用 Shutdown 并卸载插件域。

private void LoadUnloadPlugin()
{
    IHostObject hostObject = new HostObject();

    AppDomain pluginAppDomain = AppDomain.CreateDomain("PluginAppDomain");

    IContract plugin = (IContract)pluginAppDomain.CreateInstanceFromAndUnwrap(@".\Plugin1.dll", "Plugin1.Plugin1");

    plugin.Init(hostObject);
    plugin.Shutdown();

    AppDomain.Unload(pluginAppDomain);
}

该插件是它自己的 Plugin1.dll:

namespace Plugin1
{
    public class Plugin1 : MarshalByRefObject, IContract
    {
        IHostObject myHostObject;

        public void Init(IHostObject hostObject)
        {
            myHostObject = hostObject;
            myHostObject.ValueChanged += OnValueChanged;
        }

        public void Shutdown()
        {
            myHostObject.ValueChanged -= OnValueChanged;
            myHostObject = null;
        }

        private void OnValueChanged()
        {
            // Do Stuff
        }
    }
}

同样,问题是当 myHostObject.ValueChanged += OnValueChanged; 执行时,它会将 Plugin1.dll 加载到主应用程序域中,这将锁定 dll,从而阻止我删除它。

顺便提一句。我有许多类似 IHostObject 的接口,它们定义了由不同对象触发的不同 ValueChanged 类事件。同样一般来说,我的插件不会订阅所有对象的事件。我之所以提到这一点,是因为上面示例代码中的 ValueChanged 事件可以很容易地替换为 IContract 中的方法,该方法将为每个插件调用而不是触发事件,但我的实际实现需要更多类似事件的方法。

那么有没有一种方法/技术/模式我可以使用以便能够从主应用程序中获取选定的通知/事件,而无需将插件 dll 加载到主应用程序域中?

4

0 回答 0