• 基于AppDomain的插件开发EMIT通用加载框架(四)


    前面我们已经实现了IPlugin接口的插件自动加载。 但是程序使用中,可以不仅仅只有一个接口,而可能是多个接口并存,不能每个接口都手动实现代码类!怎么办?

    解决方案是:泛型+EMIT。 使用泛型把自动加载和插件接口分开,使用EMIT自动生成代理类。

    如4项目组成:自动加载+接口定义+接口实现+测试程序,如下图:

    我们使用的方式上,仍没有什么改变:

    private void FormMain_Load(object sender, EventArgs e)

    {

    //这里加上了泛型参数 IPlugin表示要求自动生成这个接口的代理类

    var inst = PluginManager<IPlugin>.Instance;

    inst.PluginChanged += OnPluginChanged;

    }

    void OnPluginChanged(object sender, PluginManagerEventArgs<IPlugin> e)

    {

    if (e.ChangeType == PluginChangeType.Created)

    {

    // 这里初始化插件,提供服务

    e.PluginInstance.Run(DateTime.Now.ToString());

    }

    }

    当然,最主要的项目就是"PluginLoader"

    思路仍是使用RemotePluginLoader加载插件,但此类不再实现IPlugin,而是使用EMIT生成一个插件代理类,该代理类从MarshalByRefObject 继承以支持AppDomain间的通信。

    public class RemotePluginLoader<T> : RemoteTypeLoader where T :class

    {

    protected Type pluginType;

    public T Plugin { get; private set; }

    protected override System.Reflection.Assembly LoadAssembly(string assemblyPath)

    {

    var ass = base.LoadAssembly(assemblyPath);

    //查找插件

    Type typePlugin = typeof(T);

    this.pluginType = ass.GetTypes().Where(t => typePlugin.IsAssignableFrom(t)).FirstOrDefault();

    //生成代理插件类,并从MarshalByRefObject继承以支持AppDomain通信

    this.Plugin = InterfaceProxyBuilder<T>.CreateProxy((T)System.Activator.CreateInstance(pluginType), typeof(MarshalByRefObject));

    return ass;

    }

    /// <summary>

    ///直接在远程Domain中,执行方法。要求支持序列化

    /// </summary>

    /// <param name="action">执行的匿名方法</param>

    public void Execute(Action action)

    {

    action();

    }

    /// <summary>

    ///直接在远程Domain中,执行函数。要求支持序列化

    /// </summary>

    /// <typeparam name="T"></typeparam>

    /// <param name="fun"></param>

    /// <returns></returns>

    public T Execute<T>(Func<T> fun)

    {

    return fun();

    }

    在调用方,基于插件更新时,我们要求统一更新引用,也有生成一个代理类。 所不同的是,该代理类每次都要访问函数,从而每次都可以检查插件的情况并进行控制:

    public class PluginCallerProxy<T> where T : class

    {

    private T _targetPlugin;

    private T _proxyPlugin;

    private PluginLoader<T> _pluginLoader;

    private System.Threading.ReaderWriterLockSlim locker = new ReaderWriterLockSlim();

    /// <summary>

    ///构造一个代理类,保存在 ProxyPlugin 属性中

    /// </summary>

    /// <param name="loader"></param>

    public PluginCallerProxy(PluginLoader<T> loader)

    {

    this.PluginLoader = loader;

    this.TargetPlugin = loader.Plugin;

    //从函数生成代理类

    this._proxyPlugin = InterfaceProxyBuilder<T>.CreateFuncProxy(() => this.TargetPlugin, typeof(object));

    }

    /// <summary>

    ///供使用的代理

    /// </summary>

    public T ProxyPlugin

    {

    get { return _proxyPlugin; }

    }

    #region实现

    internal PluginLoader<T> PluginLoader

    {

    get

    {

    return _pluginLoader;

    }

    set

    {

    _pluginLoader = value;

    this.TargetPlugin = _pluginLoader == null ? null : _pluginLoader.Plugin;

    }

    }

    internal T TargetPlugin

    {

    get

    {

    locker.EnterReadLock();

    try

    {

    if (_targetPlugin == null)

    {

    throw new PluginException("插件已经卸载");

    }

    return _targetPlugin;

    }

    finally

    {

    locker.ExitReadLock();

    }

    }

    set

    {

    locker.EnterWriteLock();

    try

    {

    _targetPlugin = value;

    }

    finally

    {

    locker.ExitWriteLock();

    }

    }

    }

    #endregion

    }

    最终得到新的插件管理类,代码如下:

    public class PluginManager<T> where T : class

    {

    #region实现

    #region字段

    private static PluginManager<T> _instance = null;

    private FileSystemWatcher pluginWatcher;

    private Timer timerProcess = null;

    private ConcurrentDictionary<string, FileSystemEventArgs> changedPlugins = new ConcurrentDictionary<string, FileSystemEventArgs>();

    private ConcurrentDictionary<string, PluginCallerProxy<T>> plugins = new ConcurrentDictionary<string, PluginCallerProxy<T>>();

    #endregion

    static PluginManager()

    {

    if (_instance == null)

    {

    lock (typeof(PluginManager<T>))

    {

    if (_instance == null)

    _instance = new PluginManager<T>();

    }

    }

    }

    private PluginManager()

    {

    string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins");

    //监控

    this.pluginWatcher = new FileSystemWatcher(path, "*.dll");

    this.pluginWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;

    this.pluginWatcher.Changed += OnPluginChanged;

    this.pluginWatcher.Created += OnPluginChanged;

    this.pluginWatcher.Deleted += OnPluginChanged;

    this.pluginWatcher.Renamed += OnPluginRenamed;

    pluginWatcher.EnableRaisingEvents = true;

    timerProcess = new Timer(new TimerCallback(e => this.ProcessChangedPlugin()));

    //加载所有

    Directory.GetFiles(path, "*.dll").ToList().ForEach(file =>

    {

    FileInfo fi = new FileInfo(file);

    this.changedPlugins[fi.Name] = new FileSystemEventArgs(WatcherChangeTypes.Created, fi.DirectoryName, fi.Name);

    });

    this.timerProcess.Change(10, -1);

    }

    void OnPluginRenamed(object sender, RenamedEventArgs e)

    {

    //重命名,理解为去掉原来的,增加新命名的

    FileInfo old = new FileInfo(e.OldFullPath);

    this.changedPlugins[old.Name] = new FileSystemEventArgs(WatcherChangeTypes.Deleted, old.DirectoryName, old.Name);

    FileInfo n = new FileInfo(e.FullPath);

    this.changedPlugins[n.Name] = new FileSystemEventArgs(WatcherChangeTypes.Created, n.DirectoryName, n.Name);

    //1秒后再处理

    this.timerProcess.Change(1000, -1);

    }

    void OnPluginChanged(object sender, FileSystemEventArgs e)

    {

    Debug.Print(e.Name + e.ChangeType);

    //记录变更

    this.changedPlugins[e.Name] = e;

    //1秒后再处理

    this.timerProcess.Change(1000, -1);

    }

    protected void ProcessChangedPlugin()

    {

    #region处理插件变化

    foreach (var kv in this.changedPlugins)

    {

    FileSystemEventArgs e;

    if (changedPlugins.TryRemove(kv.Key, out e))

    {

    Debug.Print(e.Name + "=>" + e.ChangeType);

    switch (e.ChangeType)

    {

    case WatcherChangeTypes.Created:

    {

    //加载

    var loader = new PluginLoader<T>(e.Name);

    var proxy = new PluginCallerProxy<T>(loader);

    plugins.TryAdd(e.Name, proxy);

    OnPluginChange(new PluginManagerEventArgs<T>(e.Name, PluginChangeType.Created, proxy.ProxyPlugin));

    }

    break;

    case WatcherChangeTypes.Deleted:

    {

    PluginCallerProxy<T> proxy;

    if (plugins.TryRemove(e.Name, out proxy))

    {

    OnPluginChange(new PluginManagerEventArgs<T>(e.Name, PluginChangeType.Deleted, proxy.ProxyPlugin));

    var loader = proxy.PluginLoader;

    proxy.PluginLoader = null;

    loader.Unload();

    }

    }

    break;

    case WatcherChangeTypes.Changed:

    {

    PluginCallerProxy<T> proxy;

    if (plugins.TryGetValue(e.Name, out proxy))

    {

    OnPluginChange(new PluginManagerEventArgs<T>(e.Name, PluginChangeType.Deleted, proxy.ProxyPlugin));

    var loader = proxy.PluginLoader;

    loader.Unload();

    loader = new PluginLoader<T>(e.Name);

    proxy.PluginLoader = loader;

    OnPluginChange(new PluginManagerEventArgs<T>(e.Name, PluginChangeType.Created, proxy.ProxyPlugin));

    }

    }

    break;

    }

    }

    }

    #endregion

    }

    protected virtual void OnPluginChange(PluginManagerEventArgs<T> e)

    {

    if (PluginChanged != null)

    {

    PluginChanged(this, e);

    }

    }

    #endregion

    #region公共

    public static PluginManager<T> Instance

    {

    get

    {

    return _instance;

    }

    }

    public event PluginChangeHandle<T> PluginChanged;

    #endregion

    }

    附:

    1. EMIT代理生成

    /// <summary>

    ///从接口 TPlugin 生成代理类

    /// </summary>

    /// <typeparam name="TPlugin">要求是一个接口</typeparam>

    public static class InterfaceProxyBuilder<TPlugin> where TPlugin : class

    {

    private static ConcurrentDictionary<Type, Type> proxies = new ConcurrentDictionary<Type, Type>();

    private static void CheckTargetType(Type targetType)

    {

    if (!targetType.IsInterface)

    {

    throw new NotImplementedException("T 必须为接口");

    }

    }

    /// <summary>

    ///生成接口实例对像的代理

    /// </summary>

    /// <typeparam name="TObject"></typeparam>

    /// <param name="target"></param>

    /// <returns></returns>

    public static TPlugin CreateProxy<TObject>(TObject target, Type baseType) where TObject : TPlugin

    {

    Type targetType =typeof(TPlugin);

    CheckTargetType(targetType);

    var proxyType = proxies.GetOrAdd(targetType, t =>

    {

    return CreateProxyType(t, baseType);

    });

    return System.Activator.CreateInstance(proxyType, target) as TPlugin;

    }

    /// <summary>

    ///生成一个接口的代理类,并从baseType继承

    /// </summary>

    /// <param name="targetType">接口类型</param>

    /// <param name="baseType">基类</param>

    /// <returns>代理类</returns>

    public static Type CreateProxyType(Type targetType, Type baseType)

    {

    Debug.Assert(targetType.IsInterface,"必须是接口");

    string typeName = string.Concat(targetType.FullName, "_Proxy");

    AssemblyName assemblyName = new AssemblyName("DynamicAssembly");

    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);

    var module = assemblyBuilder.DefineDynamicModule("DynamicProxy", "DynamicAssembly.dll");

    //声明类

    var typeBuilder = module.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, baseType, new Type[] { targetType }); //

    //实现类

    //字段

    var fieldBuilder = typeBuilder.DefineField("target", targetType, FieldAttributes.Private);

    fieldBuilder.SetConstant(null);

    //构造函数

    var cnstBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { targetType });

    {

    var il = cnstBuilder.GetILGenerator();

    il.Emit(OpCodes.Ldarg_0);

    il.Emit(OpCodes.Ldarg_1);

    il.Emit(OpCodes.Stfld, fieldBuilder);

    il.Emit(OpCodes.Ret);

    }

    //属性

    Dictionary<MethodInfo, MethodBuilder> map = new Dictionary<MethodInfo, MethodBuilder>();

    //方法代理

    var methods = targetType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

    methods.ToList().ForEach(mi =>

    {

    var retType = mi.ReturnType;

    var parmTypes = mi.GetParameters().ToList().ConvertAll(pi => pi.ParameterType).ToArray();

    MethodAttributes methodAttrs = mi.Attributes & (~MethodAttributes.Abstract);

    var methodBuilder = typeBuilder.DefineMethod(mi.Name, methodAttrs, CallingConventions.Standard, mi.ReturnType, parmTypes);

    //方法体

    {

    var il = methodBuilder.GetILGenerator();

    il.Emit(OpCodes.Ldarg_0);

    il.Emit(OpCodes.Ldfld, fieldBuilder);

    if (parmTypes.Length > 0)

    {

    for (int i = 1; i <= parmTypes.Length; ++i)

    {

    switch (i)

    {

    case 0:

    il.Emit(OpCodes.Ldarg_0);

    break;

    case 1:

    il.Emit(OpCodes.Ldarg_1);

    break;

    case 2:

    il.Emit(OpCodes.Ldarg_2);

    break;

    case 3:

    il.Emit(OpCodes.Ldarg_3);

    break;

    default:

    il.Emit(OpCodes.Ldarg_S, i);

    break;

    }

    }

    }

    il.Emit(OpCodes.Callvirt, mi);

    if (mi.ReturnType == typeof(void))

    {

    il.Emit(OpCodes.Pop);

    }

    il.Emit(OpCodes.Ret);

    }

    map.Add(mi, methodBuilder);

    });

    var props = targetType.GetProperties();

    props.ToList().ForEach(pi =>

    {

    var propBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.HasDefault, pi.ReflectedType, null);

    if (pi.CanRead)

    {

    var mi = pi.GetGetMethod();

    Debug.Assert(map.ContainsKey(mi));

    var builder = map[mi];

    propBuilder.SetGetMethod(builder);

    }

    if (pi.CanWrite)

    {

    var mi = pi.GetSetMethod();

    Debug.Assert(map.ContainsKey(mi));

    var builder = map[mi];

    propBuilder.SetSetMethod(builder);

    }

    });

    var ret = typeBuilder.CreateType();

    #if DEBUG

    assemblyBuilder.Save(string.Concat(typeName, "_", DateTime.Now.ToString("yyyyMMddHHmmss") , ".dll"));

    #endif

    return ret;

    }

    public static TPlugin CreateFuncProxy(Func<TPlugin> targetFunc, Type baseType)

    {

    Type targetType = typeof(TPlugin);

    CheckTargetType(targetType);

    var proxyType = proxies.GetOrAdd(typeof(Func<TPlugin>), t =>

    {

    return CreateFuncProxyType(t, baseType);

    });

    return System.Activator.CreateInstance(proxyType, targetFunc) as TPlugin;

    }

    /// <summary>

    ///生成一个接口工厂的代理类,并从baseType继承

    /// </summary>

    /// <param name="targetType">接口工厂类型要求必须为 Func&lt;TPlugin&gt;</param>

    /// <param name="baseType">基类</param>

    /// <returns>代理类</returns>

    public static Type CreateFuncProxyType(Type targetType, Type baseType)

    {

    var miInvoke = targetType.GetMethod("Invoke");

    Debug.Assert(miInvoke != null, "Func<T>.Invoke 必须有");

    Type interfaceType = miInvoke.ReturnType;

    string typeName = string.Concat(interfaceType.FullName, "_FuncProxy");

    AssemblyName assemblyName = new AssemblyName("DynamicAssembly");

    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);

    var module = assemblyBuilder.DefineDynamicModule("DynamicProxy", "DynamicAssembly.dll");

    //声明类

    var typeBuilder = module.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, baseType, new Type[] { interfaceType }); //

    //实现类

    //字段

    var fieldBuilder = typeBuilder.DefineField("targetFunc", targetType, FieldAttributes.Private);

    fieldBuilder.SetConstant(null);

    //构造函数

    var cnstBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { targetType });

    {

    var il = cnstBuilder.GetILGenerator();

    il.Emit(OpCodes.Ldarg_0);

    il.Emit(OpCodes.Ldarg_1);

    il.Emit(OpCodes.Stfld, fieldBuilder);

    il.Emit(OpCodes.Ret);

    }

    //属性

    Dictionary<MethodInfo, MethodBuilder> map = new Dictionary<MethodInfo, MethodBuilder>();

    //方法代理

    var methods = interfaceType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

    methods.ToList().ForEach(mi =>

    {

    var retType = mi.ReturnType;

    var parmTypes = mi.GetParameters().ToList().ConvertAll(pi => pi.ParameterType).ToArray();

    MethodAttributes methodAttrs = mi.Attributes & (~MethodAttributes.Abstract);

    var methodBuilder = typeBuilder.DefineMethod(mi.Name, methodAttrs, CallingConventions.Standard, mi.ReturnType, parmTypes);

    //方法体

    {

    var il = methodBuilder.GetILGenerator();

    il.Emit(OpCodes.Ldarg_0);

    il.Emit(OpCodes.Ldfld, fieldBuilder);

    il.Emit(OpCodes.Callvirt, miInvoke);

    if (parmTypes.Length > 0)

    {

    for (int i = 1; i <= parmTypes.Length; ++i)

    {

    switch (i)

    {

    case 0:

    il.Emit(OpCodes.Ldarg_0);

    break;

    case 1:

    il.Emit(OpCodes.Ldarg_1);

    break;

    case 2:

    il.Emit(OpCodes.Ldarg_2);

    break;

    case 3:

    il.Emit(OpCodes.Ldarg_3);

    break;

    default:

    il.Emit(OpCodes.Ldarg_S, i);

    break;

    }

    }

    }

    il.Emit(OpCodes.Callvirt, mi);

    if (mi.ReturnType == typeof(void))

    {

    il.Emit(OpCodes.Pop);

    }

    il.Emit(OpCodes.Ret);

    }

    map.Add(mi, methodBuilder);

    });

    var props = interfaceType.GetProperties();

    props.ToList().ForEach(pi =>

    {

    var propBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.HasDefault, pi.ReflectedType, null);

    if (pi.CanRead)

    {

    var mi = pi.GetGetMethod();

    Debug.Assert(map.ContainsKey(mi));

    var builder = map[mi];

    propBuilder.SetGetMethod(builder);

    }

    if (pi.CanWrite)

    {

    var mi = pi.GetSetMethod();

    Debug.Assert(map.ContainsKey(mi));

    var builder = map[mi];

    propBuilder.SetSetMethod(builder);

    }

    });

    var ret = typeBuilder.CreateType();

    #if DEBUG

    assemblyBuilder.Save(string.Concat(typeName, "_Func_", DateTime.Now.ToString("yyyyMMddHHmmss"), ".dll"));

    #endif

    return ret;

    }

    }

     

    2. 项目类关系图:

  • 相关阅读:
    Java中怎么控制线程訪问资源的数量
    我的Hook学习笔记
    《编写可读代码的艺术》——简单总结
    Hdu 1016 Prime Ring Problem (素数环经典dfs)
    setsockopt()使用方法(參数具体说明)
    Html5培训之精髓
    [unity3d]unity平台的预处理
    音频编辑大师 3.3 注冊名 注冊码
    linux tar.gz zip 解压缩 压缩命令
    面向对象的三个基本特征
  • 原文地址:https://www.cnblogs.com/evlon/p/emt_plugin.html
Copyright © 2020-2023  润新知