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

应用程序域

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

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

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

优势

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

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

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

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

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

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

创建和卸载AppDomain

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

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

            domain.DoCallBack(new CrossAppDomainDelegate(() =>

            {

                Window win = new Window

                {

                    Width = 300,

                    Height = 100,

                    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)]

        public static void 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 = 300,

                    Height = 100,

                    Content = AppDomain.CurrentDomain.FriendlyName

                };

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

                {

                    throw new 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 = 300,
Height = 100,
Content = AppDomain.CurrentDomain.FriendlyName
};
win.Loaded += (obj, arg) =>
{
throw new 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;

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

至于域中的权限配置部分,将在下篇中讲述。

谨以此记录成长的脚步,同时和大家一起分享快乐。
 
http://www.cnblogs.com/huihui0630/archive/2010/06/30/1768387.html

【插件式框架探索系列】应用程序域(AppDomain)的更多相关文章

  1. 【插件式框架探索系列】使用多UI线程提升性能

    了解WPF线程模型的都知道,UI线程负责呈现和管理UI,而UI元素(派生自 DispatcherObject)只能由创建该元素的线程来访问,这就导致了一些耗时的UI操作将影 响到整个应用程序性能,未响 ...

  2. QT/C++插件式框架、利用智能指针管理内存空间的实现、动态加载动态库文件

    QT.C++插件式框架.主要原理还是 动态库的动态加载. dlopen()函数.下面为动态加载拿到Plugininstance对应指针.void**pp=(void**)dlsym(handle,&q ...

  3. [热拔插] 轻量级Winform插件式框架

    写在前面的话 对于大神,Winform这种“古董玩具”,实在没太多“技术性”可言了,然而『好用才是王道』,本文不以技术为卖点,纯属经验之谈,欢迎交流拍砖 朴素版UI 开发初衷 由于本人所在公司不定时需 ...

  4. Python3漏洞扫描工具 ( Python3 插件式框架 )

    目录 Python3 漏洞检测工具 -- lance screenshot requirements 关键代码 usage documents README Guide Change Log TODO ...

  5. 基于Python3的漏洞检测工具 ( Python3 插件式框架 )

    目录 Python3 漏洞检测工具 -- lance screenshot requirements 关键代码 usage documents Any advice or sugggestions P ...

  6. asp.mvc 插件式框架

    参考文档: http://blog.csdn.net/bitfan/article/details/17260775 http://www.cnblogs.com/Mainz/archive/2012 ...

  7. 基于AppDomain的"插件式"开发

    很多时候,我们都想使用(开发)USB式(热插拔)的应用,例如,开发一个WinForm应用,并且这个WinForm应用能允许开发人员定制扩展插件,又例如,我们可能维护着一个WinService管理系统, ...

  8. (1)从底层设计,探讨插件式GIS框架的实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 研一时,听当时的师兄推荐,买了蒋波涛的一本关于GIS插件框架的书.当时 ...

  9. 基于OSGI.NET的MVC插件式开发

    最近在研究OSGI.NET插件式开发框架.官方网站提供了一个基于OSGI.NET的插件仓库.下载官方的SDK包安装后VS项目模板会多出一组iOpenWorks项目模板.在学习过程中,发现通过iOpen ...

随机推荐

  1. Spring -09 -在Spring工程 中加载 properties 文件 -为某个属性添加注解赋初值

    1.在src 下新建 xxx.properties 文件,不要任意加空格,注明jdbc等标识名!2.在spring 配置文件中先引入xmlns:context,在下面添加2.1如果需要记载多个配置文件 ...

  2. 《3+1团队》第九次团队作业:Beta冲刺与验收准备

    1.团队项目github仓库地址链接 https://github.com/HaiYou667/3-1Growingfruits 2.Scrum meeting导航: [Beta]Scrum meet ...

  3. cookie,session,token介绍

    本文目录 发展史 Cookie Session Token 回到目录 发展史 1.很久很久以前,Web 基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记录谁在某一段时间里都浏览了什么文档 ...

  4. C++编译原理

    链接

  5. go语言-for循环

    一.for循环语法: for 循环变量初始化:循环条件:循环变量迭代{ 循环体 }案例: 打印10句hello 方式一 package main import "fmt" func ...

  6. vue引入js文件时报This dependency was not found:错误

    vue引入js文件时报This dependency was not found:错误 使用了很多方法,原来是这么小的问题,特此记录 解决办法 添加 ./

  7. Mysql 基础 1

    MySQL基础 1.概念 数据库,为我们提供高效.便捷的方式对数据进行增删改查的工具 优势 程序稳定.数据一致性.并发.效率 2.数据库管理系统 (DataBase Management System ...

  8. 2017.10.2 国庆清北 D2T2 树上抢男主

    /* 我只看懂了求LCA */ #include<iostream> #include<cstring> #include<cstdio> #include< ...

  9. 微信小程序--安装教程

    首先 奉上腾讯官方文档 方便参考:https://mp.weixin.qq.com/debug/wxadoc/design/index.html  个人认为没说啥特别有用的信息(可能是我看的太粗糙了) ...

  10. npm 更新包

    方法一手动跟新: 手动修改package.json中依赖包版本,执行npm install --force,强制从远程下载所有包更新本地包 方法二使用第三方插件: npm install -g npm ...