应用程序域(AppDomain)已经不是一个新名词了,只要熟悉.net的都知道它的存在,不过我们还是先一起来重新认识下应用程序域吧,究竟它是何方神圣。

应用程序域

众所周知,进程是代码执行和资源分配的最小单元,每个进程都拥有独立的内存单元,而进程之间又是相互隔离的,自然而然,进程成为了代码执行的安全边界。

一个进程对应一个应用程序是一个普遍的认知,而.net却打破了这一惯例,因为它带来了应用程序域这一全新的概念,CLR可使用应用程序域来提供应用程序 之间的隔离,而一个进程中可以运行多个应用程序域,也就是说只要使用应用程序域,我们可以在一个进程中运行多个应用程序,而不会造成进程间调用或进程间切 换等方面的额外开销。

是不是觉得应用程序域是个很神奇的东东了,别急,我们再来看看它的隔离特性又为我们带来了什么。

优势

首先,应用程序域之间是不相互影响的,它是天生的异常隔离机制。也就是说,在一个应用程序域中出现的错误不会影响到其他应用程序域,因为类型安全的代码不会导致内存错误。

其次,它能够在运行时动态的加载和卸载程序集。我们都知道,在.net世界中,加载器一旦加载了程序集,那么它将一直存在于应用程序的整个生命周期中,而应用程序域则改变了这一切,它为我们提供了卸载程序集的能力。

最后,应用程序域可以单独实施安全策略和配置策略。说白了就是可以为每个应用程序域配置相应的权限,以更好的管理应用程序。

另外值得注意的是,应用程序域和线程之间不具有一对一的相关性。在任意给定时间,在单个应用程序域中可以执行多个线程,而特定线程并不局限在单个应用程序 域内。也就是说,线程可以自由跨越应用程序域边界,如果没有主动新启线程,那么多个应用程序域依然运行在同一个线程中。

总的来说,应用程序域形成了托管代码的隔离、卸载和安全边界。而这些特性带给一个插件式框架的将是异常隔离、动态加载卸载插件和更安全的插件运行环境。

由于这篇文章的定位是针对框架设计结合应用程序域的特性,因此假设你已经对应用程序域有了一定的了解了,下面通过示例,让我们一步一步来认识应用程序域的这些特性。

创建和卸载AppDomain

使用C#我们可以用如下的方式创建一个应用程序域,并在新域中执行一段代码:


AppDomain domain = AppDomain.CreateDomain("Hello AppDomain!");

            domain.DoCallBack(new CrossAppDomainDelegate(() =>

            {

                Window win =new Window

                {

                    Width =,

                    Height =,

                    Content = AppDomain.CurrentDomain.FriendlyName

                };

                win.Show();

            }));

运行后可以看到在新域中创建的Window展示如下:

卸载应用程序域则可以通过AppDomain静态方法AppDomain.Unload(domain)实现,就是这么简单。

配置域加载方式

如果你运行了上面这段代码,是不是发现新域创建的Window过了好久才呈现出来,这是怎么回事呢,简单来说,这是因 为.net加载器默认的行为是在每个域里都会重新加载引用的程序集(包括Framework本身除了mscorlib外的程序集),当然我们可以更改这种 行为,不过在这之前我们先来了解下一个新概念”domain neutrality”, 详细资料可以看这篇文章Domain Neutral Assemblies,简单来说它拥有跨域共享程序集的能力,这就避免了重复加载的损耗,我们可以通过为程序入口main函数添加LoaderOptimization标签修改默认加载方式:


[System.STAThreadAttribute()]

        [System.Diagnostics.DebuggerNonUserCodeAttribute()]

        [LoaderOptimization(

        LoaderOptimization.MultiDomainHost)]

        publicstaticvoid Main()

        {

            AppDomainTest.App app =new AppDomainTest.App();

            app.InitializeComponent();

            app.Run();

        }

重新编译运行,速度有了明显的提升吧。

LoaderOptimization有三种方式(SingleDomain, MultiDomainMultiDomainHost),在Domain Neutral Assemblies中均有详细的解译,有兴趣的朋友可以看下,此处就不再重述了。

异常隔离 

对于插件式框架而言,异常隔离是非常重要的,这是保证一个框架稳定性的必要特性。下面我们来看看使用应用程序域如何实现异常隔离。

首先我们来模拟在新创建的域中抛出异常:


AppDomain domain = AppDomain.CreateDomain("Hello AppDomain!");

            domain.DoCallBack(new CrossAppDomainDelegate(() =>

            {

                Window win =new Window

                {

                    Width =,

                    Height =,

                    Content = AppDomain.CurrentDomain.FriendlyName

                };

                win.Loaded += (obj, arg) =>

                {

                    thrownew Exception("test exception.");

                };

                win.Show();

            }));

这里采用的是在Window loaded事件中直接抛出异常达到模拟效果,OK,编译运行,很不幸,成功挂掉。

这是因为新域中未处理的异常,最终都会抛至默认域,进而导致崩溃。要验证这一点,很容易,我们只要在默认域中添加 AppDomain.CurrentDomain.UnhandledException事件处理就可以截获到新域中抛出的异常,可惜在此你只能截获却无 法改变崩溃的结果。

那么如何才能处理掉这个异常,在默认域或者新域中注册System.Windows.Threading.Dispatcher.CurrentDispatcher.UnhandledException事件处理,示例如下:


AppDomain domain = AppDomain.CreateDomain("Hello AppDomain!");
System.Windows.Threading.Dispatcher.CurrentDispatcher.UnhandledException += (obj, arg) =>
{
arg.Handled =true;
MessageBox.Show(arg.Exception.Message);
AppDomain.Unload(domain);
};
domain.DoCallBack(new CrossAppDomainDelegate(() =>
{
Window win =new Window
{
Width =,
Height =,
Content = AppDomain.CurrentDomain.FriendlyName
};
win.Loaded += (obj, arg) =>
{
thrownew Exception("test exception.");
};
win.Show();
}));

注意到最关键的arg.Handled = true这一句,它的意义在于告诉系统这个事件已经被处理过了,不要再往下传递了,最后主动把新域卸载掉,而默认域则仍然正常运行着,如此便达到了异常隔离的效果。

组合不同域中的插件 

假设所有的插件都处于不同的域中,那么如何组合它们呢,即如何将不同域中的插件同时呈现到一个容器中。

众所周知,要实现对象在域之间传递,对象必须是可序列化的或者是继承自MarshalByRefObject的类型,然而UI控件对此却是 无能为力了, 在此就需要微软的Addin框架帮助了,虽然大家都觉得Addin框架复杂、难用,但是里面有好些东西还是很有用处的,比如这里将要用到的 FrameworkElementAdapters类,它提供了两个静态方法ContractToViewAdapterViewToContractAdapter用于实现FrameworkElementINativeHandleContract之间的相互转换,传说中这种转换是通过句柄实现的。还是用例子来说明如何让插件跨域呈现吧,首先添加System.Addin.Contract.dll和System.Windows.Presentation.dll两个引用,然后编写如下代码


AppDomain domain = AppDomain.CreateDomain("test");
domain.DoCallBack(new CrossAppDomainDelegate(() =>
{
// 在新域中创建Button控件
Button btn =new Button { Content ="test" };
// 将Button控件转换为INativeHandleContract
INativeHandleContract ict = FrameworkElementAdapters.ViewToContractAdapter(btn);
AppDomain.CurrentDomain.SetData("testbtn", ict);
}));
// 在主域中获取新域中的INativeHandleContract对象
INativeHandleContract iContract = domain.GetData("testbtn") as INativeHandleContract;
// 将INativeHandleContract对象转换回FrameworkElement
FrameworkElement ctrl = FrameworkElementAdapters.ContractToViewAdapter(iContract);
Application.Current.MainWindow.Content = ctrl;

运行结果如下,新域中创建的控件成功的呈现在了主域中

应用程序域 z的更多相关文章

  1. 【Python】使用torrentParser1.03对多文件torrent的分析结果

    Your environment has been set up for using Node.js 8.5.0 (x64) and npm. C:\Users\horn1>cd C:\User ...

  2. 【.net 深呼吸】跨应用程序域执行程序集

    应用程序域,你在网上可以查到它的定义,凡是概念性的东西,大伙儿只需要会搜索就行,内容看了就罢,不用去记忆,更不用去背,“名词解释”是大学考试里面最无聊最没水平的题型. 简单地说,应用程序域让你可以在一 ...

  3. .NET应用程序域

    在.NET平台下,可执行程序并没有直接承载在Windows进程中,而非托管程序是直接承载的..NET可执行程序承载在进程的一个逻辑分区中,称之为应用程序域(AppDomain).一个进程可以包含多个应 ...

  4. Android立体旋转动画实现与封装(支持以X、Y、Z三个轴为轴心旋转)

    本文主要介绍Android立体旋转动画,或者3D旋转,下图是我自己实现的一个界面 立体旋转分为以下三种: 1. 以X轴为轴心旋转 2. 以Y轴为轴心旋转 3. 以Z轴为轴心旋转--这种等价于andro ...

  5. Z字形扫描(201412-2)

    问题描述 在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan).给定一个n×n的矩阵,Z字形扫描的过程如下图所示: 对于下面的4×4的矩阵, 1 5 3 9 3 7 5 ...

  6. 【IOS】将一组包含中文的数据按照#ABC...Z✿分组

    上一篇文章[IOS]模仿windowsphone列表索引控件YFMetroListBox里面 我们一步步的实现了WindowsPhone风格的索引. 但是有没有发现,如果你要实现按照字母排序,你还得自 ...

  7. Java 压缩/ 解压 .Z 文件

    1.问题描述 公司项目有需要用 JAVA 解压 .z文件. .z 是 unix 系统常见的压缩文件. 2.源码 import com.chilkatsoft.CkUnixCompress; impor ...

  8. 中文编程语言Z语言开源正式开源!!!

    (Z语言基于.NET环境,源码中有很多高技术的代码,让更多的人知道对大家有会有很好的帮助,请管理员一点要批准放在首页) 本人实现的中文编程语言Z语言现在正式开源,采用LGPL协议. 编译器核心的网址为 ...

  9. CCF——Z字形扫描问题

    试题编号: 201412-2 试题名称: Z字形扫描 时间限制: 2.0s 内存限制: 256.0MB 问题描述: 问题描述 在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag ...

随机推荐

  1. java新手笔记3 运算符&循环

    1.包 2.运算符 public class Operator { public static void main(String[] args) { int a = 5; System.out.pri ...

  2. 一些简单的帮助类(1)-- String的类型验证

    在工作中经常会遇到 验证String 中的值是否属于Int型或者是Bool又或是Date 一般的做法是用方法 类型.TryParse(string,类型) 来做验证. "; int intV ...

  3. [001] winnie the pooh - 读后记

    winnie the pooh 我是在伍君仪透析英语视频培训班,获得这本书的,PDF格式的(排版不是很好,和当当上的相比有部分章节缺失) 这是我第一本采用透析法读完的英文书. 今天(2015年10月2 ...

  4. 无责任共享 Coursera、Udacity 等课程视频

    本文转载自网络,原作者不详. (本文是用 markdown 写的,访问 https://www.zybuluo.com/illuz/note/71868 获得更佳体验) 程序语言 interactiv ...

  5. IDE开发<LER-Studio>(2)::登录模块

    软件中写登录模块是为了防止软件的恶意传播,内测阶段可以忽略登录. 以下为登录模块主要源代码: void CLoginDlg::OnBnClickedBtnLogin() { // TODO: Add ...

  6. 九度OJ 1108 堆栈的使用

    题目地址:http://ac.jobdu.com/problem.php?pid=1108 题目描述: 堆栈是一种基本的数据结构.堆栈具有两种基本操作方式,push 和 pop.Push一个值会将其压 ...

  7. 桥接模式(Bridge Pattern)

    桥接模式,用于将抽象化与实现化解偶,使得二者可以独立变化. 举一个数据库JDBC的例子: 定义一个Driver接口,不同的数据库实现的接口,如MySQL,SQLServer public interf ...

  8. 默认时,销毁会话,session_unset, session_destory

    <?php /** 一般我们登录时,开启了会话,就会自动生成 session 有关的文件, 保存有相关的用户登录信息,所以正常情况下得退出登录, 同时也要清空 session 有关的文件和相关的 ...

  9. sonar-maven-plugin错误2

    From maven-sonar-plugin 2.7, SonarQube < 4.5 is no longer supported. If using SonarQube instance ...

  10. 在后台代码中引入XAML的方法

    本文将介绍三种方法用于在后台代码中动态加载XAML,其中有两种方法是加载已存在的XAML文件,一种方法是将包含XAML代码的字符串转换为WPF的对象. 一.在资源字典中载入项目内嵌资源中的XAML文件 ...