惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
Microsoft Azure Blog
Microsoft Azure Blog
The Register - Security
The Register - Security
Vercel News
Vercel News
Cloudbric
Cloudbric
Recent Announcements
Recent Announcements
O
OpenAI News
Y
Y Combinator Blog
Hacker News: Ask HN
Hacker News: Ask HN
I
InfoQ
WordPress大学
WordPress大学
S
Secure Thoughts
P
Proofpoint News Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
小众软件
小众软件
I
Intezer
博客园 - 司徒正美
Google DeepMind News
Google DeepMind News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
G
GRAHAM CLULEY
P
Palo Alto Networks Blog
Attack and Defense Labs
Attack and Defense Labs
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
美团技术团队
V
Vulnerabilities – Threatpost
Scott Helme
Scott Helme
C
Check Point Blog
H
Help Net Security
博客园 - Franky
AWS News Blog
AWS News Blog
MongoDB | Blog
MongoDB | Blog
Project Zero
Project Zero
N
Netflix TechBlog - Medium
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V2EX - 技术
V2EX - 技术
Cisco Talos Blog
Cisco Talos Blog
Latest news
Latest news
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Last Week in AI
Last Week in AI
V
V2EX
C
Cybersecurity and Infrastructure Security Agency CISA
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
大猫的无限游戏
大猫的无限游戏
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
The Hacker News
The Hacker News
Schneier on Security
Schneier on Security
PCI Perspectives
PCI Perspectives
Apple Machine Learning Research
Apple Machine Learning Research

博客园 - 云飞扬

揭开文档在线编辑和预览的神秘面纱 AI的正确使用方法 详解HFBPM开发框架(下) 详解HFBPM开发框架(中) 详解HFBPM开发框架(上) 浅谈数据访问层 流程梳理的重要性 菜鸟电子面单对接技术方案(link) 数据库升级工具 HF Code Designer 代码生成器 HF工作流引擎与现有业务系统的整合 一步一步学习开发BPM工作流系统--------(七)BPM表单设计和业务展现平台 一步一步学习开发BPM工作流系统--------(六)BPM流程设计器(2) 一步一步学习开发BPM工作流系统--------(六)BPM流程设计器(1) 一步一步学习开发BPM工作流系统--------(五)用户、组织机构和权限管理 一步一步学习开发BPM工作流系统--------(四)BPM数据库访问层 一步一步学习开发BPM工作流系统--------(三)开发WinForm的应用平台-2 - 云飞扬 一步一步学习开发BPM工作流系统--------(二)BPM工作流系统的组成部分 - 云飞扬 一步一步学习开发BPM工作流系统--------(一)为什么要开发工作流系统 支持多数据库本地和远程调用的数据访问层架构
一步一步学习开发BPM工作流系统--------(三)开发WinForm的应用平台-1
云飞扬 · 2012-07-24 · via 博客园 - 云飞扬

前面介绍过BPM有很多模块,这些模块都是C/S的,需要有一个应用平台来管理这些模块,如果你想自己开发B/S的,可以略过该篇。

我们先来看一下要开发的应用平台都有哪些功能?

首先要有一个主程序,是一个可执行的exe文件,指定统一的调用接口,可以调用实现该接口的Dll,配置信息写入数据库。有了这个应用平台,你可以任意的配置应用模块,用户需要那个给他们配置那个,方便管理和升级。

废话不多说,先看一下我们要做的应用平台的主界面。如下图:

image

左侧是导航树,右侧是工作区,整个主界面采用MDI的方式。主要用到的技术:反射动态调用dll.

本节内容包括:

一、反射技术的应用,将深入介绍程序集、类和方法的获取方法,以及如何过滤掉无用的类和方法名。

二、应用模块开发,通过一个demo介绍如何开发应用模块。

一、反射技术的应用

     反射是一个比较高级的用法,它可以在运行期加载程序集,增加了程序的灵活性,关于反射的原理和相关知识这里不多讲,只讲几个用到的方法。

     如何加载一个程序集?程序集是一个Dll文件也可以是一个EXE文件,总之只要是.Net编写的就可以。反射调用的原理是首先加载程序集文件到内存中,然后从内存中取到程序集中的类,并实例化类,然后取到类中的方法,就可以调用这个方法了,调用的时候,参数必须保持与程序集中的参数位置个数一致。在应用平台中,我们需要动态调用情况包括:动态调用一个窗体,动态调用一个方法,窗体又包括MDI类型和SDI类型,所以我们需要提供几个动态调用的方法。下面的代码动态调用一个MDI窗体。

        /// <summary>
        /// MDI方式动态调用dll中的窗体,只使用于winform.
        /// </summary>

public  System.Windows.Forms.Form CallMDIWindows()
        {
            if (_DllFileName.Trim().Length==0||_DllFileName==null) throw new Exception("CallMDIWindows调用失败,DllName 不允许为空!");
            if (_DllClassName.Trim().Length==0||_DllClassName==null) throw new Exception("CallMDIWindows调用失败,DllClassName 不允许为空!");
            if (_MainForm==null)throw new Exception("CallMDIWindows调用失败,MainForm没有指定!");
            if (!File.Exists(_DllFileName)) throw new Exception("CallMDIWindows调用失败,[" _DllFileName "]不存在。");
            try
            {
                System.Windows.Forms.Form fromCtrl = null;
                Assembly assembly = Assembly.LoadFile(_DllFileName);//从文件中加载一个程序集
                Type tp=assembly.GetType(_DllClassName);//获取到要使用的类名,这里是一个窗体类
                if (_ObjArray==null)//如果窗体类的构造函数不需要参数,这里要与窗体类保持一致
                    fromCtrl = ( System.Windows.Forms.Form)Activator.CreateInstance(tp);//实例化这个类,得到一个窗体的实例
                else//窗体类的构造函数需要参数,这里要与窗体类保持一致
                    fromCtrl = (System.Windows.Forms.Form)Activator.CreateInstance(tp, _ObjArray);//实例化这个类,得到一个窗体的实例
                fromCtrl.MdiParent=_MainForm;//MDI的主窗体
                fromCtrl.Show();//打开这个窗体
                return fromCtrl;//返回该窗体的引用
            }
            catch(Exception ex)
            {
                throw ex;

            }
        }

下面的代码调用一个SDI窗体

/// <summary>
        /// SDI方式动态调用dll中的窗体,只使用于winform.
        /// </summary>
        public System.Windows.Forms.Form  CallSDIWindows()
        {
            if (_DllFileName.Trim().Length==0||_DllFileName==null) throw new Exception("CallSDIWindows调用失败,DllName 不允许为空!");
            if (_DllClassName.Trim().Length==0||_DllClassName==null) throw new Exception("CallSDIWindows调用失败,_DllClassName 不允许为空!");
            if (!File.Exists(_DllFileName)) throw new Exception("CallSDIWindows调用失败,[" _DllFileName "]不存在。");
            try
            {
                System.Windows.Forms.Form fromCtrl = null;
                Assembly assembly = Assembly.LoadFile(_DllFileName);//从文件中加载一个程序集
                Type tp = assembly.GetType(_DllClassName);//获取到要使用的类名,这里是一个窗体类
                if (_ObjArray == null)//如果窗体类的构造函数不需要参数,这里要与窗体类保持一致
                    fromCtrl = ( System.Windows.Forms.Form)Activator.CreateInstance(tp);
                else//窗体类的构造函数需要参数,这里要与窗体类保持一致
                fromCtrl = ( System.Windows.Forms.Form)Activator.CreateInstance(tp,_ObjArray);
                fromCtrl.ShowDialog();
                return fromCtrl;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

下面的代码调用一个方法

/// <summary>
        /// 动态调用dll类中的方法
        /// </summary>
        /// <returns></returns>
        public object CallMethod()
        {
            if (_DllFileName.Trim().Length==0||_DllFileName==null) throw new Exception("CallMethod调用失败,DllName 不允许为空!");
            if (_DllClassName.Trim().Length==0||_DllClassName==null) throw new Exception("CallMethod调用失败,_DllClassName 不允许为空!");
            if (_DllMethodName.Trim().Length==0||_DllMethodName==null) throw new Exception("CallMethod调用失败,DllMethodName 不允许为空!");
            if (!File.Exists(_DllFileName)) throw new Exception("CallMethod调用失败,[" _DllFileName "]不存在。");
            object obj=null;
            try
            {
                Assembly assembly = Assembly.LoadFile(_DllFileName);//从文件中加载一个程序集
                Type tp = assembly.GetType(_DllClassName);//获取到要使用的类名,这里是一个窗体类
                MethodInfo mi=tp.GetMethod(_DllMethodName);//从类中获取要调用的方法名
                if (_ObjArray==null)//类的实例化不需要参数
                    obj = (object)Activator.CreateInstance(tp);
                else //类的实例化需要参数
                    obj = (object)Activator.CreateInstance(tp,_ObjArray);
                return mi.Invoke(obj,_ObjMethodArray);//这里默认方法都需要参数
            }
            catch (Exception ex)
            {
                throw ex;
            }
        } 

这里需要注意的几点:

1、Dll在加载前可以覆盖和删除,一旦加载即处于使用状态无法删除,所以要替换Dll的时候需要先关闭主程序。

2、获取类名的时候,类的名称必须带有完整的命名空间

我们先来学习动态调用,具体的开发过程如下:

第一步:我们先来做一个主应用程序,如下图:

image

第二步:我们开发一个测试的TestDll,界面如下图:

image

该窗体提供一个带参数的构造函数和一个Add方法。代码如下:

public partial class Form1 : Form
    {
        string _userName = "";
        public Form1()
        {
            InitializeComponent();
        }
        public Form1(string userName)
        {
            InitializeComponent();
            _userName = userName;
            label1.Text = _userName;
        }
        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(_userName ",调用Ok了!");
        }
        public int Add(int a, int b)
        {
            return a +b;
        }
    }

第三步:编译TestDll,把TestDll.dll文件拷贝到第一步创建的主应用程序目录下。

第四步:在主应用程序三个按钮分别写如下代码:

private void toolStripButton2_Click(object sender, EventArgs e)
        {
            //打开一个dll中的MDI窗体
            DynamicLibrary dyl = new DynamicLibrary();
            Object[] objArray = new object[1];//dll中类构造函数必须具有的参数数组
            objArray[0] = "云飞扬";
            dyl.DllFileName =Application.StartupPath  "\\TestDll.dll";
            dyl.DllClassName = "TestDll.Form1";
            dyl.ObjArray = objArray;
            dyl.MainForm = this;//主窗体
            dyl.CallMDIWindows();

        }

        private void toolStripButton3_Click(object sender, EventArgs e)
        {
            //打开一个Dll中的SDI窗体
            DynamicLibrary dyl = new DynamicLibrary();
            Object[] objArray = new object[1];//dll中类构造函数必须具有的参数数组
            objArray[0] = "云飞扬";
            dyl.DllFileName = Application.StartupPath   "\\TestDll.dll";
            dyl.DllClassName = "TestDll.Form1";
            dyl.ObjArray = objArray;
            dyl.CallSDIWindows();
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            //执行Dll中的一个方法
            DynamicLibrary dyl = new DynamicLibrary();
            Object[] objArray = new object[1];//dll中类构造函数必须具有的参数数组
            objArray[0] = "云飞扬";
            Object[] objArrayM = new object[2];//dll中Add方法用到的参数数组
            objArrayM[0] = 3;
            objArrayM[1] = 4;
            dyl.DllFileName = Application.StartupPath   "\\TestDll.dll";
            dyl.DllClassName = "TestDll.Form1";
            dyl.ObjArray = objArray;
            dyl.ObjMethodArray = objArrayM;
            dyl.DllMethodName="Add";
           object result= dyl.CallMethod();
           MessageBox.Show(result.ToString());
        }

第五步:运行主应用程序,分别执行三个按钮,分别出现下面的三种效果,说明执行正确。

调用Dll中的一个MDI窗体

image

调用Dll中的SDI窗体

image

执行一个Dll中的方法

image

我们成功的实现了动态调用的几种情况。窗体可以是任意复杂的窗体,方法可以是任意复杂的方法。这个Demo是学习一下利用反射实现动态调用,后面会有源码的下载。到这里我们只是学习了反射,WinForm应用平台的核心已经实现了,但要做成一个应用平台还有很多工作要做,比如dll如何配置,配置信息如何存放,打开的窗体如何判断是否已经存在,避免重复开启,还需要一个登录窗体,还需要一个扑捉错误的统一方法,可以破获dll模块中的所有错误,避免因模块出错整个平台崩掉,由于篇幅限制这些内容下一篇来介绍。

本篇源码下载地址:https://files.cnblogs.com/legweifang/WinAppDynamicDemo.rar