小试Emit

67 views 十一月 25, 10 by Timothy

.NET中的Emit,其实是个很强大的东东,它允许你在你的程序运行时,动态的生成代码。看到这里,也许大家会联想到Reflection,的确,Reflection也是我们平时用得比较多的一种技术,通过Reflection,我们能通过程序集中的元数据,动态的生成目标程序集的Instance,并执行它。而Emit的功能,恰恰和Reflection遥相呼应,前者允许我们动态的生成代码,后者允许我们动态的“查看”和运行代码。Emit和Reflection合在一起,简直就是双剑合璧,简直就是幸福的一家……难怪,微软也很邪恶的把Emit放在了System.Reflection.Emit。

其实哥平时的开发中,用得比较多的,还是Reflection(反射)了,不过早已久仰Emit的大名,又没闲暇时间来窥探一把,最近总算比较闲了,决心研究研究强大的Emit。

话不多说,代码是最有力的说明,先献上一个通过Emit动态创建并生成程序集的例子:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Reflection;
using System.Reflection.Emit;
 
namespace Emit_Learn
{
    class Program
    {
        static void Main(string[] args)
        {
            var name = new AssemblyName("HelloEmit");
            var assemblyBuilder =
            AppDomain.CurrentDomain.DefineDynamicAssembly(name,
            AssemblyBuilderAccess.RunAndSave); //创建程序集
              var modelBuilder = assemblyBuilder.DefineDynamicModule("HelloEmit",             "HelloEmit.dll"); //创建模块
              var typeBuilder = modelBuilder.DefineType("HelloEmit"); //定义类型
              var methodBuilder = typeBuilder.DefineMethod("Execute",                                 MethodAttributes.Public,                                 null,                                 null); //创建MethodBuilder
            var il = methodBuilder.GetILGenerator(); //获取ILGenerator,用于生成方法的IL
            il.Emit(OpCodes.Ldstr, "Hello, Emit");
            il.Emit(OpCodes.Call,                     typeof(Console).GetMethod("WriteLine",                     new Type[] { typeof(string) }));
            il.Emit(OpCodes.Ret);
            typeBuilder.CreateType();
            assemblyBuilder.Save("HelloEmit.dll"); //保存动态生成的程序集到磁盘文件
 
        }
    }
}

在IDE中输入以上代码,F5运行,你会发现,在你的程序的Debug目录,会生成一个HelloEmit.dll。没错,这就是我们的程序动态生成的程序集,并且它是可执行的。以上的代码,动态生成的程序集,包含一个叫HelloEmit的类,类中有一个public属性的Execute方法。方法中调用Console输出字符串:Hello,Emit

这个类,等价于如下的C#代码:

?View Code CSHARP
1
2
3
4
5
6
7
8
internal class HelloEmit
{
    // Methods
    public void Execute()
    {
        Console.WriteLine("Hello, Emit");
    }
}

我们也可以使用.NET Reflector一类的工具,打开生成的Dll查看,顺便验证一下。

Emit适用的场景:适用于对业务灵活性要求很高的系统,可以在运行时动态更改业务逻辑,并动态生成代码。

Dynamic Plugins Manager (五) Plugins Manager 源码下载

113 views 四月 01, 08 by Timothy

今天和同事一起把Plugins Manager放在了google code上面。大家可以从下面的地址获取所有的源码了。

http://code.google.com/p/dynamic-plugins-manager/

Dynamic Plugins Manager (四) 插件及Demo源码下载

184 views 三月 29, 08 by Timothy

这里提供了插件的Demo程序,和编译好的Plugin Manager下载。
Plugin Manager的代码待整理后放上来。[cool]

开发运行环境:
Windows Vista Ultimate
.NET Framework 3.5
Microsoft Visual Studio 2008

点击下载此文件

Dynamic Plugins Manager (三) Demo

208 views 三月 29, 08 by Timothy

下面来演示一下Plugins Manager,先看看我们事先做好的Interface和两个具体实现的插件.

Interface,接口定义:
[code]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Component
{
public interface IComponent
{
string Invoke();
}
}
[/code]

整个接口,只声明了一个Invoke方法,这也是具体的插件必须实现的一个方法。

第一个插件类 class1.cs
[code]
namespace Component
{

public class Class1 : MarshalByRefObject,IComponent
{
string message = string.Empty;
int number;
float number2;

public Class1(string input, int num)
{
message = input;
number = num;
number2 = 0;
}

public Class1(int num1, float num2)
{
message = "none";
number = num1;
number2 = num2;
}

public string Invoke()
{
return "This is TestLibrary. member---message:" + message + "number:" + number.ToString() + "number2:" +number2.ToString();
}
}
}

[/code]

第二个插件类 class2.cs
[code]
namespace Component
{
public class Class2 : MarshalByRefObject,IComponent
{
public Class2()
{
}

public string Invoke()
{
return "This is TestLibrary2 without construct param";
}
}
}

[/code]

这里我们看到区别,第一个插件,需要构造函数,第二个插件不需要构造函数,所以在配置文件中,第二个插件可以关闭掉ConstructParam这个节点。

下面我们将Class1.cs编译好,放到插件目录中,改名为TestLibrary.dll,将Class2.cs直接改名为TestLibrary2.cs,放到插件目录中。
演示的目的:插件1需要在加载时,构造初始化函数,而插件2不需要。
插件1,是已经编译好的dll,不需要实时编译,但是插件2需要。

下面是测试的截图

1.插件目录,一个放入编译好的dll,一个放入.cs源码

2.启动测试程序,看到2个插件都已经被加载

3.此时,再查看插件的目录,我们会看到Plugin Manager为我们编译,并生成的插件2的dll

4.查看插件的日志记录

5.插件的调用
选中TestLibrary,点击Invoke按钮:

选中TestLibrary2,点击Invoke按钮:

6.插件的动态卸载和发布
a.动态卸载,可以直接使用Plugins Manager的UnloadPluginByName直接通过程序调用卸载。也可以手动打开插件的配置文件,将EnablePlugin设置为False
b.动态发布,直接在插件目录中,建立一个新的子目录,然后将插件的dll和xml配置文件直接拷入即可
关于第6点,这些功能的演示,截图比较麻烦,大家可以直接使用测试程序测试即可。

Dynamic Plugins Manager (二) 配置及实现分析

113 views 三月 29, 08 by Timothy

整个Plugins Manager是一个基于dll的程序集,开发平台,基于VS2008和.NET 3.5 Framework。为何要用.NET 3.5呢?没别的原因,只要是为了尝试一下Linq to XML [cool]

使用改组件,需要在应用程序中,添加引用。另外,还需要在应用程序中,建立相关的配置项:
[code]







[/code]

PluginsPath是插件的主目录,里面通过子目录的方式,存放不同的插件。Plugins Manager在启动的时候,会遍历插件主目录,并搜寻可加载的插件。
LogPath是插件的日志目录,里面的日志文件,会详细记载插件管理器当前的运行情况。

另外,在Plugins Manager中,区别不同的插件,是根据不同的插件名称来决定的,这取决于插件dll的命名。
对于不同的插件,Plugins Manager也提供不同的特性配置文件。配置文件是.xml文件,命名需要和插件dll相一致。
下面是一个配置文件的示例:
[code]

Component.Class1
True



D:\MyCode\C# Projects\DynamicPlugInsApp\ITestInterface\bin\Debug\ITestInterface.dll
[/code]

TypeName:插件的类名,和具体的插件命名空间和类名有关,Plugins Manager根据配置文件提供此相关信息,通过.NET的反射技术,生成具体的插件Instance
EnablePlugin:插件开关,True表示当前插件可用,False表示阻止Plugins Manager加载此插件。该开关可以在Plugins Manager运行前设定,也可以在Plugins Manager运行时动态更改。Plugins Manager会检测配置文件变化,并留意EnablePlugin开关,以决定是否动态加载或者卸载该插件。
ConstructParams:此节点,及其子节点,用以读取插件是否需要在运行时初始化构造函数。子节点用以描述构造函数中的参数,以及类型,和初始化的值。构造初始化函数,也是在.NET的反射中实现。这里要注意,节点顺序和构造函数参数顺序有关。如果插件不需要构造函数,可以关闭此节点。
ReferenceList:此节点用以需要动态编译的插件,如果插件代码中还引用了其他的程序集,需要在此进行描述。

此外,插件还提供了事件通知功能,供应用程序进行响应和处理,实现的事件类型有以下几种:

[code]
public enum PluginEventType
{
PluginLoadOver, //所有插件加载完毕
PluginAdded, //插件新增完毕
PluginRemoved, //插件卸载完毕
PluginStopped, //插件管理器已停止
PluginStarted //插件管理器已启动
}
[/code]

插件的事件参数定义如下,只包含简单的事件类型和文本描述:

[code]
public class PluginEventArgs : EventArgs
{
#region Private Members
public PluginEventType EventType { get; set; }
public string EventMessage { get; set; }
#endregion

public PluginEventArgs(PluginEventType eventtype, string message)
{
EventType = eventtype;
EventMessage = message;
}

}
[/code]

以上是使用该插件,需要配置的地方,下一篇日志,将演示该插件的Demo.

Dynamic Plugins Manager (一) 介绍

117 views 三月 29, 08 by Timothy

Dynamic Plugins Manager是一个基于.NET的动态插件管理组件,也是我在业余时间做的一个小东东,代码在07年底就开始动工,因为一直没充裕时间完成,趁这个周末休息,加班加点的Coding,出来了一个原型,估计里面还有很多的显而易见的以及隐而未现的bug。
设计这个组件的初衷,是为了能方便的应用于今后的项目中。先进的企业级项目中,变化最为突出的,还是业务,业务的变化,直接导致编码中业务逻辑代码的变化。如果将这些业务逻辑混杂在一起,业务的变化,最终会导致代码的混乱,甚至难以维护。这对于一个长期迭代的项目来说,是相当痛苦的事。就像直接拿一个成千上万行的代码给开发人员,估计开发人员不是晕厥,就是吐血。而一个难以维护的业务逻辑代码,对于每一次迭代,都是一次冒险。很可能在某次的迭代中,会对以前的业务逻辑,产生影响或者风险。
对于复杂的业务逻辑,需要进行重构,并且将不断变化的业务逻辑,抽离出来。比较折中的方法,就是将这些变化的逻辑,做成独立的插件形式。这样一来,独立的业务,在物理上进行了分离。开发人员也不用担心修改某处业务逻辑,会影响其他的业务模块。而动态的插件管理形式,对后期维护也带来益处,独立的业务逻辑插件之间,不会相互影响。开发人员还可以在程序运行而又不影响业务的情况下,动态的发布、替换原有的业务逻辑模块。
Dynamic Plugins Manager(以下简称Plugins Manager),也就是按照这样的思路和需求来设计的,其实现的功能,具体有一下几个特点:
1.所有的插件,都以DLL程序集的方式发布,Plugins Manager在启动的时候,搜索加载所有可加载的插件并进行加载
2.Plugins Manager,支持插件的动态卸载功能。借助于AppDomain的特性,所有的组件,在动态加载后,可以动态从内存中卸载。插件的实现,需要继承自MarshalByRefObject。
3.与接口无依赖性,Plugins Manager负责插件的管理、动态加载、动态卸载、调用。但是和具体的插件接口无关,这样一来,开发人员可以定义自己的插件接口,并开发不同的插件实现接口。
4.插件可以动态发布和卸载,Plugins Manager会监视当前目录中,插件的变化,对于以xcopy方式,拷贝入插件目录的插件,可以进行动态加载。对于插件的卸载,可以Plugins Manager提供的接口进行动态卸载,也可以实时更改插件配置文件中的具体开关进行卸载。
5.动态编译功能:Plugins Manager允许直接发布插件代码。通过读取插件配置文件中的引用信息,可以直接从代码生成dll插件,并加载。
6.允许插件实现不同的构造函数重载,并将构造函数参数以及值信息,定义在插件配置文件中。Plugins Manager在加载插件的时候,采用配置文件中的值,调用相应的构造函数,并加载插件。
7.事件通知:Plugins Manager在不同的插件操作完成后,会触发相应的事件机制,通知客户端程序进行相应的处理

插件的结构一览:

PluginsManager:插件管理类,实现插件功能的主要代码逻辑,以及对插件容器的维护
AssemblyLoader:插件加载类,实现从AppDomain加载插件的逻辑
PluginEntity:插件实体类,包含插件的信息(插件实例、插件名称……)
AssemblyFactory:插件生成类,实现从插件代码生成dll的功能

下一篇日志中,将会介绍这个插件的具体配置,及简单分析其实现。

Dynamic Plugins Manager

186 views 三月 29, 08 by Timothy

Dynamic Plugins Manager (一) 介绍

http://www.xiaozhou.net/article.asp?id=187

Dynamic Plugins Manager (二) 配置及实现分析

http://www.xiaozhou.net/article.asp?id=188

Dynamic Plugins Manager (三) Demo

http://www.xiaozhou.net/article.asp?id=189

Dynamic Plugins Manager (四) 插件及Demo源码下载

http://www.xiaozhou.net/article.asp?id=190

Dynamic Plugins Manager (五) Plugins Manager 源码下载

http://www.xiaozhou.net/article.asp?id=191

动态加载并显示图片

177 views 十二月 22, 04 by Timothy

HBITMAP hbitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),”**.bmp”, IMAGE_BITMAP,0,0, LR_CREATEDIBSECTION|LR_LOADFROMFILE);
if(hbitmap==NULL)
return FALSE;
CBitmap m_bitmap;
m_bitmap.Attach(hbitmap);
HBITMAP hbitmap;
//先用上面的方法动态的装载位图,然后把图片显示到Static控件上
//获得指向静态控件的指针
CStatic *pStatic=(CStatic *)GetDlgItem(IDC_SHOWBMP);
//获得位图句柄
HBITMAP Bitmap;
//设置静态控件的样式,使其可以使用位图,并试位标显示使居中
pStatic->ModifyStyle(0xF,SS_BITMAP|SS_CENTERIMAGE);
//设置静态控件显示位图
pStatic->SetBitmap(hBitmap);

Page: 1 of 2 1 2