Application Domain #4 - Assembly Reload

2021. 5. 6. 17:47

  우리가 어떤 모듈을 어셈블리 단위로 제공을 한다고 가정해보자. 해당 모듈이 업데이트되었을 경우 기존에 load 되어 있는 어셈블리는 unload 시키고 다시 새로운 어셈블리를 load 하는 작업(re-load)을 거치기만 한다면, 해당 응용프로그램은 종료 없이 언제든 업데이트될 수 있을 것이다. 하지만, dotnet환경에서 동적으로 load된 어셈블리를 unload를 하는 행위는 그리 만만한 작업이 아니다. msdn에 나와있듯 도메인에 중립적인 어셈블리는 전체 프로세스가 종료될 때까지 메모리에 잔류하며, 해당 어셈블리를 unload 하는 유일한 방법은 프로세스를 종료하는 것이라고 한다.

 프로세스를 종료하지 않고 어셈블리를 unload 하기 위하여 여러 방법(Shadow Copy 등)을 찾아 시도해 보았으나, 그리 성공적이지 못했다. Assembly Shadow Copy의 경우, 어셈블리 자체는 unload가 되어 re-load까지 되었으나, 애플리케이션(exe파일)에 한정되었고 Dll자체는 unload하지 못했다. (이는 본인의 학습 또는 노력의 부족으로 생각이 된다.)

 

 그래서 시도하게 된 방법이 새로운 AppDomain을 구성하여 새로이 구성된 Sub AppDomain에서 어셈블리를 Load, Unload 하는 방법이었다. 해당 방법을 시도하기 위하여 약간의 제약사항이 있었다.  우선, '새로 만든 AppDomain에 어셈블리를 load 하기 위해선, Assembly.load 또는 AppDomain.LoadAssembly로 직접 로드할 수 없고, AppDomain.CreateInstance를 통해 지정한 어셈블리에 정의된 지정한 형식의 새 인스턴스를 생성해야 한다' 것이다. 또한, AppDomain.CreateInstance를 통해 만들어진 인스턴스는 다른 AppDomain에서 왔기 때문에. 원격 객체와 통신하는 것과 같은 remoting 호출이 일어난다. (닷넷에선 서로 다른 AppDomain 간의 통신이 이루어질 경우 원격 객체와 통신하는 것과 같은 remoting 호출이 일어난다.)

 때문에, ObjectHandle 클래스를 이용하여 래핑 된 개체를 제공받고 이를 Unwrap 해야 한다.(ObjectHandle MarshalByRefObject 클래스에서 파생되었기 때문에 ObjectHandle 클래스를 호출하는 개체는 MarshalByRefObject 상속받아야 한다.)

 이후, 어셈블리가 새 AppDomain에 로드되고 개체도 새 AppDomain에만 남아 있도록 한다면 해당 개체를 통해 어셈블리를 사용 후 unload 할 수 있게 된다.

 

아래 해당 로직의 코드를 첨부하며, 전체 프로젝트는 GitHub에 첨부해 두도록 한다.

 

 

 

private object MashallLoad()
{
    try
    {
        //Create 'New AppDomain' to load 'Assembly'
        AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
        AppDomain newDomain = AppDomain.CreateDomain("NewDom", AppDomain.CurrentDomain.Evidence, setup);

        //Create instance of 'AssemblyLoader' class in new appdomain  
        System.Runtime.Remoting.ObjectHandle obj = newDomain.CreateInstance(typeof(AssemblyLoader).Assembly.FullName, typeof(AssemblyLoader).FullName);

        //The instance we created came from a different AppDomain
        //We will get that instance in wrapped format
        //So we have to unwrap it
        AssemblyLoader loader = (AssemblyLoader)obj.Unwrap();

        //Call loadassembly method 
        //so that the assembly will be loaded into the new appdomain and the object will also remain in the new appdomain only.  
        loader.Load(m_path);

        //Call exceuteMethod and pass the name of the method from assembly and the parameters.  
        object result = loader.ExecuteMethod("Class", "TestMethod", null);

        //After the method has been executed call the unload method of the appdomain.  
        AppDomain.Unload(newDomain);

        return result;
    }
    catch(Exception ex)
    {
        //
        return null;
    }
}

 

 

using System;
using System.Reflection;

namespace Dll_Reloader
{
    public class AssemblyLoader : MarshalByRefObject
    {
        private System.Reflection.Assembly m_Assembly;
        private Type m_Type;
        private object m_instance;

        public AssemblyLoader()
        {
            m_Assembly = null;
            m_Type = null;
            m_instance = null;
        }

        ~AssemblyLoader()
        {
            m_Assembly = null;
            m_Type = null;
            m_instance = null;
        }

        public void Load(string path)
        {
            this.m_Assembly = System.Reflection.Assembly.Load(AssemblyName.GetAssemblyName(path));
        }

        public object ExecuteMethod(string strModule, string methodName, params object[] parameters)
        {
            foreach (System.Type type in this.m_Assembly.GetTypes())
            {
                if (String.Compare(type.Name, strModule, true) == 0)
                {
                    this.m_Type = type;
                    this.m_instance = m_Assembly.CreateInstance(type.FullName);
                    break;
                }
            }
            
            MethodInfo MyMethod = this.m_Type.GetMethod(methodName);
            object obj = MyMethod.Invoke(this.m_instance, BindingFlags.InvokeMethod, null, parameters, null);

            return obj;
        }
    }
}

 

 

 

 

 

 

 

 


GitHub

github.com/KimUS94/Dll_Reloader

 

Reference

docs.microsoft.com/ko-kr/dotnet/api/system.appdomain?view=net-5.0

docs.microsoft.com/ko-kr/dotnet/api/system.marshalbyrefobject?view=net-5.0

stackoverflow.com/questions/658498/how-to-load-an-assembly-to-appdomain-with-all-references-recursively

egloos.zum.com/littles/v/3471191

 

 

Link

2021.04.19 - [programing/dot net] - Application Domain #3 - Shadow Copy

2021.04.19 - [programing/dot net] - Application Domain #2 - AppDomain 사용(temp)

2021.04.19 - [programing/dot net] - Application Domain #1 - Application Domain 이란?

 

 

 

 

 

BELATED ARTICLES

more