2015년 2월 12일 목요일

Making DLL - Regular DLL

1. Basis

1.1. Features
  • 내부적으로 MFC를 사용할 수 있으며, 외부로 MFC 또는 non-MFC 실행파일에서 호출할 수 있는 C-Style의 함수를 Export한다.
  • 내부적으로 C++ 클래스를 사용하고 C 함수 Wrapper만을 Export 할 수 있다. 따라서 내부적인 C++ Class에 대한 변경은 DLL의 호출에 영향을 주지 않는다.
  • DLL을 사용하는 Client는 DLL 호출을 지원하는 어떠한 Language로 작성될 수 있다.
  • MFC Library와의 Dynamic & Static Link를 지원한다.

1.2. MFC Module State
Regular DLL의 MFC DLL과의 Link 능력은 몇가지 문제를 발생시킨다.
MFC는 현재의 CWinApp object의 pointer, handle map등의 global data를 가지는데, Regular DLL을 사용하게 되면, 하나의 Process에 다수의 CWinApp object(DLL을 사용하는 프로그램의 CWinApp와 Regular DLL이 가지는 CWinApp)를 가질 수 있게 다수의 handle map 등의 global data를 가지게 된다.

따라서 각 module(application, Regular DLL)은 각자의 고유한 global data를 관리한다. 이 각 module별로 관리되는 MFC의 global data를 “Module State”라 한다.

MFC의 일반적인 window procedure는 자동으로 적절한 module state로 전환시키므로 그에 대한 별도의 처리를 해줄 필요가 없지만, Regular DLL의 함수를 호출하는 경우에는, 반드시 어느 module state를 사용할 것인지를 명시하여야 한다. 예를 들어, MFC는 기본적으로 main application의 resource handle을 사용하도록 설정된다. 만약 DLL의 함수가 DLL 내에 포함되어있는 Resource를 사용하여 Dialog Box를 보여주는 기능을 가진다면, 함수 호출 시 MFC는 main application의 resource를 사용하여 Dialog를 표현할 것이다. 이런 경우, 오동작을 막기 위하여 반드시 어떤 module state를 사용할 것인지를 명시하여야한다. 이를 위하여 함수의 첫머리에 반드시 아래의 Code를 추가하도록 한다.

AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
이것은 Scope 내에서 module state를 현재의 Module에 대한 것으로 전환시켜준다.



2. Making Regular DLL
2.1. 목표
간단한 DLL을 만들기 위하여 아래와 같은 두가지의 기능만을 가지는 DLL을 만들기로 한다.
  • 하나의 Edit Box를 가지는 Dialog Box를 보이고, 입력받은 String을 되돌려주는 함수
  • 하나의 String을 Parameter로 받아 Message Box에 표시하는 함수

2.2. 구현방법
  • 아래와 같이 새 프로젝트를 선택하고 “MFC” 카테고리의 “MFC DLL”을 선택한다.







  • MFC DLL 마법사에서 “공유 MFC DLL을 사용하는 기본 DLL”을 선택하여 프로젝트를 생성한다.

  • export할 함수의 정의를 위하여 Header File을 생성한다. 이는 후에 import 측에서도 공용으로 사용될 것이다. “RegularDLLSampleFn.h”의 이름으로 Header를 생성하고, 두개의 함수를 위한 함수정의를 만든다.
[RegularDLLSampleFn.h]
#pragma once

#ifdef _USRDLL
   #define DLLFunction  __declspec(dllexport)
#else
   #define DLLFunction  __declspec(dllimport)
#endif

extern "C" {
DLLFunction void __stdcall GetUserInput(char* szInput);
DLLFunction void __stdcall ShowMsgBox(char* szMsg);
}
Resource에 Dialog를 추가하고 아래와 같이 만들고, Dialog Class를 추가한 후, TextBox에 대한 멤버변수를 추가한다.

  • 함수 구현을 위한 cpp 파일을 만들고 선언된 함수에 대한 기능을 작성한다.
[RegularDLLSampleFn.cpp]
#include "stdafx.h"
#include "RegularDllSample.h"
#include "RegularDLLSampleFn.h"
#include "TestDlg.h"

DLLFunction void __stdcall GetUserInput(char* szInput)
{
   AFX_MANAGE_STATE(AfxGetStaticModuleState());

   szInput[0] = '\0';

   CTestDlg dlg;

   if(dlg.DoModal() == IDOK)
   {
                 lstrcpy(szInput, (LPCTSTR)dlg.m_sInput);
   }
}

DLLFunction void __stdcall ShowMsgBox(char* szMsg)
{
   AFX_MANAGE_STATE(AfxGetStaticModuleState());

   AfxMessageBox(szMsg);
}

  • Build하면 “RegularDll.dll”이 생성된다.



3. Using Regular DLL
Regular DLL의 사용방법은 1.3에서의 DLL사용법과 동일하다. DLL을 Test할 수 있는 Project를 VC++와 C#에서 생성하고, 1.3에서 설명한 방법대로 호출하면 DLL의 각 함수가 정상적으로 동작함을 알 수 있다.

[VC++]
void CRegularDLLSampleTestDlg::OnBnClickedButtonTest1()
{
   char szInput[32];
   GetUserInput(szInput);

   CString sMsg;
   sMsg.Format("Input: %s", szInput);
   AfxMessageBox(sMsg);
}

void CRegularDLLSampleTestDlg::OnBnClickedButtonTest2()
{
   UpdateData(TRUE);
   char szMsg[32];
   lstrcpy(szMsg, (LPCTSTR)m_sInput);
   ShowMsgBox(szMsg);
}
[C#]
[DllImport("RegularDLLSample.dll")]
public static extern void GetUserInput(StringBuilder inputMsg);
[DllImport("RegularDLLSample.dll")]
public static extern void ShowMsgBox(string msg);

private void btnTest1_Click(object sender, EventArgs e)
{
   var sb = new StringBuilder(100);
   GetUserInput(sb);
   MessageBox.Show(sb.ToString());
}

private void btnTest2_Click(object sender, EventArgs e)
{
   ShowMsgBox(txtInput.Text);
}

댓글 없음:

댓글 쓰기