2015년 2월 12일 목요일

Making DLL - MFC Extension DLL


1. Basis

1.1. Features
Extension DLL은 MFC로부터 상속받은 재사용가능한 Class를 구현하는 DLL이다. 이는 아래와 같은 특징을 가진다.

  • DLL을 사용하는 Client는 반드시 “_AFXDLL”이 정의되어 Compile된 MFC Application이어야 한다. 따라서 Client는 VC++ 또는 MFC를 지원하는 개발도구로만 개발될 수 있다.
  • Extension DLL은 MFC Regular DLL에서 사용될 수 있다.
  • Extension DLL은 반드시 “_AFXEXT”가 정의 되어 Compile되어야 한다.
  • Extension DLL은 MFC의 Dynamic Link Library version을 사용하여 만들어지며, DLL과 Client는 반드시 같은 버전의 MFCx0.DLL을 사용하여야 한다.
  • Extension DLL은 Client Application은 Address 공간에 Load된다. 따라서 Client와 같은 메모리할당공간, Resource, Module State를 갖는다.

1.2. Class Export and Import
Extension DLL은 Class의 Export/Import를 위하여 “AFX_EXT_CLASS” Macro를 사용한다. 이 Macro는 Preprocessor Symbol인 “_AFXDLL”과 “_AFXEXT” 모두가 정의 되어 있는 경우, 기존의 DLL Function의 Export에 사용했던 “__declspec(dllexport)”로 정의된다. 따라서 Extension DLL Project에서는 export로 정의 되어 사용된다. 반대로 앞에서 말한 2개의 Proprocessor Symbol이 정의 되어 있지 않다면, “__declspec(dllimport)”로 정의 되어 import하는 것으로 사용된다.
?
· Using AFX_EXT_CLASS
AFX_EXT_CLASS를 이용하여 Class를 Export/Import를 하기 위해서는 Class의 Header File에 Class의 정의부분에 AFX_EXT_CLASS를 추가하면 된다.
class AFX_EXT_CLASS CMyClass : public CDocument
{
// <body of class>
};
· Export/Import for Multiple Layer
앞에서 얘기했듯이, AFX_EXT_CLASS는 “_AFXEXT”의 정의 여부에 따라 Export/Import가 결정된다. 따라서 만약 Extension DLL이 또다른 Extension DLL을 Import하는 경우에는 문제가 발생하게 된다. 예를 들어 A라는 Extension DLL이 B라는 Extension DLL을 Import한다고 하면, A 역시 DLL이므로 AFX_EXT_CLASS는 export로 동작하게 된다. 따라서 B는 Import임에도 불구하고, B 역시 export로 인식하게 된다. 이러한 문제를 방지하기 위해서 Multiple Layer를 가지는 경우, AFX_EXT_CLASS를 사용하면 안되며, 각 DLL을 위한 고유한 Preprocessor가 필요하게 된다.
// A.H
#ifdef A_IMPL
   #define CLASS_DECL_A   __declspec(dllexport)
#else
   #define CLASS_DECL_A   __declspec(dllimport)
#endif

class CLASS_DECL_A CExampleA : public CObject
{ ... class definition ... };

// B.H
#ifdef B_IMPL
   #define CLASS_DECL_B   __declspec(dllexport)
#else
   #define CLASS_DECL_B   __declspec(dllimport)
#endif

class CLASS_DECL_B CExampleB : public CExampleA

{ ... class definition .. };
위와 같이 각각의 DLL마다 고유한 export/import 정의문을 만들어 사용하고, 각 DLL의 Project는 해당 Preprocessor를 Option에 정의하여 Compile한다. 이렇게 하여 각 DLL의 Header가 적절하게 export/import로 동작하게 된다.


2. Making Extension DLL

2.1. Object
간단한 DLL을 만들기 위하여 아래와 같은 기능을 가지는 Class를 DLL로 만들기로 한다.

  • CObject로부터 상속받은 Class를 사용한다.
  • Class는 Constructor에 이름(String), 번호(int)를 인자로 넘겨받아 멤버변수에 저장한다.
  • Class는 GetName(), GetNumber() 함수를 제공한다.

2.2. Implementation
  • 아래와 같이 새 프로젝트를 선택하고 “Visual C++” 카테고리의 “MFC DLL”을 선택한다.

  • MFC DLL 마법사에서 “MFC 확장 DLL”을 선택하고 프로젝트를 생성한다.



  • 프로젝트에서 “추가-새항목”을 선택한 후, 아래와 같이 MFC 클래스를 선택하고, 클래스 추가마법사에서 아래와 같이 CObject로부터 상속받는 클래스를 생성한다.




  • CTest Class의 Header를 열어 아래와 같이 Export Class로 만들고 각 함수를 선언한다.
[Test.h]
class AFX_EXT_CLASS CTest : public CObject  
{
public:
   CTest(CString sName, int nNumber);
   virtual ~CTest();
private:
   CString m_sName;
   int m_nNumber;
public:
   CString GetName();
   int GetNumber();
};
  • CTest의 Implementation을 작성한다.
[Test.cpp]
CTest::CTest(CString sName, int nNumber)
{
   m_sName = sName;
   m_nNumber = nNumber;
}

CTest::~CTest()
{
}

CString CTest::GetName()
{
   return m_sName;
}

int CTest::GetNumber()
{
   return m_nNumber;
}
  • 위와 같이 하나의 Class를 완성하고 Compile을 하면 “SimpleExtDll.dll”이 만들어진다.


3. Using Extension DLL
Extension DLL의 Import는 Project에서 DLL의 Library를 설정해주고, Class의 Header만 Include하면 된다. Test를 위하여 새로운 Project를 만들고 아래와 같이 Class의 Object를 생성하여 각 멤버변수를 호출한다.
#include "Test.h"

void CSimpleExtDllTestDlg::OnTestGetname() 
{
   CTest test("Test", 10);
   CString sName = test.GetName();
   int nNum = test.GetNumber();

   CString sTest;
   sTest.Format("Name: %s Number: %d", sName, nNum);
   AfxMessageBox(sTest);
}
위와 같은 Code를 작성하여 테스트하면, 정상적으로 CTest Class가 Import됨을 알수 있다. “Test.h”는 모든 멤버를 나타낼 필요는 없다. 실제로 DLL의 Client에 노출되는 public Member만을 가지면 동작할 수 있다.
[Test.h]
class AFX_EXT_CLASS CTest : public CObject  
{
public:
   CTest(CString sName, int nNumber);
   virtual ~CTest();

public:
   CString GetName();
   int GetNumber();
};

댓글 없음:

댓글 쓰기