EMDI는 지금도 개발중

C# : 실행프로그램(exe)만 있어도 BouncyCastle.CryptoExt.dll 참조 가능하도록 설정 (dll을 포함한 단일 exe배포) 본문

언어/C#

C# : 실행프로그램(exe)만 있어도 BouncyCastle.CryptoExt.dll 참조 가능하도록 설정 (dll을 포함한 단일 exe배포)

EMDI 2021. 3. 30. 14:26

이번 글에서는 실행프로그램(exe)만 있어도 dll참조하여 사용할 수 있는 dll포함 단일 exe배포방법에 대해서 공부하도록 하겠습니다. 저 같은 경우 BouncyCastle.CryptoExt.dll때문에 이번 공부를 하게 되었는데요. 다양한 방법을 정리하여 올려놓았으니 원하시는 방법으로 사용하시면 될 듯 싶습니다!


1. Assembly를 이용한 dll참조 단일 실행프로그램(exe)만들기

사용하고자하는 dll(예시 BouncyCastle.CryptoExt.dll)을 우선 프로젝트에 참조 추가합니다. 그 다음 프로그램 진입점(Program.cs)에 코드를 아래와 같이 변경합니다.

[STAThread]
static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);

    Application.EnableVisualStyles();
    //Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
}

// .NET 4.0 이상
static Assembly ResolveAssembly(object sender, ResolveEventArgs args) {
  Assembly thisAssembly = Assembly.GetExecutingAssembly();
  var name = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";

  var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(name));
  if (resources.Count() > 0) {
      string resourceName = resources.First();
      using (Stream stream = thisAssembly.GetManifestResourceStream(resourceName)) {
          if (stream != null) {
              byte[] assembly = new byte[stream.Length];
              stream.Read(assembly, 0, assembly.Length);
              Console.WriteLine("Dll file load : " + resourceName);
              return Assembly.Load(assembly);
          }
      }
  }
  return null;
}

// LINQ가 지원되지 않는 .NET 버전
static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
    Assembly thisAssembly = Assembly.GetExecutingAssembly();
    string resourceName = null;
    string fileName = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
    foreach (string name in thisAssembly.GetManifestResourceNames())
    {
        if (name.EndsWith(fileName))
        {
            resourceName = name;
        }
    }

    
    if (resourceName != null)
    {
        using (Stream stream = thisAssembly.GetManifestResourceStream(resourceName))
        {
            if (stream != null)
            {
                byte[] assembly = new byte[stream.Length];
                stream.Read(assembly, 0, assembly.Length);
                Console.WriteLine("Dll file load : " + resourceName);
                return Assembly.Load(assembly);
            }
        }
    }
    return null;
}

단, 위의 소스는 exe파일과 Application이 호출하는 dll파일이 동일한 경로에 위치해 있어야 한다는 기준입니다. 만약 exe파일 경로가 아닌 다른 경로의 dll파일을 사용하고 싶은 경우 Assembly의 경로를 다르게 지정해주시면 됩니다.

Assembly thisAssembly = Assembly.GetExecutingAssembly();
string AppPath = @"특정 경로";
if (System.IO.File.Exists(String.Format("{0}{1}", AppPath,  "BouncyCastle.CryptoExt.dll")))
{
    thisAssembly = Assembly.LoadFile(String.Format("{0}{1}", AppPath, "BouncyCastle.CryptoExt.dll"));
}

 

2. EnterpriseServices Internal Publish에서 GacInstall을 이용하여 특정경로의 dll 참조걸기

[STAThread]
static void Main()
{
    string AppPath = @"";
    System.EnterpriseServices.Internal.Publish dllImport = new System.EnterpriseServices.Internal.Publish();
    dllImport.GacInstall(String.Format("{0}{1}", AppPath, "BouncyCastle.CryptoExt.dll"));
            
    Application.Run(new MainForm());
}

단, 위의 방법을 사용하기 위해서는 특정경로에 해당 dll이 필수로 있어야 하며, System.EnterpriseServices를 참조해야 합니다.

* GACInstall : 전역 어셈블리 캐시에 어셈블리를 설치하는 방식 GacInstall(string AsssemblyPath) : 해당 메소드를 사용하시려면 실행프로그램(exe)이 관리자 권한으로 실행되어야 합니다. 권한이 없는경우 SecurityException 오류가 발생할 수 있습니다.

* SecurityException : 호출 체인에 있는 호출자에게 비관리 코드에 액세스할 수 있는 권한이 없는 경우

Comments