硬件管理平台-硬件产品库-反射模块

发布时间 2023-08-01 18:43:57作者: 摧残一生

硬件产品库-反射模块

公共项目改进

在公共项目中对当前目录进行分组,主要按照使用场景进行划分

按照上一章所描述的顺序进行1-4的划分,其中公共包为所有项目共用的代码,虽然该部分是编写期间随时进行编写的,但是按照个人习惯,喜欢将公共部分放到最上面。

界面布局

该项目暂不涉及酷炫页面的展示效果,因此使用的是WIndows窗体应用程序,程序名称为:HardwareGatewayProductization

按照上文截出的页面进行布局和控件拖拽:

  1. 窗体的大小设置:Width:1536,Heigth:1454

  2. 使用Panel设置布局

    先设置一个左右划分的布局,窗体宽度改变时左侧区域跟着改变,然后右侧再划分一个上下的,当窗体高度改变时,上册区域的高度会随之改变。

    1. 拖拽一个Panel(panel1),将Dock属性设置为Right
    2. 拖拽一个Panel(panel2)放到panel1的左侧,将Dock属性设置为Fill
    3. 拖拽一个Panel(panel3)放到panel1的内部,将Dock属性设置为Bottom
    4. 拖拽一个Panel(panel4)放到panel3的上方,将Dock属性设置为Fill

    这样子,就完成了布局设置。

  3. 在panel2中拖拽TreeView,将Dock属性设置为Fill,CheckBoxes属性为true,命名为hardwareTV

  4. 在panel4中拖拽一个Label,用于展示硬件的说明信息,命名为infoLab

  5. 在panel3暂时不做处理,因为该处是定时任务所需的,先不操作。

反射功能

添加MainForm_Load方法,一个是反射方法(InitDlls),一个是获得反射后的类的方法(LoadLibrary)。

InitDlls

因为硬件网关项目也需要该反射方法,因此将该方法放到公共包中。

反射公共项目

:在公共包中创建一个AssemblyLibraries项目,创建项目时位置中添加了一个public文件夹

在AssemblyLibrary中创建一个control文件夹,里面创建AssemBlyControl的类。

AssemBlyControl类
  1. GetHardwareAbstracts

    该类的作用是通过特定文件夹获取HardwareAbstract类的列表,对外接口名称为GetHardwareAbstracts

    /// <summary>
    /// 通过获取的硬件路径返回硬件公共类
    /// </summary>
    /// <param name="pluginsDlls">硬件dll存放的路径</param>
    /// <returns></returns>
    public List<HardwareAbstract> GetHardwareAbstracts(string[] pluginsDlls = null)
    {
        List<HardwareAbstract> hardwareAbstracts = new List<HardwareAbstract>();
        List<string> files = GetLoadDlls(pluginsDlls);
        // 2.循环将每个dll文件都加载起来
        foreach (string dllFile in files)
        {
            HardwareAbstract hardware = GetHardwareAbstract(dllFile);
            if (hardware != null)
            {
                hardwareAbstracts.Add(hardware);
            }
        }
        return hardwareAbstracts;
    }
    
    • 目前HardwareAbstract只是新建的一个空类
    • GetLoadDlls方法为获取当前文件夹下面的所有dll文件
    • GetHardwareAbstract为反射获取HardwareAbstract类
  2. GetLoadDlls方法,该方法就是简单的获取特定目录下的所有dll文件,代码如下

    List<string> files = new List<string>();
    if (pluginsDlls != null && pluginsDlls.Length > 0) {
        files.AddRange(Directory.GetFiles(pluginsDlls, "*.dll", SearchOption.AllDirectories));
    }
    return files;
    
  3. GetHardwareAbstract为核心方法

    //  2.1 动态加载当前循环的dll文件
    Assembly assembly = Assembly.LoadFile(dllFile);
    //  2.2 获取当前dll中的所有的public类型
    Type[] types = null;
    try
    {
        types = assembly.GetExportedTypes();
    }
    catch (Exception ex)
    {
                    
    }
    
    //  2.3 获取IEditor接口的Type
    Type typeIEditor = typeof(HardwareAbstract);
    // 查找继承自指定接口的第一个类
    var clazz = types.FirstOrDefault(t => t.IsSubclassOf(typeof(HardwareAbstract)));
    if (clazz != null)
    {
        if (clazz.IsClass && !clazz.IsAbstract && clazz.IsPublic)
        {
            // 检查是否存在无参的构造
            if (clazz.GetConstructors().All(con => con.GetParameters().Length == 0))
            {
                HardwareAbstract hardware = Activator.CreateInstance(clazz) as HardwareAbstract;
                hardware.IncludeDll = dllFile;
                return hardware;
            }
        }
    }
    return null;
    

    :所有的硬件类都默认有无参构造函数,且因为统一,所以约定所有的项目类都无有参构造函数。如果有,则需要删除检查无法构造函数的判断。

  4. 在GetHardwareAbstract中有异常抛出,因此需要重写异常类及异常的参数AssemblyErrorEventAssemblyErrorEventArgs

    • AssemblyErrorEventArgs 异常抛出时的参数信息

      public class AssemblyErrorEventArgs : EventArgs
      {
      
          /// <summary>
          /// 读取的dll文件路径
          /// </summary>
          public string DllFile { get; internal set; }
          /// <summary>
          /// 类加载失败的异常信息
          /// </summary>
          public Exception Exception { get; internal set; }
          /// <summary>
          /// 位置
          /// </summary>
          public string Seek { get; internal set; }
      }
      
    • AssemblyErrorEvent 异常类,异常类中的的参数基类为EventArgs

      表示反射时硬件加载出现了什么问题

      public delegate void AssemblyErrorEvent<T>(T e) where T : AssemblyErrorEventArgs;
      
InitDlls方法

添加反射公共项目依赖到硬件产品库项目中,并在InitDlls方法中添加如下代码:

AssemblyControl assembly = new AssemblyControl();
//获得dll中的文件信息
string[] dlls = Directory.GetFiles(assembly.PluginsFolder, "*.dll", SearchOption.AllDirectories);
// 全局变量List<HardwareAbstract>
_hardwares = assembly.GetHardwareAbstracts(dlls);
功能延申
  1. 手动引用dll

    基础部分已添加成功,但是有个问题,我们将硬件的所有dll都放到同一个文件夹中,那获取的dll文件将非常多,都要进行反射效率很慢;再者例如海康的依赖包过大,对于硬件产品库提取时依赖项也需要一并提取,因此手动添加引用。

    说明

    为什么要这么弄,主要是所有dll都在一个文件夹中,太过于臃肿,而分开的话就要约定每个硬件项目的依赖都不要进行复制,只需要将生成路径放到产品库指定的路径(硬件类型名称+"plugins")中即可。

    而运行时需要的dll分两种,第一种为公共包中的dll,该dll只存放一份,所以项目共用;第二种为硬件项目使用,例如海康威视的依赖包。所以这两份需要分开存放,而第二种也需要根据硬件类型存放,便于后期提取时一并放到update.zip包中。

    1. 创建四个全局变量

      1. LocalPath:当前项目所在路径

      2. includeFolder:基础类引用路径,该路径存放的为公共包中的相关dll

      3. runtimeFolder:硬件项目特定引用的dll,可能有些没有

      4. pluginsFolder:组件自身的dll

        public event AssemblyErrorEvent<AssemblyErrorEventArgs> ErrorEvent;
        readonly string _includeFolder = "dlls;runtime";
        readonly string _pluginsFolder = GlobalVar.LocalPath + "plugins";
        readonly string _runtimeFolder = GlobalVar.LocalPath + "runtime";
        
    2. 在GetLoadDlls方法中直接简化为

      return new List<string>(Directory.GetFiles(_pluginsFolder, "*.dll", SearchOption.AllDirectories));
      

      此时硬件产品库的GetHardwareAbstracts方法就不需要传入参数了。

    3. InitDlls方法中手动添加dll的引用

      AssemblyControl assembly = new AssemblyControl();
      //引用文件夹中的dll
      string _add1Folder = string.Join(";", Directory.GetDirectories(assembly.PluginsFolder)).Replace(Application.StartupPath + "\\", "");
      string _add2Folder = string.Join(";", Directory.GetDirectories(assembly.RuntimeFolder)).Replace(Application.StartupPath + "\\", "");
      string privatePath = string.Format("{0};{1};{2}", assembly.IncludeFolder, _add1Folder, _add2Folder);
      AppDomain.CurrentDomain.AppendPrivatePath(privatePath);
      
      //获得dll中的文件信息
      _hardwares = assembly.GetHardwareAbstracts();
      
  2. 改变生成文件夹的构造

    vs2022默认生成时,exe和依赖的dll都在一个文件夹中,很不清晰,特别是文件很多时,特别不清晰,而修改也很简单

    在项目的App.config中添加配置代码,App.config全部内容如下

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <runtime>
        <!--xmlns是必需的特性。指定程序集绑定所需的 XML 命名空间。 使用字符串“urn: 架构-microsoft-com:asm.v1”作为值。-->
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <!--<publisherPolicy apply="yes"/>-->
          <!--指定运行时是否使用发布者策略-->
          <!--指定加载程序集时公共语言运行时搜索的子目录, 其中privatePath是相对于*.exe.config文件的相对路径,多个文件夹以分号分隔。-->
          <probing privatePath="dlls;"/>
        </assemblyBinding>
      </runtime>
      <startup> 
          <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
      </startup>
    </configuration>
    

下一章将介绍LoadLibrary,该方法主要是初始化TreeView控件,但要说明的却是HardwareAbstract的内容构造,因为展示TreeView简单,但是其他内容还要进行额外的添加。