随心所欲

做个幸福的人
posts - 152, comments - 1472, trackbacks - 28, articles - 0
  博客园 :: 首页 :: 新随笔 ::  :: 订阅 订阅 :: 管理

实现目的:动态加载dll,执行完毕之后可以随时卸载掉,并可以替换这些dll,以在运行中更新dll中的类。

其实就是通过应用程序域AppDomain加载和卸载程序集。

在这方面微软有篇文章http://www.microsoft.com/china/msdn/archives/library/dncscol/html/csharp05162002.asp介绍的比较详细;Wayfarer's Prattle同志也有一篇http://www.cnblogs.com/wayfarer/archive/2004/09/29/47896.html文章。

具体实现起来倒也不难,我的问题是:可以加载了,可以卸载了,但是不同域的实例是通过代理来实现调用的(这是可以动态加载/卸载的基础),从主域调用子域中的没问题,反之如何让子域中的类来访问主域中的实例呢?


先说动态加载和卸载,前面两篇文章已经有很精彩的论述了,只说几个要点

1:主域中创建子域,加载的dll来自别的文件夹

   AppDomain svcDomain = null;
    try
    {
     AppDomainSetup setup = new AppDomainSetup();
     setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
     setup.PrivateBinPath = setup.ApplicationBase;
      setup.ApplicationName = asmName.FullName;
      setup.ShadowCopyDirectories = setup.ApplicationBase+@"dlls";    //dll不是来自bin目录下(很奇怪,为什么有很多人认为dll必须就在bin目录下)
      setup.ShadowCopyFiles = "true";
      svcDomain = AppDomain.CreateDomain(asmName.FullName, null, setup);
    }
    catch (Exception ex)
    {
         Log.Write("test","can not CreateDomain")
           return;
    }

    // Get remote service handler
    ServiceProxy svc = null;
    try
    { //关键点
       svc = (ServiceProxy)   svcDomain.CreateInstanceFromAndUnwrap(
      svcDomain.BaseDirectory + "\\Services.dll",    "MaServices.ServiceProxy");
    }
    catch (Exception ex)
    {
      AppDomain.Unload(svcDomain);
     Log.Write("test", "can not create proxy")
          return;
    }

2:proxy的定义,一定要继承自MarshalByRefObject,传递的数据对象一定要可以序列化

    public class ServiceProxy: MarshalByRefObject,IDisposable

3:proxy中加载dll

public bool LoadService(string assemblyName)
  {
   Assembly assembly = null;
   try
   {     assembly = Assembly.LoadFrom(assemblyName);
         IService sev = (IService)assembly.CreateInstance(typename, true);

         this.services[assemblyName]=sev ;
   }
 4:proxy中调用具体的类:注意,具体调用一定不能再主域中

public IContext RunService(string serviceName,IContext ctx)
  {
       object o = this.service[serviceName];
        ((IService)o).RunService(ctx);
        return ctx;
   }


     最后,主域中直接调用既可

   proxy.RunService(xx,xx)



就这些。这样程序就可以随时加载一个dll(位置任意),反射得到类,运行,这个dll随时可以被替换

几个关键点:
代理不要返回被反射的实例,传递的数据要可以序列化,注意设置ShadowCopyDirectories。

现在言归正传,说说问题

假设主域M,子域C1,C2。通过代理之后,M可以调用C1和C2中的实例,反过来,如何让C1调用M中的实例呢?或者C1调用C2中的实例?

tcp通讯是一种方法,但是系统消耗太大,频繁使用那就糟透了。

谁有高招?

还有一个问题:看上述文章时发现,他们的prxoy在返回时都是返回的要代理的类的实例,对于这一点我比较怀疑。如果dll不在一个目录下,那么本地将无法接受这个实例。所以要求本地必须有这么一个dll存在,才能接受这个返回结果,然后根据本地的dll中定义的类的结构来反序列化。这样一来,不论如何使用proxy,其实在DefaultAppDomain里面都引用了具体的类和dll,所以导致dll是被当前程序占用的,无论如何都不能被替换。

欢迎讨论。



Feedback

#1楼   回复  引用  查看    

2005-10-19 08:47 by 风满袖      
假设主域M,子域C1,C2。通过代理之后,M可以调用C1和C2中的实例,反过来,如何让C1调用M中的实例呢?或者C1调用C2中的实例?

-------------
既然C1调用C2中的对象的话,那C1和C2就存在依赖关系,那根本就应该将C1和C2合并,不然你独立出来两个AppDomain有何意义?!分离出来多个AppDomain的意义就在于能够彼此独立,起到一种隔离的作用。

进程(AppDomain)间通信的方法有很多,TCP/IP还是非常快速的,你还可以用共享内存、管道等。

#2楼[楼主]   回复  引用  查看    

2005-10-19 08:56 by 随心所欲      
to 风满袖:
这是使用的场景:M中的实例是一个调度算法,C1,C2都是可以plug-in的应用。有时候C1和C2会发出一个命令,使用M调度其他的应用,C1和C2不会直接调用。



#3楼   回复  引用    

2005-10-19 09:24 by SAPikachu[未注册用户]
可不可以在C1、C2初始化时传入一个主域中实现的接口,要发出命令时就调用接口方法呢?

#4楼   回复  引用  查看    

2005-10-19 13:14 by QPG      
理解比较深入,难得看到的文章!
可以参考我的理解:
http://qpg2006.cnblogs.com/archive/2005/10/09/250809.html">http://qpg2006.cnblogs.com/archive/2005/10/09/250809.html

#5楼[楼主]   回复  引用  查看    

2005-10-19 14:18 by 随心所欲      
to SAPikachu :
非别在两个域里面,不可能设置引用。

to QPG :
在你的文章里好像没有类似的考虑

#6楼   回复  引用    

2006-05-08 16:16 by ego[未注册用户]
我晕了,用以下代码创建的Form,不能设置它的MdiParent,系统会报告序列化错误,有什么办法可以解决吗?
Form f = (Form)newDomain.CreateInstanceFromAndUnwrap(dllpath, className, false, bindings, null, args, null, null, null);
f.MdiParent = this

#7楼[楼主]   回复  引用  查看    

2006-05-08 16:34 by 随心所欲      
to ego :
如果你传递的参数不能序列化,那肯定会出错的。因为它需要把对象序列化成二进制,然后再还原回来。

不能序列化的对象,只能自己想办法了,或者使用简单参数代替。

#8楼   回复  引用    

2007-04-26 17:45 by yx79118[未注册用户]
直接用Spring。net实现不就行了,搞的这么麻烦

#9楼   回复  引用    

2007-08-24 17:08 by LoveVirus[未注册用户]
从内存中加载dll,或者建立一个临时目录,从临时目录里面加载



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 257425




相关文章:

相关链接:

Google