zl程序教程

您现在的位置是:首页 >  .Net

当前栏目

C# 应用程序域

2023-04-18 15:04:04 时间

在.NET中,每个应用程序域都是一个独立的执行环境,有自己的安全边界和上下文。当使用AppDomain.ExecuteAssembly方法在一个新的应用程序域中执行一个程序集时,这个程序集将会在新的应用程序域中加载和执行,而与原应用程序域隔离开来,两个应用程序域之间的交互是受到限制的。

默认情况下,新应用程序域中的程序集无法访问原应用程序域中的对象和资源。如果需要在不同的应用程序域之间进行通信,需要使用.NET提供的跨应用程序域通信(Cross-AppDomain Communication)机制,例如MarshalByRefObject和AppDomain.UnhandledException事件等。

如果需要在新应用程序域中执行某些操作并将结果返回给原应用程序域,可以使用Remoting或WCF等技术。其中,Remoting提供了一种透明的远程调用机制,可以使得应用程序域之间的调用像本地调用一样简单;而WCF则提供了更加通用的跨进程和跨网络通信机制,可以支持多种协议和格式,具有更高的灵活性和扩展性。

总之,在.NET中,新的应用程序域与原应用程序域之间的交互需要通过特定的机制来实现,具体实现方式取决于具体的场景和需求.
在.NET中,被调用的目标程序集必须是可执行的,这是因为在.NET中,应用程序域的基本单元是可执行程序集(Assembly),而非单个类或对象。应用程序域是.NET提供的一种隔离机制,可以在同一个进程中加载和执行多个不同的可执行程序集,从而实现应用程序的模块化和隔离。

当在应用程序域中调用一个程序集时,CLR会将其加载到应用程序域中,并在其中创建一个或多个AppDomain对象。这个AppDomain对象将会在应用程序域的边界内隔离程序集的执行环境,保证程序集的运行不会对应用程序域之外的其他部分产生影响,从而提高应用程序的安全性和可靠性。

因此,为了能够在应用程序域中加载和执行一个程序集,这个程序集必须是可执行的,即符合.NET可执行程序集的规范,包含符号表、元数据和IL代码等信息。只有这样,CLR才能够正确地解析和执行这个程序集,并将其隔离在应用程序域内部的执行环境中,同时与其他程序集和应用程序域进行交互和通信。

总之,在.NET中,被调用的目标程序集必须是可执行的,才能够被加载到应用程序域中进行隔离和执行。
在.NET中,AppDomain类提供了一个DoCallBack方法,可以在当前应用程序域中调用指定委托(Delegate)的方法,并将其委托到另一个应用程序域中执行。这个方法通常用于实现应用程序域之间的通信和协作,可以在不同的应用程序域之间传递数据和执行代码,从而实现应用程序的分布式和模块化。

public void DoCallBack(CrossAppDomainDelegate callBackDelegate);

其中,callBackDelegate参数是一个CrossAppDomainDelegate委托,表示要在另一个应用程序域中执行的方法。该委托的参数和返回值必须是序列化和可传递的类型,否则将无法在应用程序域之间进行传递。
使用DoCallBack方法在新应用程序域中执行一个委托。这个委托输出了当前应用程序域的名称,并通过AppDomain.SetData方法将数据传递回主应用程序域。需要注意的是,在使用DoCallBack方法时,委托中的代码将会在另一个应用程序域中执行,因此需要考虑到数据和代码的传递和隔离问题。同时,由于跨应用程序域调用涉及到数据序列化和传输,因此可能会对性能和可靠性产生一定的影响。

AppDomain newDomain = AppDomain.CreateDomain("JohnYang New Domain");
            
            Console.WriteLine(AppDomain.CurrentDomain.FriendlyName+","+AppDomain.CurrentDomain.IsDefaultAppDomain());
            newDomain.SetData("Data", "Hello in new Domain");//在新应用程序域中设置槽
            newDomain.DoCallBack(() =>
            {
                //这些代码在newDomain中执行
                Console.WriteLine($"this is in {AppDomain.CurrentDomain.FriendlyName},{AppDomain.CurrentDomain.IsDefaultAppDomain()}");
                
                var data = AppDomain.CurrentDomain.GetData("Data");//newDomain自身设置Data
                Console.WriteLine($"new domain get data:{data}");//newDomain自身获取Data
            });
            //主程序域获取不到该项
            Console.WriteLine($"{AppDomain.CurrentDomain.FriendlyName}获取"+AppDomain.CurrentDomain.GetData("Data"));
            AppDomain.Unload(newDomain);
            Console.ReadLine();

output:

Learn.exe,True
this is in JohnYang New Domain,False
new domain get data:Hello in new Domain
EasyBimBackend.exe获取

以下代码证明,独立应用程序域中,每个应用程序域都有自己的内存空间和运行环境,不同的应用程序域之间相互隔离,无法直接相互访问。因此,即使是同一个类的静态成员,也不能在不同的应用程序域之间相互访问。

class ProgramA
    {
        public static int count;
        static void Main()
        {
            var domain = AppDomain.CreateDomain("JohnYang new domain");
            domain.DoCallBack(() =>
            {
                ProgramA.count = 100;
                
            });
            Console.WriteLine(ProgramA.count);//打印0
            Console.ReadLine();
        }
}

在 MarshalByRefObject 对象之间进行远程调用时,数据流通常是单向的,即从客户端应用程序域到服务器应用程序域。但是,通过将一个 MarshalByRefObject 对象传递给另一个 MarshalByRefObject 对象的方法作为参数,可以实现远程过程调用的逆向。

具体来说,当 FooA 对象的方法接受一个 MarshalByRefObject 参数时,可以将另一个可远程访问的对象作为参数传递进来。这个参数对象在客户端应用程序域中创建,但是由于它继承自 MarshalByRefObject,所以客户端应用程序域中的代理对象将会在服务器应用程序域中创建一个真实的对象,然后将该对象作为参数传递给服务器应用程序域中的方法。

因此,逆向的 Remoting 过程是指在远程过程调用中,数据流向是从服务器应用程序域到客户端应用程序域。这使得客户端应用程序可以向服务器应用程序传递对象,从而实现更灵活和动态的交互。

public class FooA : MarshalByRefObject
    {
        public string SayHello(FooB b) => $"Hello from {AppDomain.CurrentDomain.FriendlyName}. FooB says: {b.SayHello()}";
    }

    public class FooB : MarshalByRefObject
    {
        public string SayHello() => $"Hello from {AppDomain.CurrentDomain.FriendlyName}";
    }
    class ProgramA
    {
        static void Main()
        {
            AppDomain newDomainA = AppDomain.CreateDomain("new domain A");
            AppDomain newDomainB = AppDomain.CreateDomain("new domain B");
            
            FooA fooA = (FooA)newDomainA.CreateInstanceAndUnwrap(typeof(FooA).Assembly.FullName, typeof(FooA).FullName);
            //FooB fooB = (FooB)newDomainB.CreateInstanceAndUnwrap(typeof(FooB).Assembly.FullName, typeof(FooB).FullName);
            FooB fooB = new FooB();
            Console.WriteLine(fooA.SayHello(fooB)); // 在客户端应用程序域中调用FooA对象的方法,并将FooB对象作为参数传递进去
            AppDomain.Unload(newDomainA);
            AppDomain.Unload(newDomainB);
            Console.ReadLine();
         
        }

    }

output:

Hello from new domain A. FooB says: Hello from Learn.exe
public class FooA : MarshalByRefObject
    {
        public string SayHello(FooB b) => $"Hello from {AppDomain.CurrentDomain.FriendlyName}. FooB says: {b.SayHello()}";
    }

    public class FooB : MarshalByRefObject
    {
        public string SayHello() => $"Hello from {AppDomain.CurrentDomain.FriendlyName}";
    }
    class ProgramA
    {
        static void Main()
        {
            AppDomain newDomainA = AppDomain.CreateDomain("new domain A");
            AppDomain newDomainB = AppDomain.CreateDomain("new domain B");
            
            FooA fooA = (FooA)newDomainA.CreateInstanceAndUnwrap(typeof(FooA).Assembly.FullName, typeof(FooA).FullName);
            FooB fooB = (FooB)newDomainB.CreateInstanceAndUnwrap(typeof(FooB).Assembly.FullName, typeof(FooB).FullName);
            //FooB fooB = new FooB();
            Console.WriteLine(fooA.SayHello(fooB)); // 在客户端应用程序域中调用FooA对象的方法,并将FooB对象作为参数传递进去
            AppDomain.Unload(newDomainA);
            AppDomain.Unload(newDomainB);
            Console.ReadLine();
         
        }

    }

output:

Hello from new domain A. FooB says: Hello from new domain B