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


    硬件产品库-反射模块

    公共项目改进

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

    按照上一章所描述的顺序进行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

      /// 
      /// 通过获取的硬件路径返回硬件公共类
      /// 
      /// 硬件dll存放的路径
      /// 
      public List GetHardwareAbstracts(string[] pluginsDlls = null)
      {
          List hardwareAbstracts = new List();
          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
        {
        
            /// 
            /// 读取的dll文件路径
            /// 
            public string DllFile { get; internal set; }
            /// 
            /// 类加载失败的异常信息
            /// 
            public Exception Exception { get; internal set; }
            /// 
            /// 位置
            /// 
            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
    _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 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全部内容如下

      
      <configuration>
        <runtime>
          
          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            
            
            
            <probing privatePath="dlls;"/>
          assemblyBinding>
        runtime>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
        startup>
      configuration>
      

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

  • 相关阅读:
    PBA.客户需求分析 & 需求管理
    Kotlin高仿微信-第25篇-朋友圈-显示列表
    如何使用ModelBox快速提升AI应用性能?
    三、一起来学习Java 基础语法
    【MySql】mysql之用户管理
    [附源码]java毕业设计基于web场馆预约管理系统
    基于反演法的悬架控制
    EG边缘计算网关连接纵横云3.0物联网平台(MQTT协议)
    PHP基础语法(上)
    数字_获取指定位数的小数
  • 原文地址:https://www.cnblogs.com/wanghun315/p/17598776.html