2014. 1. 24. 15:00

일반적으로 'DLL'은 개발단계에서 참조를 통해 연결하여 사용합니다.

하지만 여러 가지 상황에 따라서 동적 로드를 해야 하는 경우가 있죠.

대표적인 경우가 국가나 언어, 운영체제 등에 따라 다른 dll을 써야 하는 경우라던가 동적으로 생성된 'DLL'을 참조해야 하는 경우라던가...

물론 설계 의도에 따라 많이 달라지니 꼭 필요하다고는 볼 수 없습니다 ㅎㅎㅎ

 

어찌 됐건 동적으로 DLL을 로드하여 사용하는 방법을 알아봅시다.

 

 

1. 테스트 DLL 만들기

클래스 라이브러리로 프로젝트를 2개 생성합니다.

하나는 'TestDll1', 다른 하나는 'TestDll2'로 생성합니다.

 

각각의 코드는 아래와 같습니다.

namespace TestDll1
{
    public class Class1
    {
		public string Say(string sData)
		{
			return "TestDll1 - " + sData;
		}
    }
}

 

namespace TestDll1
{
    public class Class2
    {
		public string Say(string sData)
		{
			return "TestDll2 - " + sData;
		}
    }
}

 

 

이제 윈폼 프로젝트를 만들고 빌드한 다음 dll을 윈폼 프로젝트의 출력 폴더에 복사해 넣습니다.

 

 

2. DLL 로드하기

DLL을 로드해주는 'LoadDll' 메소드를 생성합니다.

/// <summary>
/// Dll을 로드 하는 클래스
/// </summary>
private bool LoadDll(int nDll)
{
	//DLL 이름
	string sFileName = "";

	switch(nDll)
	{
		case 1:
			sFileName = "TestDll1.dll";
			break;
		case 2:
			sFileName = "TestDll2.dll";
			break;

		default:	//에러
			return false;
	}

	//Dll이 있는지 여부
	if( false == File.Exists(sFileName) )
	{	//없으면 에러
		return false;
	}

	//Dll 로드
	Assembly asm = Assembly.LoadFrom(sFileName);

	//로드가 되었나?
	if (asm == null)
	{
		return false;
	}

	//로드가되었으면 
	//Dll에 소속된 구성요소 리스트를 받아온다.
	Type[] types = asm.GetExportedTypes();

	//types[]의 내용으로 원하는 네임스페이스나 클래스를 찾을수 있다.
	//이 예제에서는 네임스페이스와 클래스가 한개뿐이기 때문에 그냥 0번 오브젝트를 사용한다.
	m_Type = Activator.CreateInstance(types[0]);
			

	//Dll로드가 정상적으로 끝났다.
	return true;
}

 

여기서 봐야 할 코드는


Assembly asm = Assembly.LoadFrom(sFileName);

DLL을 실질적으로 로드하는 코드 입니다.

 

Type[] types = asm.GetExportedTypes();

이렇게 해서 DLL에 포함된 구성요소의 배열을 받아 올 수 있습니다.

이렇게 받아온 'Type[]'의 내용을 검색하여 원하는 네임스페이스나 클래스를 찾을 수 있습니다.

 

m_Type = Activator.CreateInstance(types[0]);

'object m_Type'는 전역 변수입니다.

'Activator.CreateInstance' 이 메서드를 통해 지정한 클래스를 인터페이스로 만듭니다.

DLL에 포함된 클래스를 우리가 사용할 수 있도록 변환한다고 생각하시면 쉽습니다.

 

 

3. 호출 코드 만들기

이제 메소드를 지정하고 호출하는 코드를 만들어 보겠습니다.

 

3-1. 인보크 방식으로 사용하기

인보크 방식은 델리게이트를 따로 선언하지 않아도 된다는 장점이 있습니다.

대신 모양이 안 좋죠-_-;

private void DllOutText()
{
	//오브젝트 안의 "Say"메소드를 찾는다.
	MethodInfo methodInfo = m_Type.GetType().GetMethod("Say");
			
	//파라메타를 보네 메소드를 호출한다.
	txtOut.Text = methodInfo.Invoke(m_Type, new object[] { "호출" }).ToString();
}

 

여기 코드는 심플합니다.

 

메소드를 지정하고 지정한 메소드를 인보크를 통해 호출하면 됩니다.

 

'MethodInfo'는 dll의 메소드에 접근하기 위한 기능들을 제공합니다.

(참고 : MSDN - MethodInfo 클래스)

 

 

3-2. 델리게이트(delegate)를 이용한 방식

델리게이트 방식은 델리게이트를 미리 선언해야 한다는 불편함이 있지만 메소드를 호출하는 모양이 우리가 흔히 사용하는 모양이라 편하게 사용할 수 있다는 장점이 있습니다.

 

네임스페이스나 클래스의 맨 위에 다음과 같이 델리게이트를 선언합니다.

public delegate string SayDel(string sData);

 

 

그다음 다음과 같은 메소드를 만듭니다.

private void DllOutText_Del()
{
	MethodInfo minfo = m_Type.GetType().GetMethod("Say");
	SayDel delSay
		= (SayDel)Delegate.CreateDelegate(typeof(SayDel), null, minfo);
	txtOut.Text = delSay("호출");
}

 

미리 선언만 되어 있으면 델리게이트 방식이 좋습니다.

모양이 이쁘게 나오니까요 ㅎㅎㅎ

 

오류 1. 시그니처 또는 보안 투명도가 대리자 형식과 호환되지 않으므로 대상 메서드에 바인딩할 수 없습니다.

델리게이트를 연결할 때 다음과 같은 오류가 날 수 있습니다.

이 오류를 해결하러면 

'Delegate.CreateDelegate'에 두 번째 인자로 'null'이나 'this'를 주고 

'MethodInfo'를 세 번째 인자로 넘겨주면 해결됩니다.

 

 

4. 테스트하기

이제 테스트해봅시다.

 

두 방식다 동작상 차이는 없습니다.

 

잘 되네요.

 

 

마무리

이 포스팅에 사용된 프로젝트 파일입니다.

TestDynamicDll.zip
다운로드

 

이 방법 말고 인터페이스를 만들어서 바인딩하거나 그냥 클래스로 처리하거나 하는 방법도 있습니다.
그런 것들은 기회 되면 포스팅하도록 하죠 ㅎㅎㅎㅎ