硬件管理平台-硬件产品库-反射模块
硬件产品库-反射模块
公共项目改进
在公共项目中对当前目录进行分组,主要按照使用场景进行划分
按照上一章所描述的顺序进行1-4的划分,其中公共包为所有项目共用的代码,虽然该部分是编写期间随时进行编写的,但是按照个人习惯,喜欢将公共部分放到最上面。
界面布局
该项目暂不涉及酷炫页面的展示效果,因此使用的是WIndows窗体应用程序,程序名称为:HardwareGatewayProductization
按照上文截出的页面进行布局和控件拖拽:
-
窗体的大小设置:Width:1536,Heigth:1454
-
使用Panel设置布局
先设置一个左右划分的布局,窗体宽度改变时左侧区域跟着改变,然后右侧再划分一个上下的,当窗体高度改变时,上册区域的高度会随之改变。
- 拖拽一个Panel(panel1),将Dock属性设置为Right
- 拖拽一个Panel(panel2)放到panel1的左侧,将Dock属性设置为Fill
- 拖拽一个Panel(panel3)放到panel1的内部,将Dock属性设置为Bottom
- 拖拽一个Panel(panel4)放到panel3的上方,将Dock属性设置为Fill
这样子,就完成了布局设置。
-
在panel2中拖拽TreeView,将Dock属性设置为Fill,CheckBoxes属性为true,命名为hardwareTV
-
在panel4中拖拽一个Label,用于展示硬件的说明信息,命名为infoLab
-
在panel3暂时不做处理,因为该处是定时任务所需的,先不操作。
反射功能
添加MainForm_Load方法,一个是反射方法(InitDlls),一个是获得反射后的类的方法(LoadLibrary)。
InitDlls
因为硬件网关项目也需要该反射方法,因此将该方法放到公共包中。
反射公共项目
注:在公共包中创建一个AssemblyLibraries项目,创建项目时位置中添加了一个public文件夹
在AssemblyLibrary中创建一个control文件夹,里面创建AssemBlyControl的类。
AssemBlyControl类
-
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类
-
GetLoadDlls方法,该方法就是简单的获取特定目录下的所有dll文件,代码如下
List<string> files = new List<string>(); if (pluginsDlls != null && pluginsDlls.Length > 0) { files.AddRange(Directory.GetFiles(pluginsDlls, "*.dll", SearchOption.AllDirectories)); } return files;
-
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;
注:所有的硬件类都默认有无参构造函数,且因为统一,所以约定所有的项目类都无有参构造函数。如果有,则需要删除检查无法构造函数的判断。
-
在GetHardwareAbstract中有异常抛出,因此需要重写异常类及异常的参数AssemblyErrorEvent和AssemblyErrorEventArgs
-
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);
功能延申
-
手动引用dll
基础部分已添加成功,但是有个问题,我们将硬件的所有dll都放到同一个文件夹中,那获取的dll文件将非常多,都要进行反射效率很慢;再者例如海康的依赖包过大,对于硬件产品库提取时依赖项也需要一并提取,因此手动添加引用。
说明:
为什么要这么弄,主要是所有dll都在一个文件夹中,太过于臃肿,而分开的话就要约定每个硬件项目的依赖都不要进行复制,只需要将生成路径放到产品库指定的路径(硬件类型名称+"plugins")中即可。
而运行时需要的dll分两种,第一种为公共包中的dll,该dll只存放一份,所以项目共用;第二种为硬件项目使用,例如海康威视的依赖包。所以这两份需要分开存放,而第二种也需要根据硬件类型存放,便于后期提取时一并放到update.zip包中。
-
创建四个全局变量
-
LocalPath:当前项目所在路径
-
includeFolder:基础类引用路径,该路径存放的为公共包中的相关dll
-
runtimeFolder:硬件项目特定引用的dll,可能有些没有
-
pluginsFolder:组件自身的dll
public event AssemblyErrorEvent<AssemblyErrorEventArgs> ErrorEvent; readonly string _includeFolder = "dlls;runtime"; readonly string _pluginsFolder = GlobalVar.LocalPath + "plugins"; readonly string _runtimeFolder = GlobalVar.LocalPath + "runtime";
-
-
在GetLoadDlls方法中直接简化为
return new List<string>(Directory.GetFiles(_pluginsFolder, "*.dll", SearchOption.AllDirectories));
此时硬件产品库的GetHardwareAbstracts方法就不需要传入参数了。
-
在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();
-
-
改变生成文件夹的构造
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简单,但是其他内容还要进行额外的添加。