标签:domain void AppDomain System 应用程序 using Net
一、AppDomain的作用
AppDomain(应用程序域)是一种在.NET 中提供的隔离机制,用于将应用程序的不同部分隔离开来,以提高安全性、可靠性和可管理性。以下是其主要的功能和用途:
-
隔离性:
- 允许在一个进程中运行多个应用程序,同时使它们相互隔离。这样可以避免一个应用程序中的错误影响到其他应用程序,即使它们在同一个进程中运行。例如,在一个服务器进程中,可以运行多个不同的 Web 应用程序,它们可以在各自的 AppDomain 中运行,避免相互干扰。
- 每个 AppDomain 都有自己的加载程序集、配置和资源,这有助于防止不同应用程序之间的冲突。
-
资源管理:
- 可以对应用程序的资源进行独立的管理,包括内存的分配和使用。当一个 AppDomain 不再需要时,可以将其卸载,从而释放其所占用的资源,包括内存和其他系统资源。
- 有助于减少内存泄漏的风险,因为可以将不再使用的 AppDomain 及其相关资源清除。
-
安全性:
- 不同的 AppDomain 可以有不同的安全权限。这意味着可以对不同的 AppDomain 进行不同的安全设置,比如允许一个 AppDomain 访问网络资源,而另一个不允许,从而提高系统的整体安全性。
- 可以为 AppDomain 中的代码设置代码访问安全策略,限制代码的执行权限,确保代码不会执行未经授权的操作。
-
程序集加载和卸载:
- 可以在不同的 AppDomain 中加载和卸载程序集,这提供了一种灵活的方式来管理应用程序的动态行为。
- 允许在不影响其他 AppDomain 的情况下,更新或替换某个 AppDomain 中的程序集,这在需要动态更新程序的情况下非常有用,比如热更新应用程序的某些部分。
-
应用程序配置的独立性:
- 每个 AppDomain 可以有自己的配置信息,如配置文件(app.config)。这使得不同的应用程序可以根据自己的需要设置不同的配置,即使它们在同一个进程中运行。
二、使用AppDomain在一个进程创建两个应用
- 在
Main
函数中:
- 首先,使用
AppDomain.CreateDomain
创建了两个 AppDomain,分别命名为 "FirstAppDomain" 和 "SecondAppDomain"。
- 然后,创建了两个
Thread
对象 firstThread
和 secondThread
,每个线程都将在其自己的 AppDomain 中运行一个应用程序。
- 通过
new Thread(() => RunAppInDomain(domain, appType))
为每个线程指定要执行的操作,即调用 RunAppInDomain
方法并传递相应的 AppDomain 和应用程序类型。
- 使用
firstThread.Start()
和 secondThread.Start()
启动两个线程,这样它们会同时开始执行。
- 使用
firstThread.Join()
和 secondThread.Join()
等待两个线程完成。这样可以确保在卸载 AppDomain 之前,线程中的操作已经完成。
- 最后,使用
AppDomain.Unload
卸载两个 AppDomain。
- 在
RunAppInDomain
方法中:
- 使用
domain.CreateInstanceAndUnwrap
在指定的 AppDomain 中创建对象实例。
- 通过
appType.GetMethod("Run")
找到要调用的 Run
方法。
- 使用
method.Invoke
调用 Run
方法。
- 在
FirstApp
和 SecondApp
类中:
Run
方法中包含了打印信息和 Thread.Sleep
模拟应用程序的运行时间,你可以添加更复杂的逻辑。
这个示例允许两个应用程序在不同的 AppDomain 中同时运行,每个应用程序都有自己的资源和执行环境,并且它们不会相互干扰。通过将每个应用程序的执行放在单独的线程中,实现了并行处理。
需要注意的是:
- 当使用多线程时,要注意线程之间的同步和资源共享问题。在这个示例中,由于两个应用程序是隔离的,通常不会出现资源共享问题,但在更复杂的场景中,可能需要考虑线程安全。
- 卸载 AppDomain 时要确保其中的操作已经完成,否则可能会导致异常。使用
Join
方法可以帮助我们等待线程完成操作。
- 你可以根据实际需要修改
FirstApp
和 SecondApp
中的 Run
方法,添加更多的功能和逻辑。
using System;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
namespace AppDomainTest
{
internal class Program
{
static void Main(string[] args)
{
// 创建第一个 AppDomain
AppDomain firstDomain = AppDomain.CreateDomain("FirstAppDomain");
// 创建第二个 AppDomain
AppDomain secondDomain = AppDomain.CreateDomain("SecondAppDomain");
// 创建线程来运行第一个应用程序
Thread firstThread = new Thread(() =>
{
RunAppInDomain(firstDomain, typeof(FirstApp));
});
// 创建线程来运行第二个应用程序
Thread secondThread = new Thread(() =>
{
RunAppInDomain(secondDomain, typeof(SecondApp));
});
// 启动线程
firstThread.Start();
secondThread.Start();
// 等待线程完成
firstThread.Join();
secondThread.Join();
// 卸载 AppDomain
AppDomain.Unload(firstDomain);
AppDomain.Unload(secondDomain);
}
static void RunAppInDomain(AppDomain domain, Type appType)
{
// 在指定的 AppDomain 中创建对象实例
object instance = domain.CreateInstanceAndUnwrap(
appType.Assembly.FullName,
appType.FullName);
// 调用对象的方法
MethodInfo method = appType.GetMethod("Run");
if (method != null)
{
method.Invoke(instance, null);
}
}
}
[Serializable]
class FirstApp
{
public void Run()
{
// 创建一个 Windows 窗体应用程序
Form form = new Form();
form.Text = "First App";
form.Size = new System.Drawing.Size(300, 200);
form.Load += Form_Load;
Application.Run(form);
}
private void Form_Load(object sender, EventArgs e)
{
// 窗体加载时的操作
MessageBox.Show("第一个应用的窗体已加载");
// 可以添加更多逻辑
}
}
[Serializable]
class SecondApp
{
public void Run()
{
// 创建一个 Windows 窗体应用程序
Form form = new Form();
form.Text = "Second App";
form.Size = new System.Drawing.Size(300, 200);
form.Load += Form_Load;
Application.Run(form);
}
private void Form_Load(object sender, EventArgs e)
{
// 窗体加载时的操作
MessageBox.Show("第二个应用的窗体已加载");
// 可以添加更多逻辑
}
}
}
三、AppDomain的其他用法
1、程序集加载和卸载的更多细节
- 加载特定版本的程序集:
- 可以在不同的 AppDomain 中加载特定版本的程序集,避免不同版本的程序集冲突。例如:
收起
csharp
using System;
using System.Reflection;
class Program
{
static void Main()
{
AppDomain domain = AppDomain.CreateDomain("MyDomain");
try
{
domain.Load("MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
// 在这里可以使用在该 AppDomain 中加载的程序集
}
finally
{
AppDomain.Unload(domain);
}
}
}
- 上述代码使用
domain.Load
方法精确指定了要加载的程序集的版本号、区域性和公钥令牌,确保在该 AppDomain 中加载的是所需的特定版本程序集。
- 动态加载程序集并执行代码:
收起
csharp
using System;
using System.Reflection;
class Program
{
static void Main()
{
AppDomain domain = AppDomain.CreateDomain("DynamicDomain");
try
{
domain.AssemblyResolve += Domain_AssemblyResolve;
object instance = domain.CreateInstanceFromAndUnwrap("MyAssembly.dll", "MyNamespace.MyClass");
MethodInfo method = instance.GetType().GetMethod("MyMethod");
if (method!= null)
{
method.Invoke(instance, null);
}
}
finally
{
AppDomain.Unload(domain);
}
}
private static Assembly Domain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// 自定义程序集解析逻辑
return Assembly.LoadFrom("Path/To/MyAssembly.dll");
}
}
- 这里使用
domain.AssemblyResolve
事件添加了自定义的程序集解析逻辑,当程序集无法找到时,会调用 Domain_AssemblyResolve
方法,你可以根据需要从自定义位置加载程序集。
2、异常处理和隔离
- 隔离可能引发异常的代码:
- 将可能引发异常的代码放在单独的 AppDomain 中运行,避免影响主程序的执行。
收起
csharp
using System;
class Program
{
static void Main()
{
AppDomain domain = AppDomain.CreateDomain("ExceptionDomain");
try
{
domain.DoCallBack(ExceptionThrowingMethod);
}
catch (Exception ex)
{
Console.WriteLine($"Caught exception from another AppDomain: {ex.Message}");
}
finally
{
AppDomain.Unload(domain);
}
}
static void ExceptionThrowingMethod()
{
throw new Exception("This is an exception from another AppDomain");
}
}
- 在这个例子中,
ExceptionThrowingMethod
方法被放在新的 AppDomain 中执行,当它抛出异常时,不会直接导致主程序崩溃,而是可以在主程序中进行异常捕获。
3、监控资源使用
- 监控内存使用:
- 可以在 AppDomain 中监控内存使用情况,帮助优化性能。
收起
csharp
using System;
using System.Reflection;
class Program
{
static void Main()
{
AppDomain domain = AppDomain.CreateDomain("MemoryMonitorDomain");
try
{
domain.MonitoringIsEnabled = true;
// 执行一些操作
object instance = domain.CreateInstanceFromAndUnwrap("MyAssembly.dll", "MyClass");
// 可以调用一些方法等操作
long memoryUsed = domain.MonitoringSurvivedMemorySize;
Console.WriteLine($"Memory used in the AppDomain: {memoryUsed} bytes");
}
finally
{
AppDomain.Unload(domain);
}
}
}
- 通过将
domain.MonitoringIsEnabled
设置为 true
,可以启用对该 AppDomain 的内存使用监控,使用 domain.MonitoringSurvivedMemorySize
可以获取该 AppDomain 存活对象占用的内存大小。
4、跨 AppDomain 通信
- 使用 MarshalByRefObject 实现跨 AppDomain 通信:
收起
csharp
using System;
public class RemoteObject : MarshalByRefObject
{
public string GetMessage()
{
return "Hello from another AppDomain";
}
}
class Program
{
static void Main()
{
AppDomain domain = AppDomain.CreateDomain("CommunicationDomain");
try
{
RemoteObject remoteObj = (RemoteObject)domain.CreateInstanceAndUnwrap(typeof(RemoteObject).Assembly.FullName, typeof(RemoteObject).FullName);
string message = remoteObj.GetMessage();
Console.WriteLine(message);
}
finally
{
AppDomain.Unload(domain);
}
}
}
RemoteObject
继承自 MarshalByRefObject
,这允许它被跨 AppDomain 调用。当在主 AppDomain 中调用 GetMessage
方法时,实际的调用会被代理到另一个 AppDomain 中执行,实现了跨 AppDomain 的通信。
5、配置不同的安全策略
- 设置不同的安全权限:
收起
csharp
using System;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
class Program
{
static void Main()
{
Evidence ev = new Evidence();
ev.AddHostEvidence(new Zone(SecurityZone.Internet));
PermissionSet internetPermissionSet = SecurityManager.GetStandardSandbox(ev);
AppDomain domain = AppDomain.CreateDomain("SecureDomain", ev, new AppDomainSetup(), internetPermissionSet);
try
{
// 在安全受限的 AppDomain 中执行代码
}
finally
{
AppDomain.Unload(domain);
}
}
}
- 这里使用
Evidence
和 PermissionSet
为新创建的 AppDomain 配置了安全权限,使其只能在特定的安全范围内运行,例如将其权限限制为从 Internet 区域下载的代码的权限。
标签:domain,
void,
AppDomain,
System,
应用程序,
using,
Net
From: https://www.cnblogs.com/xietianjiao/p/18675470