一步一步学习开发BPM工作流系统(三)开发WinForm的应用平台1

前面介绍过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

我的程序人生
原文地址:https://www.cnblogs.com/legweifang/p/2607438.html