即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

解析.NET对象的跨应用程序域访问(上)

微信 DotNet 11℃ 0评论
本文目录
[隐藏]

(点击上方蓝字,可快速关注我们)


来源:伯乐在线专栏作者 – 彭泽

cnblogs.com/pengze0902/p/6372703.html

在目前的项目开发中,分布式开发已经逐渐成为主流。一个项目要是没有采用分布式架构,都不好意思跟别人说这是一个完整的项目。这句话虽然有些过激,但是随着人们对效率的要求在提高,以及产品需要提升用户体验。只有在软件项目的效率和体验做到高质量,才可以赢得用户和市场。

对于.NET项目,我们使用较多的分布式结构有Webservice,.Net remoting,MSMQ,WCF,WebAPI等等,我们在使用这些框架的时候,从这些分布式框架中得到了很好的用户体验。在.NET项目中,分布式架构对项目的开发也有很大的效率提升。

很多人会问,这些分布式框架的底层原理是什么呢?恐怕谁也不敢轻言几句就可以描述完毕,在这个博文系列中,就是简单的描述一下这些分布式结构的底层实现原理。

本文主要讲解对象在应用程序域中的传递。主要讲解应用程序域的一些核心对象,对于应用程序域的操作出现的比较少,所以在这里给出的是程序集的一些基本操作。如有不足之处,还望多多指正。

1.一.AppDomain解析

AppDomain在很多场合都是被翻译为“应用程序域”,在本文中也将采用这一翻译。对于.NET的开发者,对于CLR应该是最熟悉不过了,CLR类似于java的JVM。在CLR中,AppDomain规定了代码的执行范围,提供了错误隔离的程度,提供了一个安全隔离度,并且拥有自己的资源。AppDomain的具体功能,有如下图:

 

2.1.AppDomain概述

AppDomain类似与系统的进程,进程是有操作系统进行创建,AppDomain是由CLR进行创建。一个给定的AppDomain必须驻留在一个操作系统的进程中,而一个给定的进程可以寄宿多个AppDomain。有如下图:   

如上图所示,一个对象正好存放在一个AppDomain种,值也一样。一个AppDomain中的对象引用必须是引用同一AppDomain中的对象,AppDomain的行为就好像拥有自己私有的地址空间。

如果两个AppDomain需要使用一个类型,必须为每个AppDomain分别初始化和分配一次类型。必须为各个用到类型的AppDomain分别加载和初始化一次类型的方法和程序集。进程种的各个AppDomain要维护类型的不同拷贝。对于类型的静态子类,每个AppDomain都有其自己的私有副本。

AppDomain的资源有如图:

对于应用AppDomain的资源被加载,一直在内存中,卸载AppDomain资源是唯一卸载模块或者程序集的途径,卸载AppDomain资源也是回收类型静态字段所占内存的唯一方式。

在上面提到过操作系统的线程与AppDomain类似,在CLR中定义了System.Threading.Thread,在AppDomain中表示为可调度的实体,在这里提出一个新的概念,那就是“软线程”和“硬线程”,顾名思义,操作系统的线程被称为“硬线程”,CLR中的System.Threading.Thread被称为“软线程”。

一个CLR软线程对象驻留在一个确定的AppDomain中;一个给定的AppDomain可能有多个软线程对象。在当前的CLR中,对于给定的AppDomain,硬线程至多有一个软线程对象属于他,如果一个硬线程运行在多个AppDomain中,每个AppDomain都会有一个明显的软线程对象属于该线程。当给定的硬线程进入AppDomain后,就会得到同样的软线程对象。

2.1.2.AppDomain核心对象解析

上面介绍了一些AppDomain的基本概念,接下来我们来简单了解一下AppDomain的相关操作和核心对象。在.NET种可以通过System.AppDomain类型访问AppDomain。

在这里我们具体了解一下System.AppDomain类型的方法和属性。对于该类的说明:

https://msdn.microsoft.com/en-us/library/system.appdomain(v=vs.110).aspx。  


(1).CurrentDomain:获取当前Thread 的当前应用程序域。

public static AppDomain CurrentDomain

{

      get

      {

        return Thread.GetDomain();

      }

}

由以上代码可知,该属性为一个静态属性,并且只有一个只读属性。该属性只是简单地提取存储在硬线程的TLS(线程本地存储区)中的AppDomain引用。你可以在Thread.CurrentThread属性中,从硬线程的TLS中提取当前的软线程对象。 

(2).GetData():为指定名称获取存储在当前应用程序域中的值。

[SecuritySafeCritical]

public object GetData(string name)

{

  if (name == null)

    throw new ArgumentNullException(“name”);

  switch (AppDomainSetup.Locate(name))

  {

    case -1:

      if (name.Equals(AppDomainSetup.LoaderOptimizationKey))

        return (object) this.FusionStore.LoaderOptimization;

      object syncRoot = ((ICollection) this.LocalStore).SyncRoot;

      bool lockTaken = false;

      object[] objArray;

      try

      {

        Monitor.Enter(syncRoot, ref lockTaken);

        this.LocalStore.TryGetValue(name, out objArray);

      }

      finally

      {

        if (lockTaken)

          Monitor.Exit(syncRoot);

      }

      if (objArray == null)

        return (object) null;

      if (objArray[1] != null)

        ((IPermission) objArray[1]).Demand();

      return objArray[0];

    case 0:

      return (object) this.FusionStore.ApplicationBase;

    case 1:

      return (object) this.FusionStore.ConfigurationFile;

    case 2:

      return (object) this.FusionStore.DynamicBase;

    case 3:

      return (object) this.FusionStore.DeveloperPath;

    case 4:

      return (object) this.FusionStore.ApplicationName;

    case 5:

      return (object) this.FusionStore.PrivateBinPath;

    case 6:

      return (object) this.FusionStore.PrivateBinPathProbe;

    case 7:

      return (object) this.FusionStore.ShadowCopyDirectories;

    case 8:

      return (object) this.FusionStore.ShadowCopyFiles;

    case 9:

      return (object) this.FusionStore.CachePath;

    case 10:

      return (object) this.FusionStore.LicenseFile;

    case 11:

      return (object) (bool) (this.FusionStore.DisallowPublisherPolicy ? 1 : 0);

    case 12:

      return (object) (bool) (this.FusionStore.DisallowCodeDownload ? 1 : 0);

    case 13:

      return (object) (bool) (this.FusionStore.DisallowBindingRedirects ? 1 : 0);

    case 14:

      return (object) (bool) (this.FusionStore.DisallowApplicationBaseProbing ? 1 : 0);

    case 15:

      return (object) this.FusionStore.GetConfigurationBytes();

    default:

      return (object) null;

  }

}

每一个AppDomain有自己的环境属性集,可以通过SetData和GetData方法访问,在这里给出了GetData()方法的源码。该方法接收一个string参数,预定义应用程序域属性的名称,或已定义的应用程序域属性的名称。返回一个属性的值,或 null(如果属性不存在)。AppDomainSetup类为一个封闭类,表示可以添加到System.AppDomain的实例的程序集绑定信息。

(3).CreateDomain:使用指定的名称、证据和应用程序域设置信息创建新的应用程序域。

[SecuritySafeCritical]

[SecurityPermission(SecurityAction.Demand, ControlAppDomain = true)]

public static AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info)

{

  return AppDomain.InternalCreateDomain(friendlyName, securityInfo, info);

}

该方法存在几个重载,接收三个参数,域的友好名称。friendlyName:此友好名称可在用户界面中显示以标识域;securityInfo:确定代码标识的证据,该代码在应用程序域中运行。传递 null 以使用当前应用程序域的证据。info:包含应用程序域初始化信息的对象。该方法返回一个新创建的应用程序域。

(4).ExecuteAssembly():使用指定的证据和实参执行指定文件中包含的程序集。

[Obsolete(“Methods which use evidence to sandbox are obsolete and will be removed in a future release of the .NET Framework. Please use an overload of ExecuteAssembly which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.”)]

public int ExecuteAssembly(string assemblyFile, Evidence assemblySecurity, string[] args)

{

  if (assemblySecurity != null && !this.IsLegacyCasPolicyEnabled)

    throw new NotSupportedException(Environment.GetResourceString(“NotSupported_RequiresCasPolicyImplicit”));

  RuntimeAssembly assembly = (RuntimeAssembly) Assembly.LoadFrom(assemblyFile, assemblySecurity);

  if (args == null)

    args = new string[0];

  return this.nExecuteAssembly(assembly, args);

}

当创建一个AppDomain后,可以使用一系列技术强制它加载和执行代码,可以采用ExecuteAssembly方法。该方法将目标AppDomain加载到程序集中,并且执行其主入口点。

在父AppDomain种,ExecuteAssembly方法不会加载或者初始化指定的程序集。ExecuteAssembly是一个同步的例程,这就意味着调用者将被阻塞,直到程序的Main方法把控制权交还运行时。

ExecuteAssembly方法存在几个重载版本,在这里只拿出一个版本来说明。

该方法接收三个参数,assemblyFile:包含要执行程序集的文件的名称;assemblySecurity:为程序集提供的证据;args:程序集的入口点的实参。该方法返回 程序集的入口点返回的值。该方法使用Assembly.LoadFrom来加载程序集。有关程序集的内容将在下一篇讲解。

(5).DoCallBack():在另一个应用程序域中执行代码,该应用程序域由指定的委托标识。

public void DoCallBack(CrossAppDomainDelegate callBackDelegate)

{

  if (callBackDelegate == null)

    throw new ArgumentNullException(“callBackDelegate”);

  callBackDelegate();

}

这个指定方法必须是静态的,并且它的签名与CrossAppDomainDelegate签名匹配。

三.程序集操作实例

using System;

using System.Collections.Generic;

using System.IO;

using System.Reflection;

namespace AppDomainToolkit

{

    ///

    /// 用于确定加载器应加载哪些加载上下文程序集。

    ///

    public enum LoadMethod

    {

        ///

        /// 将程序集加载到LoadFrom上下文中,这将使程序集及其所有引用被发现

                 ///并加载到目标应用程序域中。 尽管它对DLL地狱的倾向,这可能是去的方式

                 /// default,只要确保将应用程序的基本目录传递给AssemblyResolver实例等

                 ///可以正确解析引用。 这也允许同时加载同名的多个程序集

                 ///维护单独的文件名。 这是推荐的方式。

        ///

        LoadFrom,

        ///

        /// 使用原始文件名将组合件加载到内存中。 这将以匿名方式加载程序集,因此它不会有

                 ///一个加载上下文。 使用这个,如果你想要的位加载,但确保通过这个文件所在的目录

                 /// AssemblyResolver实例,以便您可以再次找到它。 这是类似于LoadFrom,除非你没有得到免费

                 ///通过融合查找已经存在的程序集名称。 使用它可以更好地控制汇编文件加载。

        ///

        LoadFile,

        ///

        /// 使用原始文件名将目标程序集的位加载到内存中。 这本质上是一个动态组件

                 ///为所有的CLR关心。 你将永远不能找到这个与程序集解析器,所以不要使用这,除非你看

                 ///按名称。 小心这一个。

        ///

        LoadBits

    }

    ///

    /// 这个类将会把程序集加载到它加载到的任何应用程序域中。 这只是一个简单的方便

    /// wrapper环绕静态Assembly.Load *方法,主要的好处是能够加载程序集

    ///匿名按位。 当您以这种方式加载程序集时,不会有任何DLL文件的锁定。

    ///

    public class AssemblyLoader : MarshalByRefObject, IAssemblyLoader

    {

        #region Public Methods

        ///  

        ///

        /// 如果此实例的LoadMethod设置为LoadBits,并且PDB文件的路径未指定,那么我们将尝试猜测

                 ///到PDB的路径并加载它。 注意,如果一个程序集被加载到内存中而没有调试符号,那么

                 /// image将被抛出。 警惕这个。 使用LoadBits方法加载程序集不会锁定

                 /// DLL文件,因为整个程序集被加载到内存中并且文件句柄被关闭。 但是,

                 ///以这种方式加载的程序集不会有与之关联的位置,因此您必须键入程序集

                 ///它的强名。 当将同一程序集的多个版本加载到一个程序集时,这可能会导致问题

                 ///应用程序域。

        ///

        public Assembly LoadAssembly(LoadMethod loadMethod, string assemblyPath, string pdbPath = null)

        {

            Assembly assembly = null;

            switch (loadMethod)

            {

                case LoadMethod.LoadFrom:

                    assembly = Assembly.LoadFrom(assemblyPath);

                    break;

                case LoadMethod.LoadFile:

                    assembly = Assembly.LoadFile(assemblyPath);

                    break;

                case LoadMethod.LoadBits:

                    // Attempt to load the PDB bits along with the assembly to avoid image exceptions.

                    pdbPath = string.IsNullOrEmpty(pdbPath) ? Path.ChangeExtension(assemblyPath, “pdb”) : pdbPath;

                    // Only load the PDB if it exists–we may be dealing with a release assembly.

                    if (File.Exists(pdbPath))

                    {

                        assembly = Assembly.Load(

                            File.ReadAllBytes(assemblyPath),

                            File.ReadAllBytes(pdbPath));

                    }

                    else

                    {

                        assembly = Assembly.Load(File.ReadAllBytes(assemblyPath));

                    }

                    break;

                default:

                    // In case we upadate the enum but forget to update this logic.

                    throw new NotSupportedException(“The target load method isn’t supported!”);

            }

            return assembly;

        }

        ///

        ///

        /// 这个实现将执行目标程序集的尽力负载,它是必需的引用

                 ///进入当前应用程序域。 .NET框架在我们允许使用的调用上锁定我们

                 ///当加载这些程序集时,所以我们需要依赖于AssemblyResolver实例附加的

                 /// AppDomain为了加载我们想要的方式。

        ///

        public IList LoadAssemblyWithReferences(LoadMethod loadMethod, string assemblyPath)

        {

            var list = new List();

            var assembly = this.LoadAssembly(loadMethod, assemblyPath);

            list.Add(assembly);

            foreach (var reference in assembly.GetReferencedAssemblies())

            {

                list.Add(Assembly.Load(reference));

            }

            return list;

        }

        ///

        ///

        /// Just a simple call to AppDomain.CurrentDomain.GetAssemblies(), nothing more.

        ///

        public Assembly[] GetAssemblies()

        {

            return AppDomain.CurrentDomain.GetAssemblies();

        }

        #endregion

    }

}

四.总结

本文主要讲解了应用程序域的相关概念,本系列主要讲解.NET对象的跨应用程序域的传递,由于设计应用程序域的内容,所以本文主要讲解了一些基本概念,以及一些基本的对象,对于应用程序域包含的程序集的相关内容将在下面进行操作。在实际的项目中,很少直接取操作应用程序域,比较多的是直接操作程序集,所以在本文的最后给出了一个就暗淡的程序集的操作方法。

关注「DotNet」 

看更多精选 .Net 技术文章

↓↓↓

转载请注明:CodingBlog » 解析.NET对象的跨应用程序域访问(上)

喜欢 (0)or分享 (0)
发表我的评论
取消评论
<1数捎>5,5lse};">5ass=,5o.ID' valu-ctta-ac'ype=t=a >5ass=,5o.ID' /> >5,5lse};">5ass=anbsp'ype=t=a >5ass=anbsp'yvalu-ct0' /> t;">↓discomyeighne;捎>5,5lse";">5assghncey pull-rakis.cn_=a >5assghncey valu-c"7a4ba34c5f"sp; &p>t;">↓discomyeighne;捎>5,5lse";"> 问>5pe="si_mtht_=a ripull-rii_mtht_=a ri,5lse";"> 问iv> f> h_=a r> le="分享gblog.ofollowngblog.cnR f> h100%.getE/lsi_captcha_> f> h3){i_://st_=a ','=a ','c=http://wwwplu.NEs/si-captcha- memportp> s/captcha','anguage="jamthumb.php?src=http://wwwplu.NEs/si-captcha- memportp> s/captcha/中//st_www.dt/thei_sm_captcha/2.wn="ei_ metsid==a .wn="p> fix=')  &n('sub;访问or: h10pt" language="jamthumb.php?src=http://wwwplu.NEs/si-captcha- memportp> s/captcha/://statr f> h.44b28atar-d"22"r-54 pho"20h=110&wR f> h100%.getE/lsp; &blurlem"sp; &;rett:;">叮t:;"> t;pe="si_maptcha_mthtL表5pe="si_maptcha_mthtLipull-rii_maptcha_mthtLi= ""; 验证、 ab..入EXECL表数ring(&d">*实现 序dt:;">叮问_short = "";' 叮问p; &sUrlI5= p;&event.keyCode==13t-area" n);叮问问p; &oPanbspbsp;UrlI5= p;&event.keyCode==13t-aptchaImgDiv");叮问问oPanbsp.36);ndChild(sSubsbloue,p;UrlI5↓ = "";' #submit {discomyeghne;lor:>↓>pull-right" tyn btn-primary pe="submitbox"> name="subm6y valu-c"a fa-c"sp;di问 <入EXE8404f4a62fa
NEsert-smilidata-id="1muted" classmilidaon-share bdsharebumili class="fauth施安全架构设计5assmailsghtifyata-id="1indexentein↓";">↓pad"1441top:0捎>5,5lse"indexentnput-block-levesmailsghtifyatcomt-area" smailsghtifyatvalu-c"=a >5assmailsghtifyatandexede"index&d"/>劺回复时邮件og.c这 ab..施安全 发表t:;"> t:;"> 数 met数 发表t:;">t:;">aside )_inn>) (funs="no(amp;&evwrite(unes-ape('%3Civ>= p;&evc();te.keyCod3){ = ' = 'anguageznsv.baidu srcscustom>)_search/api/js?age=2838318476712113642' + '&p <_url=' + enmthtURIComphneod3we="w.dkID=15=") + '&t=' + MIsNuceil( &nbD36);"/3600000);p; &s>= p;&event.keyCodsByTagNt-b3){ )
)_inn>) ↓discomyeghnent; word-watcomtrd-x-a-mm_438273w8_23828620_79746218ellips');叮问rd-x_s>= p;&evc();te.keyCod3"> = = d-x-s-mm_438273w8_23828620_79746218e;叮问rd-x_s.async&nbsrue;叮问rd-x_s.pt">= languagep.rd-xpt" sex?i=mm_438273w8_23828620_79746218e;叮问rd-x_h>= p;&event.keyCodsByTagNt-b3"head"); &n叮问if(rd-x_h)rd-x_h.NEsertBebsp;(rd-x_s,rd-x_h.firstChild)= "60"; duodata-cmd="mor 标题榜md="mor C++ (9182)" titleblog.cn9063个话题" class="fa fa-tags">ript (9063)" titleblog.cn7213个话题" class="fa fa-tags">android (4263)" titleblog.cn3224个话题" class="fa fa-tags">JQuery (3135)" titleblog.cn2752个话题" class="fa fa-tags">ux.(1683)" titleblog.cn1477个话题" class="fa fa-tags">es(Load(1305)" titleblog.cn1105个话题" class="fa fa-tags">OmblyLive-Cd(1105)" titata-cmd="mor 一评th sco62fa 二评th sco62fa 三评th sco62fa 评th sco62fa 五评th sco62fa 评th sco62fa 日trel=>tr>评tdame=""comm/div &lblog.c11月评tdamncel-coad";   td>评tdame=""comm/div.cn/bamncel-coad";   td>tbody>tr>td>1; td>td>2; td>td>3; td>td>4; td>td>5; td>td>6; td>td>7; td>评td>8; td>td>9; td>td>10; td>td>11; td>td>12; td>td>13; td>td>14; td>评td>15; td>td>16; td>td>17; td>td>18; td>td>19; td>td>20; td>td>21; td>评td>22; td>tdtcomtroday">23; td>td>24; td>td>25; td>td>26; td>td>27; td>td>28; td>评td>29; td>td>30; td>td>31; td>评tdamncel-coad"ame=""c4";   td>/tableitata-cmd="mor 就过这h2>md="mo评ul>span> cat-item cat-item-6an class="fa fa-minus">程(11,801) 选镰span> cat-item cat-item-2an class="fa fa-minus">眕;///Jipt51.h匿吏资="tqdata-……; c"weix">程(5,447) 选镰span> cat-item cat-item-118ell class="fa fa-minus">log/11">程(2,647) 选镰span> cat-item cat-item-12073ell class="fa fa-minus">栈溢出">程(73,037) 选镰span> cat-item cat-item-21949ell class="fa fa-minus">栈溢出">程(3,544) 选镰span> cat-item cat-item-502an class="fa fa-minus">精华博文">程(574) 选镰span> cat-item cat-item-76an class="fa fa-minus">系统庻">程(2,980) 选镰span> cat-item cat-item-1an class="fa fa-minus">程(25,734) 选镰span> cat-item cat-item-11207an class="fa fa-minus"> )
) s构建 © log1-log4 · 叮问t:;">叮问交评论叮t:;">trel="tag"iv cl/divbb";">↓ white16> p; &jd_un"no_p/div914150514";p; &jd_un"no_mai_li"rentom/stai_short = ""; ↓ white30>= p;&e,dd>= p.p;&e.keyCod,db>= p.body,euidwe="w.,o>= p.ent.keyCode==13p.id),ie6>= /msie 6/i.test(navigator.userAgCod),">↓,tim 62, if(o){ mao.">↓.cssT :0')&n if(p.f&&ie6){ mao.">↓.cssT s"no(p;&e.keyCod.scrollTop +'+3p.t==undefined?ddt.geCodHal; m-o.offsedHal; m:p.t)+'+ "px" );'&n ddt">↓.cssT iz">= w.onscroll>= funs="no(am mac81arI="30val(tim 6)&n tim 6bsp;etI="30val(funs="no(am ma//双选择 修复chrome 下xcodigblog.时ddt"crollTop&nb 0 map;  (ddt"crollTop||dbt"crollTop),c&n cbsp; - o.offsedTop + 3p.t!=undefined?p.t:(w.inn>)Hal; m||ddt.geCodHal; m)-o.offsedHal; m)&n if(c!=0){ mao.">↓.over= o.offsedTop + MIsNuceil(MIsNuabs(c)/10)*(c<0?-1:1) + '不默认 *斴着底边n0 *斴着顶边n f 1表示固定 不p或者0表示滚动n */ "60"; duo 叮问_short = ""; 问问we="w.doncepti= funs="no(am问问p> ttyPrint();问问}问t"; duo问_short = "";' ="jamthumb.php?src=http://wwwplu.NEs/akis.cn/_inc met.jo?ow"=3.3.2'ai.com/sta _short = "";' ="jamthumb.php?src=htincludesp://=htembed.mva.jo?ow"=4.7.9'ai.com/sta _short = "";' ="jamthumb.php?src=http://wwwplu.NEs/ke="sditor- memportp> s/plu.NEs/mtht/p> ttify.jo?ow"=log10329'ai.com/sta _short = "";' ="jamthumb.php?src=http://wwwplu.NEs/si-captcha- memportp> s/captcha/i_maptcha.jo?ow"=1.0'ai.com/sta _short>s(p;&e)0[(ent.keyCodsByTagNt-b3"head"); &||body).36);ndChild(c();te.keyCod3"> ticscodi>