如何从程序集中加载及卸载插件(下)
2008-07-12 10:21
302 查看
继续我们上一章的讲解。现在我们用一个具体的程序示例来演示我们插件的加载及卸载过程,我们先回顾一下上一章中我们总结出来的一些思路:
建立一个新的AppDomain: AppDomain.CreateDomain()。
利用的AppDomain的实例,采用 CreateInstanceFromAndUnwrap() 方法在新的AppDomain中构建一个指定的类型,并返回相应的Proxy。
根据获取的Proxy就可调用插件了。
卸载时,完成一些资源清理后可以直接对新建出的AppDomain进行UnLoad
我们先来准备一下Visual Studio中的解决方案:
![](http://www.rogertong.cn/attachments/month_0807/t20087129154.jpg)
在上图的解决方案中,我们新建了三个工程,来看看他们具体的职责:
Unit9.Contract:包含插件的调用接口
Unit9.Implement:包含插件具体的实现
Unit9.App:主程序的代码,动态加载插件,并依据插件的调用接口对插件进行调用。
为什么要做这样的区分呢?因为对于主程序而言,它可以在运行时动态的加载多个插件,但主程序并不需要知道插件内部的具体实现细节。主程序只需要知道被调用插件相应的接口即可完成调用,因此我们有必要将调用规范及插件的具体实现分开。
插件的调用接口代码
namespace Unit9
{
public interface IAddin
{
string Run(string paramString);
}
}
定义出来上面的调用接口,我们接下来做插件功能的具体实现。在上章的讲述中,我们总结出了插件的实现必须符合两个规范:
必须继承自MarshalByRefObject。
插件中传递的参数类型及返回值必须是可序列化的。
因此依此来做具体的实现:
插件的实现代码
using System;
namespace Unit9
{
public class AddinOne:MarshalByRefObject,IAddin
{
public string Run(string paramString)
{
const string resultString =
"Current AppDomain:{0},Param String:{1}!";
return string.Format(
resultString,
AppDomain.CurrentDomain.FriendlyName,
paramString);
}
}
}
在上面的代码中,我们输出了一个AppDomain的信息。如前面所讲的,我们需要在一个新的AppDomain中加载插件的实现,因此这个AppDomain应该与系统默认的AppDomain应该是不相同的。接下来我们来看看主程序的代码:
C#代码
using System;
using System.IO;
using System.Runtime.Remoting.Lifetime;
namespace Unit9
{
class Program
{
/// <summary>
/// 构建一个AppDomainSetup实例
/// 用于启用卷影复制并设置基本路径
/// </summary>
public static AppDomainSetup CreateAppDomainSetup()
{
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.ShadowCopyFiles = "true";
return setup;
}
/// <summary>
/// 从当前目录下的指定的程序集文件中加载指定的类型
/// </summary>
public static object CreateAndUnwrap(AppDomain appDomain, string assemblyFile, string typeName)
{
string fullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyFile);
return appDomain.CreateInstanceFromAndUnwrap(fullName, typeName);
}
static void Main()
{
Console.WriteLine("Current AppDomain:{0}",
AppDomain.CurrentDomain.FriendlyName);
AppDomainSetup setup = CreateAppDomainSetup();
//建立准备加载插件的AppDomain
AppDomain secAppDomain = AppDomain.CreateDomain("SecAppDomain", null, setup);
//忽略新建立的AppDomain里面的调用租约管理
secAppDomain.DoCallBack(delegate
{
LifetimeServices.LeaseTime = TimeSpan.Zero;
});
IAddin addinOne = (IAddin) CreateAndUnwrap(
secAppDomain, "Unit9.Implement.dll", "Unit9.AddinOne");
Console.WriteLine(addinOne.Run("Test"));
//卸载装入插件的AppDomain
AppDomain.Unload(secAppDomain);
//由于插件所在的AppDmain已被卸载,所以以下的执行会出现异常
try
{
Console.WriteLine(addinOne.Run("Test"));
}
catch(Exception ex)
{
Console.WriteLine("发生调用异常:"+ex.Message);
}
Console.ReadLine();
}
}
}
如果大家对于AppDomain及Remoting有一定了解的话,上面的代码不难理解。我们建立了一个新的AppDomain,并开启了这个AppDomain的卷影复制功能,卷影复制功能其实是不一定需要的,只是这样做的话可以让程序运行时不至少锁住相关的程序集文件。接下来,我们取消了对新建立的AppDomain的租约管理,因为我们的插件是一个长效的服务。再接着,我们从指定的插件实现文件中加载了指定的服务,如果在这真正的插件架构现中,这个过程可以进行配置化的。我们看看输出的结果:
![](http://www.rogertong.cn/attachments/month_0807/42008712101922.jpg)
本文首发于:http://www.rogertong.cn/article.asp?id=24
如果大家对插件感兴趣的话,请阅读我们Mussel框架相关的文章
![](http://www.rogertong.cn/images/download.gif)
点击下载程序源文件
建立一个新的AppDomain: AppDomain.CreateDomain()。
利用的AppDomain的实例,采用 CreateInstanceFromAndUnwrap() 方法在新的AppDomain中构建一个指定的类型,并返回相应的Proxy。
根据获取的Proxy就可调用插件了。
卸载时,完成一些资源清理后可以直接对新建出的AppDomain进行UnLoad
我们先来准备一下Visual Studio中的解决方案:
![](http://www.rogertong.cn/attachments/month_0807/t20087129154.jpg)
在上图的解决方案中,我们新建了三个工程,来看看他们具体的职责:
Unit9.Contract:包含插件的调用接口
Unit9.Implement:包含插件具体的实现
Unit9.App:主程序的代码,动态加载插件,并依据插件的调用接口对插件进行调用。
为什么要做这样的区分呢?因为对于主程序而言,它可以在运行时动态的加载多个插件,但主程序并不需要知道插件内部的具体实现细节。主程序只需要知道被调用插件相应的接口即可完成调用,因此我们有必要将调用规范及插件的具体实现分开。
插件的调用接口代码
namespace Unit9
{
public interface IAddin
{
string Run(string paramString);
}
}
定义出来上面的调用接口,我们接下来做插件功能的具体实现。在上章的讲述中,我们总结出了插件的实现必须符合两个规范:
必须继承自MarshalByRefObject。
插件中传递的参数类型及返回值必须是可序列化的。
因此依此来做具体的实现:
插件的实现代码
using System;
namespace Unit9
{
public class AddinOne:MarshalByRefObject,IAddin
{
public string Run(string paramString)
{
const string resultString =
"Current AppDomain:{0},Param String:{1}!";
return string.Format(
resultString,
AppDomain.CurrentDomain.FriendlyName,
paramString);
}
}
}
在上面的代码中,我们输出了一个AppDomain的信息。如前面所讲的,我们需要在一个新的AppDomain中加载插件的实现,因此这个AppDomain应该与系统默认的AppDomain应该是不相同的。接下来我们来看看主程序的代码:
C#代码
using System;
using System.IO;
using System.Runtime.Remoting.Lifetime;
namespace Unit9
{
class Program
{
/// <summary>
/// 构建一个AppDomainSetup实例
/// 用于启用卷影复制并设置基本路径
/// </summary>
public static AppDomainSetup CreateAppDomainSetup()
{
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.ShadowCopyFiles = "true";
return setup;
}
/// <summary>
/// 从当前目录下的指定的程序集文件中加载指定的类型
/// </summary>
public static object CreateAndUnwrap(AppDomain appDomain, string assemblyFile, string typeName)
{
string fullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyFile);
return appDomain.CreateInstanceFromAndUnwrap(fullName, typeName);
}
static void Main()
{
Console.WriteLine("Current AppDomain:{0}",
AppDomain.CurrentDomain.FriendlyName);
AppDomainSetup setup = CreateAppDomainSetup();
//建立准备加载插件的AppDomain
AppDomain secAppDomain = AppDomain.CreateDomain("SecAppDomain", null, setup);
//忽略新建立的AppDomain里面的调用租约管理
secAppDomain.DoCallBack(delegate
{
LifetimeServices.LeaseTime = TimeSpan.Zero;
});
IAddin addinOne = (IAddin) CreateAndUnwrap(
secAppDomain, "Unit9.Implement.dll", "Unit9.AddinOne");
Console.WriteLine(addinOne.Run("Test"));
//卸载装入插件的AppDomain
AppDomain.Unload(secAppDomain);
//由于插件所在的AppDmain已被卸载,所以以下的执行会出现异常
try
{
Console.WriteLine(addinOne.Run("Test"));
}
catch(Exception ex)
{
Console.WriteLine("发生调用异常:"+ex.Message);
}
Console.ReadLine();
}
}
}
如果大家对于AppDomain及Remoting有一定了解的话,上面的代码不难理解。我们建立了一个新的AppDomain,并开启了这个AppDomain的卷影复制功能,卷影复制功能其实是不一定需要的,只是这样做的话可以让程序运行时不至少锁住相关的程序集文件。接下来,我们取消了对新建立的AppDomain的租约管理,因为我们的插件是一个长效的服务。再接着,我们从指定的插件实现文件中加载了指定的服务,如果在这真正的插件架构现中,这个过程可以进行配置化的。我们看看输出的结果:
![](http://www.rogertong.cn/attachments/month_0807/42008712101922.jpg)
本文首发于:http://www.rogertong.cn/article.asp?id=24
如果大家对插件感兴趣的话,请阅读我们Mussel框架相关的文章
![](http://www.rogertong.cn/images/download.gif)
点击下载程序源文件
相关文章推荐
- 如何从程序集中加载及卸载插件 转
- 如何从程序集中加载及卸载插件
- 如何从程序集中加载及卸载插件(上)
- 如何从程序集中加载及卸载插件
- C#.Net 如何动态加载与卸载程序集(.dll或者.exe)2----通过应用程序域AppDomain加载和卸载程序集之后,如何再返回原来的主程序域
- .NET: 如何通过AppDomain动态加载插件程序
- 在Qt中如何编写插件,加载插件和卸载插件
- 在Qt中如何编写插件,加载插件和卸载插件(转)
- .NET: 如何通过AppDomain动态加载插件程序
- 在Qt中如何编写插件,加载插件和卸载插件(转)
- 通过应用程序域AppDomain加载和卸载程序集之后,如何再返回原来的主程序域
- 如何用程序加载/卸载sys驱动
- Qt中如何 编写插件 加载插件 卸载插件
- 在Qt中如何编写插件,加载插件和卸载插件。
- Qt中如何 编写插件 加载插件 卸载插件
- C# 如何利用反射来加载程序集,并调用程序集中有关类的方法【转】
- C#.Net 如何动态加载与卸载程序集(.dll或者.exe)6-----在不卸载程序域的前提下替换程序集文件。
- C# 如何利用反射来加载程序集,并调用程序集中有关类的方法
- C#.Net 如何动态加载与卸载程序集(.dll或者.exe)6-----在不卸载程序域的前提下替换程序集文件。
- Qt中如何 编写插件 加载插件 卸载插件