dlbird的博客
我思我想故我在

导航

 

c#实现动态加载Dll

分类: .net

原理如下:

1、利用反射进行动态加载和调用.
 Assembly assembly=Assembly.LoadFrom(DllPath); //利用dll的路径加载,同时将此程序集所依赖的程序集加载进来,需后辍名.dll
Assembly.LoadFile 只加载指定文件,并不会自动加载依赖程序集.Assmbly.Load无需后辍名
 
2、加载dll后,需要使用dll中某类.
Type type=ass.GetType(“TypeName”);//用类型的命名空间和名称获得类型
 
3、需要实例化类型,才可以使用,参数可以人为的指定,也可以无参数,静态实例可以省略
Object obj = Activator.CreateInstance(type,params[]);//利用指定的参数实例话类型
 
4、调用类型中的某个方法:
需要首先得到此方法
MethodInfo mi=type.GetMethod(“MehtodName”);//通过方法名称获得方法
 
5、然后对方法进行调用,多态性利用参数进行控制
mi.Invoke(obj,params[]);//根据参数直线方法,返回值就是原方法的返回值

 

 

[c-sharp] view plaincopy
 
  1. #region 声明动态载入DLL的参数  
  2.        object obj=null;  
  3.        byte[] filesByte;  
  4.        Assembly assembly;  
  5.        Type type;  
  6.        MethodInfo timerInitial;  
  7.        MethodInfo timerDispose;  
  8.        #endregion  
  9.   
  10.     private void LoadDll()//加载DLL   
  11.        {  
  12.            try  
  13.            {  
  14.                filesByte = File.ReadAllBytes(Path.GetDirectoryName(Application.ExecutablePath) + "//loadDll.dll");  
  15.                assembly = Assembly.Load(filesByte);  
  16.                type = assembly.GetType("test.loadDll");  
  17.                obj = System.Activator.CreateInstance(type);  
  18.                timerStart = tp.GetMethod("TimerStart");  
  19.                timerStop = tp.GetMethod("TimerStop");  
  20.                if (timerStart != null)  
  21.                {  
  22.                    timerStart.Invoke(obj, null);  
  23.                }  
  24.            }  
  25.            catch(Exception)  
  26.            {  
  27.   
  28.            }  
  29.        }  

 

 

以下摘自MSDN

 

[c-sharp] view plaincopy
 
  1. public class A  
  2. {  
  3.     public virtual int method () {return 0;}  
  4. }  
  5.   
  6. public class B  
  7. {  
  8.     public virtual int method () {return 1;}  
  9. }  
  10.   
  11. class Mymethodinfo  
  12. {  
  13.     public static int Main()  
  14.     {  
  15.         Console.WriteLine ("/nReflection.MethodInfo");  
  16.         A MyA = new A();  
  17.         B MyB = new B();  
  18.   
  19.         // Get the Type and MethodInfo.  
  20.         Type MyTypea = Type.GetType("A");  
  21.         MethodInfo Mymethodinfoa = MyTypea.GetMethod("method");  
  22.   
  23.         Type MyTypeb = Type.GetType("B");  
  24.         MethodInfo Mymethodinfob = MyTypeb.GetMethod("method");  
  25.   
  26.         // Get and display the Invoke method.  
  27.         Console.Write("/nFirst method - " + MyTypea.FullName +  
  28.             " returns " + Mymethodinfoa.Invoke(MyA, null));  
  29.         Console.Write("/nSecond method - " + MyTypeb.FullName +  
  30.             " returns " + Mymethodinfob.Invoke(MyB, null));  
  31.         return 0;  
  32.     }  

 

http://msdn.microsoft.com/en-us/library/a89hcwhh.aspx

 

 

C#实现反射调用动态加载的DLL文件中的方法和在窗体中加载DLL文件的用户控件  

2011-06-15 15:33:58|  分类: 默认分类|举报|字号 订阅

 
 

反射的作用:
1. 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型 
2. 应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射。
3. 反射主要应用与类库,这些类库需要知道一个类型的定义,以便提供更多的功能。

 


1 需要反射的DLL
using System;
namespace Webtest
{
public class ReflectTest
{
public ReflectTest(){}
public string WriteString(string s)
{
   return "欢迎您," + s;
}
//静态函数
public static string WriteName(string s)
{
return "欢迎您光临," + s;
}
//不带参数的函数
public string WriteNoPara()
{
return "您使用的是无参数方法";
}
}
}


应用于反射的例子-在aspNET页面中加入以下函数:
public void test1()
{
System.Reflection.Assembly ass;
Type type ;
object obj;
try
{
ass = System.Reflection.Assembly.LoadFile(@"d:\TestReflect.dll");//要绝对路径
type = ass.GetType("Webtest.ReflectTest");//必须使用 名称空间+类名称
System.Reflection.MethodInfo method = type.GetMethod("WriteString");//方法的名称
obj = ass.CreateInstance("Webtest.ReflectTest");//必须使用名称空间+类名称
string s = (string)method.Invoke(obj,new string[]{"jianglijun"}); // 实例方法的调用
或:string s = (string)method.Invoke(obj,Object[] parametors = new Object[]{"param"}); 
Response.Write(s+"<br>");
method = type.GetMethod("WriteName");//方法的名称
s = (string)method.Invoke(null,new string[]{"jianglijun"}); // 静态方法的调用
Response.Write(s+"<br>");

method = type.GetMethod("WriteNoPara");//无参数的实例方法
s = (string)method.Invoke(obj,null);
Response.Write(s+"<br>");
method = null;
}
catch(Exception ex)
{
Response.Write(ex+"<br>");
}
finally
{
ass = null;
type = null;
obj = null;
}

2、在窗体中加载DLL文件中的用户控件

点击按钮,在窗体的panel1控件中加载用户控件

  private void button1_Click(object sender, EventArgs e)
        {
            Assembly ass = Assembly.LoadFrom(@"C:\Users\zhuochaoyou\Documents\Visual Studio 2010\Projects\class1\class1\obj\Debug\class1.dll");
            object oClient = (System.Windows.Forms.Control)ass.CreateInstance("class1.UserControl1", true);");//必须使用 名称空间+用户控件类名称

            this.panel1.Controls.Add((Control)oClient);

        }

 

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。反射通常具有以下用途:

使用 Assembly 定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例。

使用 Module 了解如下的类似信息:包含模块的程序集以及模块中的类等。您还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。

使用 ConstructorInfo 了解以下信息:构造函数的名称、参数、访问修饰符(如 public 或 private)和实现详细信息(如 abstract 或 virtual)等。使用 Type 的 GetConstructors 或 GetConstructor 方法来调用特定的构造函数。

使用 MethodInfo 了解以下信息:方法的名称、返回类型、参数、访问修饰符(如 public 或 private)和实现详细信息(如 abstract 或 virtual)等。使用 Type 的 GetMethods 或 GetMethod 方法来调用特定的方法。

使用 FieldInfo 了解以下信息:字段的名称、访问修饰符(如 public 或 private)和实现详细信息(如 static)等;并获取或设置字段值。

使用 EventInfo 来了解如下的类似信息:事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等;并添加或移除事件处理程序。

使用 PropertyInfo 来了解如下的类似信息:属性的名称、数据类型、声明类型、反射类型和只读或可写状态等;并获取或设置属性值。

使用 ParameterInfo 来了解如下的类似信息:参数的名称、数据类型、参数是输入参数还是输出参数,以及参数在方法签名中的位置等。

当您在一个应用程序域的仅反射上下文中工作时,请使用 CustomAttributeData 来了解有关自定义属性的信息。使用 CustomAttributeData,您不必创建属性的实例就可以检查它们。[FROM:MSDN]

通过一个示例来简要说明:

using System;
using System.Reflection;

namespace testReflection
{
    public delegate void TestDelegate(string info);
    class Program
    { 
        static void Main(string[] args)
        {
             //从Dll中加载
             //Assembly ass = Assembly.LoadFile(@"TestReflect.dll");
             //Type myType = ass.GetType("testReflection.Person");
             //object aPerson = ass.CreateInstance("Person");

             //取得类型
            Type myType = Type.GetType ("testReflection.Person");
             //构造函数要用到的参数
            object [] constuctParms = new object[] { "Brad Pitt" };
            //创建实例
            //object TestName = Assembly.GetAssembly(myType).CreateInstance("Person"); 
            object aPerson = Activator.CreateInstance (myType, constuctParms);

             //使用MethodInfo 和Invoke 调用方法
            MethodInfo displayInfoMethod = myType.GetMethod ("DisplayInfo");
            displayInfoMethod .Invoke(aPerson, new object[] { "Using Invoke to call Method DisplayInfo()" });

            //使用InvokeMember 调用方法
            //调用方法的一些标志位
             BindingFlags flag = BindingFlags .InvokeMethod | BindingFlags.Public | BindingFlags.Instance;
            myType.InvokeMember("DisplayInfo", flag, null, aPerson, new object[] { "Using InvokeMethod to call DisplayInfo()" });
             //如果方法有返回值
             string name = (string)myType.InvokeMember("getName" , flag, null , aPerson, null );
            System.Console.WriteLine( "call getName(), return: " + name );

            //设置属性值
            myType.InvokeMember("Age" , BindingFlags.SetProperty, null, aPerson, new object[] { 30 });
            //得到属性值
             int age = (int)myType.InvokeMember( "Age", BindingFlags.GetProperty, null, aPerson, null);
            System.Console .WriteLine("Get the property of Age : " + Convert.ToString(age));

             //设置字段值
            myType.InvokeMember ("Name", BindingFlags.SetField , null, aPerson , new object[] { "Michal Jodn" });
             //获取字段值
            string fname = ( string)myType.InvokeMember("Name", BindingFlags.GetField, null , aPerson, null );
            System.Console.WriteLine("Get the Field Value of Name : " + fname);

            myType.InvokeMember("DisplayName", flag, null, aPerson, null);

            //获得方法集
             MethodInfo[] methods = myType.GetMethods();
            foreach (MethodInfo m in methods) {
                System.Console.WriteLine(m.Name);
            }
            //同样还有:GetFiedls()、GetProperties()、GetEvents()等方法

            //使用Delegate
            //此方法是静态的,所以必须提供委托类型。
            TestDelegate dg = (TestDelegate)Delegate.CreateDelegate( typeof(testReflection.TestDelegate), aPerson, "DisplayInfo");
            dg ("Test Delegate by call DisplayInfo()");
             
            
            //获得解决方案的所有Assembly
            Assembly[] AX = AppDomain.CurrentDomain.GetAssemblies();
            //遍历显示每个Assembly的名字
            foreach (object var in AX ) {
                Console.WriteLine ("Assembly的名字:"+var.ToString());                
            }
             //使用一个已知的Assembly名称,来创建一个Assembly
             //通过CodeBase属性显示最初指定的程序集的位置
            Console.WriteLine ("最初指定的程序集TestReflection的位置:" + Assembly.Load("TestReflection").CodeBase);

            System.Console.ReadLine();

         }
    }   

    public class Person
    {
        public string Name;
        private int _Age;
        public int Age
        {
            get
            {
                 return _Age;
             }
            set
            {
                _Age = value;
            }
        }

        public Person (string Name)
         {
            this.Name = Name;
        }
        public void DisplayInfo(string info)
        {
            System.Console.WriteLine(info );
            System.Console.WriteLine( "called sucessfully!");
        }
         public void DisplayName()
        {
            System .Console.WriteLine(Name);
        }
        public string getName()
        {
             return Name;
        }

    }
}
说明:
使用反射动态调用类成员,需要Type类的一个方法:InvokeMember。对该方法的声明如下:

public object InvokeMember(
   string name,
   BindingFlags invokeAttr,
   Binder binder,
   object target,
   object[] args
);

参数

name 
String,它包含要调用的构造函数、方法、属性或字段成员的名称。
- 或 - 
空字符串 (""),表示调用默认成员。

invokeAttr 
一个位屏蔽,由一个或多个指定搜索执行方式的 BindingFlags 组成。访问可以是 BindingFlags 之一,如 Public、NonPublic、Private、InvokeMethod 和 GetField 等。不需要指定查找类型。如果省略查找类型,则将应用 BindingFlags.Public | BindingFlags.Instance。

binder 
一个 Binder 对象,该对象定义一组属性并启用绑定,而绑定可能涉及选择重载方法、强制参数类型和通过反射调用成员。
- 或 - 
若为空引用(Visual Basic 中为 Nothing),则使用 DefaultBinder。

target 
在其上调用指定成员的 Object。

args 
包含传递给要调用的成员的参数的数组。

返回值
表示被调用成员的返回值的 Object。

下列 BindingFlags 筛选标志可用于定义包含在搜索中的成员:

为了获取返回值,必须指定 BindingFlags.Instance 或 BindingFlags.Static。 
指定 BindingFlags.Public 可在搜索中包含公共成员。 
指定 BindingFlags.NonPublic 可在搜索中包含非公共成员(即私有成员和受保护的成员)。
指定 BindingFlags.FlattenHierarchy 可包含层次结构上的静态成员。

下列 BindingFlags 修饰符标志可用于更改搜索的执行方式:
BindingFlags.IgnoreCase,表示忽略 name 的大小写。
BindingFlags.DeclaredOnly,仅搜索 Type 上声明的成员,而不搜索被简单继承的成员。

可以使用下列 BindingFlags 调用标志表示要对成员采取的操作: 
CreateInstance,表示调用构造函数。忽略 name。对其他调用标志无效。 
InvokeMethod,表示调用方法,而不调用构造函数或类型初始值设定项。
对 SetField 或 SetProperty 无效。 
GetField,表示获取字段值。对 SetField 无效。 
SetField,表示设置字段值。对 GetField 无效。 
GetProperty,表示获取属性。对 SetProperty 无效。 
SetProperty 表示设置属性。对 GetProperty 无效。

 

 

使用反射动态调用类成员,需要Type类的一个方法:InvokeMember。对该方法的声明如下(摘抄于MSDN):

分类: 新技术
 使用反射动态调用类成员,需要Type类的一个方法:InvokeMember。对该方法的声明如下(摘抄于MSDN):

public object InvokeMember(

   string name,

   BindingFlags invokeAttr,

   Binder binder,

   object target,

   object[] args

);

参数

name

String,它包含要调用的构造函数、方法、属性或字段成员的名称。

- 或 -

空字符串 (""),表示调用默认成员。

invokeAttr

一个位屏蔽,由一个或多个指定搜索执行方式的 BindingFlags 组成。访问可以是 BindingFlags 之一,如 Public、NonPublic、Private、InvokeMethod 和 GetField 等。不需要指定查找类型。如果省略查找类型,则将应用 BindingFlags.Public | BindingFlags.Instance。

binder

一个 Binder 对象,该对象定义一组属性并启用绑定,而绑定可能涉及选择重载方法、强制参数类型和通过反射调用成员。

- 或 -

若为空引用(Visual Basic 中为 Nothing),则使用 DefaultBinder。

target

要在其上调用指定成员的 Object。

args

包含传递给要调用的成员的参数的数组。

 

返回值

表示被调用成员的返回值的 Object。

 

备注:

下列 BindingFlags 筛选标志可用于定义包含在搜索中的成员:

 

为了获取返回值,必须指定 BindingFlags.Instance 或 BindingFlags.Static。

指定 BindingFlags.Public 可在搜索中包含公共成员。

指定 BindingFlags.NonPublic 可在搜索中包含非公共成员(即私有成员和受保护的成员)。

指定 BindingFlags.FlattenHierarchy 可包含层次结构上的静态成员。

下列 BindingFlags 修饰符标志可用于更改搜索的执行方式:

 

BindingFlags.IgnoreCase,表示忽略 name 的大小写。

BindingFlags.DeclaredOnly,仅搜索 Type 上声明的成员,而不搜索被简单继承的成员。

可以使用下列 BindingFlags 调用标志表示要对成员采取的操作:

 

CreateInstance,表示调用构造函数。忽略 name。对其他调用标志无效。

InvokeMethod,表示调用方法,而不调用构造函数或类型初始值设定项。对 SetField 或 SetProperty 无效。

GetField,表示获取字段值。对 SetField 无效。

SetField,表示设置字段值。对 GetField 无效。

GetProperty,表示获取属性。对 SetProperty 无效。

SetProperty 表示设置属性。对 GetProperty 无效。


一、动态加载控件
        动态加载,最基本用到的就是反射机制。在System.Reflection的namespace下有一系列的关于获取Assembly信息、类(型)信息的类、接口、结构等。可能上面的话对急切想实现动态加载控件的朋友来说可能一点用也没有,那么就看下面的代码吧,也许可以使你马上实现你想要的:

           //加载控件
           Assembly assembly = Assembly.LoadFrom(@"C:\Controls.dll");
            //获得类(型)
            Type type = assembly.GetType("Controls.UserControl",false,true);
            //设置筛选标志
            BindingFlags bflags = BindingFlags.DeclaredOnly | BindingFlags.Public 
                                                        | BindingFlags.NonPublic | BindingFlags.Instance;
            //调用构造函数并获得对象
            Object obj = type.InvokeMember("UserControl", bflags | 
                                                                         BindingFlags.CreateInstance, null, null, null);
            //将对象转换类型
            System.Windows.Forms.Control c = (Control)obj;
            //将控件添加到窗体
            this.Controls.Add(c);


下面对上面程序段用到的一些变量、方法做一点说明
1、BindingFlags,枚举类型
    BindingFlags.Instance                     : 对象实例
    BindingFlags.Static                          : 静态成员 
    BindingFlags.Public                         : 指可在搜索中包含公共成员
    BindingFlags.NonPublic                 : 指可在搜索中包含非公共成员(即私有成员和受保护的成员)
    BindingFlags.FlattenHierarchy      : 指可包含层次结构上的静态成员
    BindingFlags.IgnoreCase               : 表示忽略 name 的大小写
    BindingFlags.DeclaredOnly           : 仅搜索 Type 上声明的成员,而不搜索被简单继承的成员
    BindingFlags.CreateInstance        : 表示调用构造函数。忽略 name。对其他调用标志无效

2、Type.InvokeMember
public object InvokeMember(
    string name,
    BindingFlags invokeAttr,
    Binder binder,
    object target,
    object[] args
);
参数
      name 
         String,它包含要调用的构造函数、方法、属性或字段成员的名称。 
         - 或 - 
         空字符串 (""),表示调用默认成员。 
    invokeAttr 
            一个位屏蔽,由一个或多个指定搜索执行方式的 BindingFlags 组成。   访问可以是 BindingFlags 之一,如Public、 NonPublic、Private、   InvokeMethod 和 GetField 等。不需要指定查找类型。如果省略查找类型,   则将应用 BindingFlags.Public | BindingFlags.Instance。
      binder
          一个 Binder 对象,该对象定义一组属性并启用绑定,而绑定可能涉及选择重载方法、 强制参数类型和通过反射调用成 员。    - 或 -    若为空引用(Visual Basic 中为 Nothing),则使用 DefaultBinder。 
      target 
          要在其上调用指定成员的 Object。 
    args 
          包含传递给要调用的成员的参数的数组。 
   返回值
          表示被调用成员的返回值的 Object。

二、插件编程
          通过上面代码段,我们基本实现动态加载控件。由此我想到了现在网上提到很多的插件式的开发方法。通过动态加载控件,我们不是能很方便的为软件扩充功能吗?我不知道Eclipse这种插件是怎么实现的,但至少这种动态加载控件的方法实现插件编程的一个变通的方法。不是吗?我把一个功能模块做成一个控件,然后在程序启动是扫描目录,即可获得所有的控件,当点击菜单是,将控件加载到窗体就行了。我在母体程序里,我们所要做的只不过要一个容器窗口类来加载控件。当然,事先要有些约定,比如说,控件有哪些可供调用的方法等等。


 

 

三.调用类成员

下面通过例题对该方法进行简单应用(我一直以为,要让例题起到更容易理解文字的意义和作用,撰写的例题越简单越直观越好。)

using System;

using System.Reflection;

 

namespace ConsoleApplication9

{

    class Love

    {

        public int field1;

        private string _name;

        public Love()

        {

        }

 

        public string Name

        {

            get

            {

                return _name;

            }

            set

            {

                _name=value;

            }

        }

 

        public int GetInt(int a)

        {

            return a;

        }

 

        public void Display(string str)

        {

            Console.WriteLine(str);

        }

    }

 

    /// <summary>

    /// Class1 的摘要说明。

    /// </summary>

    class Class1

    {

        /// <summary>

        /// 应用程序的主入口点。

        /// </summary>

        [STAThread]

        static void Main(string[] args)

        {

            //

            // TODO: 在此处添加代码以启动应用程序

            //

 

            Love love = new Love();

            Type type = love.GetType();

 

            Object obj = type.InvokeMember(null,

                BindingFlags.DeclaredOnly |

                BindingFlags.Public | BindingFlags.NonPublic |

                BindingFlags.Instance | BindingFlags.CreateInstance, null, null, args);

 

 

            //调用没有返回值的方法

            type.InvokeMember("Display",BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance ,null , obj , new object[]{"aldfjdlf"});

 

            //调用有返回值的方法

            int i = (int)type.InvokeMember("GetInt",BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance,null,obj,new object[]{1});

            Console.WriteLine(i);

 

            //设置属性值

            type.InvokeMember("Name",BindingFlags.SetProperty,null,obj,new string[]{"abc"});

            //获取属性值

            string str=(string)type.InvokeMember("Name",BindingFlags.GetProperty,null,obj,null);

            Console.WriteLine(str);

 

            //设置字段值

            type.InvokeMember("field1",BindingFlags.SetField,null,obj,new object[]{444});

 

            //获取字段值

            int f=(int)type.InvokeMember("field1",BindingFlags.GetField,null,obj,null);

            Console.WriteLine(f);

            Console.ReadLine();

        }

    }

}

posted on 2014-08-20 16:03  雪域月光  阅读(666)  评论(0编辑  收藏  举报