C\#AppDomain 에 대한 정리

10427 단어 c#AppDomain
머리말
항상 이런 프로그램 을 쓰 고 싶 었 습 니 다.다른 프로그램 과 완전히 결합 을 풀 지만 다른 프로그램 을 동적 으로 불 러 오고 그 중의 특정한 방법 을 실행 할 수 있 습 니 다.실행 이 끝 난 후에 마 운 트 를 해제 할 수 있 고 프로그램 자체 에 영향 을 주지 않 습 니 다.최근 에 무의식중에 C\#중 앱 도 메 인 을 발 견 했 는데 반 사 를 더 하면 내 가 필요 로 하 는 것 같 아.
기본 개념
응용 프로그램 도 메 인 은 안전성,신뢰성,버 전 제어 및 마 운 트 해제 프로그램 집합 에 격 리 경 계 를 제공 합 니 다.응용 프로그램 도 메 인 은 보통 실행 할 때 숙주 가 만 들 고 실행 할 때 숙주 는 응용 프로그램 을 실행 하기 전에 공공 언어 를 실행 하도록 유도 합 니 다.
응용 프로그램 도 메 인 에서 제공 하 는 격 리 는 다음 과 같은 장점 을 가지 고 있 습 니 다.
(1)한 프로그램 에서 발생 하 는 오 류 는 다른 프로그램 에 영향 을 주지 않 습 니 다.형식 이 안전 한 코드 는 메모리 오 류 를 일 으 키 지 않 기 때문에 응용 프로그램 도 메 인 을 사용 하면 한 도 메 인 에서 실행 되 는 코드 가 프로 세 스 의 다른 응용 프로그램 에 영향 을 주지 않도록 확보 할 수 있 습 니 다.
(2)전체 프로 세 스 를 멈 추 지 않 고 하나의 프로그램 을 중단 할 수 있 습 니 다.응용 프로그램 도 메 인 을 사용 하면 한 프로그램 에서 실 행 된 것 을 마 운 트 해제 할 수 있 습 니 다.
메모:단일 프로그램 집합 이나 형식 을 마 운 트 해제 할 수 없습니다.도 메 인 전 체 를 마 운 트 해제 할 수 있 습 니 다.
모든 근원 은 Assembly.Load 방법 만 있 고 Assembly.Unload 방법 이 없 기 때문에 있 는 AppDomain 을 마 운 트 해제 할 수 밖 에 없습니다.
실천 하 다.
1.먼저 콘 솔 애플 릿 을 준비 합 니 다.
설정 파일 을 읽 기 위해(AppDomain 에서 설정 한 파일 의 읽 기 상황 을 테스트 하기 위해),Newtonsoft.JSon 을 사용 하여 json(AppDomain 에서 프로그램 을 불 러 오 는 제3자 참조 상황 을 테스트 하기 위해)으로 정렬 하여 콘 솔 에서 출력 합 니 다.프로젝트 이름 은 ReadPrint 로 exe 파일 로 컴 파일 하여 D:\AppDomainModules 에 저장 합 니 다.

using Newtonsoft.Json;

using System;
using System.Configuration;

namespace ReadPrint
{
  class Program
  {
    static void Main(string[] args)
    {
      DoSomething();
    }

    public static void DoSomething()
    {
      Person person = new Person
      {
        Account = ConfigurationManager.AppSettings["Account"],
        Name = ConfigurationManager.AppSettings["Name"],
        Age = int.Parse(ConfigurationManager.AppSettings["Age"])
      };

      Console.WriteLine(JsonConvert.SerializeObject(person));
      Console.ReadLine();
    }

    class Person
    {
      public string Account { get; set; }
      public string Name { get; set; }
      public int Age { get; set; }
    }
  }
}
보기 편 하도록 DoSomething 이 관련 방법 을 정 의 했 습 니 다.Main 방법 에 직접 쓸 수도 있 고 호출 할 때 인자 args 를 입력 해 야 합 니 다.앱 도 메 인 을 최종 테스트 하 는 프로그램 도 콘 솔 애플 리 케 이 션 을 사용 하고 콘 솔 애플 리 케 이 션 을 사용 해 이 애플 리 케 이 션 을 쓸 계획 이기 때문이다.
2.AppDomain 을 사용 하 는 프로그램 작성
주로 Assembly Loader.cs 파일 은 패키지 사용 세부 사항 과 Program.cs 메 인 프로그램 파일 을 포함 합 니 다.
AssemblyLoader.cs

using System;
using System.IO;
using System.Reflection;

namespace AppDomainTest
{
  public class AssemblyDynamicLoader
  {
    private AppDomain appDomain;
    public readonly RemoteLoader remoteLoader;
    public AssemblyDynamicLoader()
    {
      AppDomainSetup setup = new AppDomainSetup();
      setup.ApplicationName = "ApplicationLoader";
      setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
      setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules");
      setup.CachePath = setup.ApplicationBase;
      setup.ShadowCopyFiles = "true";	#   
      setup.ShadowCopyDirectories = setup.ApplicationBase;
      setup.ConfigurationFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", "ReadPrint.exe.config");
      //AppDomain.CurrentDomain.SetShadowCopyFiles();
      this.appDomain = AppDomain.CreateDomain("ApplicationLoaderDomain", null, setup);
      String name = Assembly.GetExecutingAssembly().GetName().FullName;
      this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName);	#   
    }

    public void Unload()
    {
      try
      {
        if (appDomain == null) return;
        AppDomain.Unload(this.appDomain);
        this.appDomain = null;
      }
      catch (CannotUnloadAppDomainException ex)
      {
        throw ex;
      }
    }
  }

  public class RemoteLoader : MarshalByRefObject
  {
    private Assembly _assembly;

    public void LoadAssembly(string assemblyFile)
    {
      try
      {
        _assembly = Assembly.LoadFrom(assemblyFile);
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }

    public void ExecuteMothod(string typeName, string methodName)
    {
      if (_assembly == null)
      {
        return;
      } 
      var type = _assembly.GetType(typeName);
      type.GetMethod(methodName).Invoke(Activator.CreateInstance(type), new object[] { });
    }
  }
}
그 중에서 클래스 RemoteLoader 는 프로그램 집합 을 불 러 오 는 클래스 이 고 Assembly DynamicLoader 클래스 는 이 를 바탕 으로 새로운 AppDomain 의 디 테 일 을 패키지 합 니 다.
Assembly DynamicLoader 의 구조 함수 에서 테스트 편 의 를 위해 프로그램 집합 파일 찾기 경로 PrivateBinPath 가 현재 프로그램 실행 디 렉 터 리 아래 의 Modules 디 렉 터 리,프로필 ConfigurationFile 을 Modules 디 렉 터 리 의 ReadPrint.exe.config 로 설정 하고 새 AppDomain 을 만 들 때의 프로그램 집합 이름 을 인 코딩 했 습 니 다.
AppDomainSetup 의 속성 ShadowCopyFiles('볼 륨 복사'로 번역 할 수 있 을 것 같 음)는 읽 은 프로그램 집합 을 잠 글 지 여부 입 니 다.true 로 설정 하면 프로그램 집합 을 메모리 로 읽 고 파일 을 잠 그 지 않 는 것 도 열 업데이트 의 전제 입 니 다.그렇지 않 으 면 프로그램 이 실행 되 는 동안 이 프로그램 집합 파일 들 은 잠 겨 서 변 할 수 없습니다.
AppDomain 의 방법 Create InstanceAndUnwrap 은 AppDomain 의 인 스 턴 스 에서 지정 한 형식의 새로운 인 스 턴 스 를 만 들 고 되 돌려 주 는 것 을 의미 합 니 다.
RemoteLoader 의 ExecuteMethod 에서 들 어 오 는 인자 하 드 인 코딩 이 비어 있 습 니 다.실제 사용 시 실제 입력 매개 변수 에 따라 야 합 니 다.
Program.cs

using System;
using System.IO;

namespace AppDomainTest
{
  class Program
  {
    static void Main(string[] args)
    {
      string modulesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules");
      DirectoryInfo di = new DirectoryInfo(modulesPath);
      if (!di.Exists)
      {
        di.Create();
      }

      string remotePath = @"D:\AppDomainModules\";

      string[] fileNames = new string[] { "ReadPrint.exe", "Newtonsoft.Json.dll", "ReadPrint.exe.config" };
      foreach(var fileName in fileNames)
      {
        FileInfo fi = new FileInfo(Path.Combine(remotePath, fileName));
        fi.CopyTo(Path.Combine(modulesPath, fileName), true);
      }

      AssemblyDynamicLoader adl = new AssemblyDynamicLoader();
      adl.remoteLoader.LoadAssembly(Path.Combine(modulesPath, "ReadPrint.exe"));
      adl.remoteLoader.ExecuteMethod("ReadPrint.Program", "DoSomething");
      adl.Unload();
    }
  }
}
주 프로그램 파일 에 Modules 폴 더 를 만 들 고 프로그램 파일,라 이브 러 리 파일,프로필 을 복사 합 니 다.프로그램 실행 결과:

우리 가 정의 한 DoSomething 방법 을 성공 적 으로 호출 한 것 을 볼 수 있 습 니 다.
약간의 사고.
1.왜 AppDomain 인 스 턴 스 의 Load 방법 으로 프로그램 집합 을 불 러 오지 않 습 니까?
이 방법 을 사용 하면 메 인 프로그램의 AppDomain 에 프로그램 집합(의존)을 한 번 불 러 오고 우리 가 만 든 AppDomain 으로 이동 합 니 다.(특히 이 때 는 새로 만 든 AppDomain 의 PrivateBinPath 에서 검색 하고 불 러 오지 않 습 니 다.)
단점 은 두 가지 가 있 습 니 다.하 나 는 프로그램 이 실 행 됨 에 따라 대량의 프로그램 집합 을 불 러 올 수 있 기 때문에 메 인 프로그램의 AppDomain 도 대량의 프로그램 집합 을 불 러 와 야 합 니 다.프로그램 집합 은 따로 마 운 트 해제 할 수 없고 메 인 프로그램 이 멈 춘 후에 만 마 운 트 해제 할 수 있 습 니 다.그 사이 에 쌓 일 수록 우아 하지 않 습 니 다.둘째,디 렉 터 리 를 설정 할 수 없습니다.메 인 프로그램 이 프로그램 집합 을 불 러 오고 의존 할 때 지정 한 PrivateBinPath 에서 만 검색 하기 때문에 다른 모듈 에 필요 한 모든 프로그램 집합 파일 이 같은 디 렉 터 리 에 쌓 여 있어 서 조리 가 없습니다.
검증 하 다.
Assembly DynamicLoader.cs 의 코드 를 수정 하고 구조 함수 에서 프로그램 로드 를 직접 실행 하 는 것 으로 바 꿉 니 다.다른 것 은 변 하지 않 습 니 다.그리고 새로 만 든 AppDomain 에서 불 러 온 프로그램 집합 을 보십시오.

	  //String name = Assembly.GetExecutingAssembly().GetName().FullName;
      //this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName);

      Assembly assembly = this.appDomain.Load("ReadPrint");
      Type t = assembly.GetType("ReadPrint.Program");
      MethodInfo mi = t.GetMethod("DoSomething");
      //mi.Invoke(Activator.CreateInstance(t), new object[] { });

      var tmp = this.appDomain.GetAssemblies();
여기 서 가장 이상 한 것 은 위 에서 자신의 App Domain 의 PrivateBinPath 와 프로필 을 지 정 했 음 에 도 불구 하고 실행 할 때 메 인 프로그램의 PrivateBinPath 와 프로필 을 찾 았 기 때문에 실 행 될 코드 설명 입 니 다.
Program.cs 의 코드 를 수정 하고 Assembly DynamicLoader 만 호출 하 는 구조 함수 로 변경 합 니 다.다른 것 은 변 하지 않 습 니 다.주 프로그램 인 AppDomain 에 불 러 온 프로그램 집합 을 보십시오.

	  AssemblyDynamicLoader adl = new AssemblyDynamicLoader();
      //adl.remoteLoader.LoadAssembly(Path.Combine(modulesPath, "ReadPrint.exe"));
      //adl.remoteLoader.ExecuteMethod("ReadPrint.Program", "DoSomething");
      //adl.Unload();

      var tmp = AppDomain.CurrentDomain.GetAssemblies();

      Console.ReadLine();
결 과 는 그림 과 같다.

2.왜 프 록 시 와 유사 한 클래스 RemoteLoader 를 사용 합 니까?Create InstanceAndUnwrap 을 직접 사용 하지 않 고 프로그램 집합 을 불 러 오 는 인 스 턴 스 를 만 듭 니 다.
직접 사용 하면 다음 과 같은 오류 가 표 시 됩 니 다:

주의해 야 할 것 은 RemoteLoader 류 는 Marshalby RefObject 를 계승 하고 이 를 계승 하면 AppDomain 을 뛰 어 넘 을 수 있 습 니 다.홈 프로그램 에 새로운 앱 도 메 인 을 만 들 수 있 지만 새로운 앱 도 메 인 은 홈 프로그램 에서 완전히 벗 어 날 수 없 을 것 으로 예상 된다.
모든 호출 된 모듈 에 이 를 계승 하 라 고 요구 할 수 없 기 때문에 프 록 시 클래스 RemoteLoader 를 사용 합 니 다.실행 과정 은:새로운 AppDomain 만 들 기;그 중에서 새 프 록 시 클래스 RemoteLoader,프 록 시 클래스 는 서로 다른 모듈 과 의존 도 를 불 러 오고 모듈 을 호출 하 는 데 도움 을 줍 니 다.Create InstanceAndUnwrap 은 실제로 새로 만 든 AppDomain 에서 프 록 시 클래스 를 만 들 고 예화 한 것 으로 이후 모든 작업 이 새로운 AppDomain 에서 진행 되 었 다.
후기
코드 에 하 드 인 코딩 을 많이 사 용 했 습 니 다.실제 적 으로 메 인 프로그램 에 호출 할 모듈 경로,파일 경로 에 의존 하고 파일 경 로 를 설정 하 며 메 인 프로그램 에서 임시 디 렉 터 리 로 복사 한 다음 에 Assembly DynamicLoader 를 사용 하여 새로운 AppDomain 을 만 들 고 실행 해 야 합 니 다.
대부분 글 을 보 는 것 이 문 제 를 해결 하기 위해 서 라 고 생각 하기 때문에 본 고 는 사용 방법 을 앞 에 두 고 상세 한 설명 을 뒤에 두 었 고 XD 를 최적화 한 셈 이다.
이상 은 C\#AppDomain 에 관 한 상세 한 내용 입 니 다.C\#AppDomain 에 관 한 자 료 는 저희 의 다른 관련 글 을 주목 하 세 요!

좋은 웹페이지 즐겨찾기