정적 링크 라이브러리 / 동적 링크 라이브러리
정적 링크 라이브러리 (Static Link Library)
C++는 기본적인 문법사항만을 규정하며, 프로그래밍에 필요한 많은 유용한 기능들은 라이브러리 형태로 제공된다. 예를 들어 strlen() 이라는 함수는 라이브러리 형태로 제공된다. 프로그램에서 strlen 함수를 사용하면 소스코드가 컴파일 된 후 링크될 때, 라이브러리 파일에서 strlen 함수의 기능이 구현된 부분이 실행파일에 덧붙여진다. 이렇게 생성된 실행파일은 단독으로 strlen 함수의 기능을 수행할 수 있게 된다.
동적 링크 라이브러리 (Dynamic Link Library)
어떤 기능이 실행 파일에 직접 덧붙여지지 않고, DLL 파일에 독립적으로 존재하다가 프로그램이 실행될 때 동적으로 링크되어 사용되는 것이다. 따라서 동적 링크 라이브러리는 실행 가능한 코드로 이루어지지만 단독으로는 실행될 수 없다. 단독으로 실행 가능한 다른 프로그램의 호출을 받아 그 프로그램의 기능을 일부 분담한다.
MFC 또한 일종의 라이브러리이다. 따라서 MFC도 프로그램에 정적으로 링크 될 것인지 동적으로 링크 될 것인지 설정할 수 있다. 아래 설정에서 MFC 라이브러리 설정을 할 수 있다.

[Use MFC in a Shared DLL]
MFC 라이브러리가 실행 파일에 덧붙여지지 않고 프로그램을 실행할 때 동적으로 링크됨
실행 파일의 크기가 작음
프로그램이 실행될 때 MFC 라이브러리가 담겨있는 DLL 파일이 반드시 필요함
[Use MFC in a Static DLL]
MFC 라이브러리가 실행 파일에 덧붙여져 프로그램을 실행시킬 때 정적으로 링크됨
실행 파일의 크기가 큼
별도의 DLL 파일이 필요 없고 실행 파일을 단독으로 실행시킬 수 있음
일반 DLL
일반 DLL 라이브러리를 작성하면 C++ 뿐만 아니라 다른 프로그래밍 언어에서도 사용할 수 있는 범용적인 DLL을 만들 수 있다. 외부에서 사용되기 위해 만들어지는 라이브러리 함수는 모두 C 함수의 형태로 만들어져야 한다. 클래스나 오버로딩 된 함수 등 C++의 특징들은 일반 DLL의 내부에서는 사용될 수 있으나, 다른 프로그램에서 호출할 수는 없다.
DLL 예제
하나의 숫자를 입력 받아 제곱을 계산하여 출력하는 프로그램을 만들어본다.

DLL : SquareDLL
암시적 연결 방법 사용 : SquareImplicit
명시적 연결 방법 사용 : SquareExplicit
DLL 생성
새 프로젝트 생성 시 MFC DLL을 선택하여 생성한다. 프로젝트명은 SquareDLL 나머지 옵션은 그대로 둔다.

SquareDLL.cpp 파일 끝에 제곱을 계산하는 기능을 추가한다. 일반 DLL에서 다른 프로그램에서 호출할 수 있는 라이브러리를 만들 때는 C 함수의 형태로 만들어야 한다. 또한 DLL 내부에서만 쓰이는 함수가 아니고, 다른 프로그램이 호출할 수 있게 하려면 함수를 다음과 같이 선언해야 한다.
// SquareDLL.cpp
extern "C" __declspec(dllexport) double Square(double input)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return (input * input);
}
extern "C"은 C++ 형태로 컴파일하지 말고, C 형태로 컴파일 하라는 뜻이고, __declspec(dllexport)은 DLL의 외부에서 이 함수를 호출할 수 있도록 하라는 뜻이다. 또한 이 함수를 MFC에서 호출할 수 있도록 하려면 함수의 맨 첫 줄에 AFX_MANAGE_STATE 매크로를 추가해줘야 한다.
이 프로젝트를 컴파일하면 Debug 폴더에 확장자가 DLL인 파일과 LIB인 파일이 생성된다.
kimgopa/mfc_project
visual c++ mfc project. Contribute to kimgopa/mfc_project development by creating an account on GitHub.
github.com
암시적 연결 방법으로 DLL 사용하기
프로젝트명은 SquareImplicit인 다이얼로그 기반 프로그램을 생성한다.
앞서 만들어진 DLL을 연결하기 위해 SquareDLL 프로젝트의 Debug 폴더에 만들어진 SquareDLL.dll 파일과 SquareDLL.lib 파일을 SquareImplicit 프로젝트 디렉터리로 복사한다. 그리고 이 프로그램이 SquareDLL.dll에 있는 함수를 호출할 수 있도록 하기 위해 설정에 아래와 같이 SquareDLL.lib를 추가해준다.

또는 아래와 같이 소스 한줄만 추가해주면 된다.
#pragma comment(lib, "SquareDLL.lib")
SquareDLL.lib 파일은 SquareDLL.dll 파일을 생성할 때 컴파일러가 같이 생성시켜 준 것으로 Import Library라고 부른다. 이 파일은 정적 링크 라이브러리처럼 lib라는 확장자를 가지고 있지만 정적 링크 라이브러리와는 다른 것이다. 정적 링크 라이브러리는 실제 실행 코드를 저장하고 있지만 Import Library는 DLL과 연결하는데 필요한 함수 이름과 DLL 파일명만을 가지고 있다. Import Library를 넣어줌으로써 이 프로젝트와 SquareDLL.dll이 연결된다.
프로그램이 실행되면 운영체제는 다음과 같은 순서로 SquareDLL.dll 파일을 찾는다.
1. DLL을 호출한 EXE 파일이 있는 디렉터리
2. 프로세스의 현재 디렉터리
3. 윈도우 시스템 디렉터리
4. 윈도우 디렉터리
5. PATH 환경 변수에 지정된 디렉터리
따라서 SquareDLL.dll 파일은 위 다섯 개 디렉터리 중 한곳에 존재해야 한다.
DLL에 있는 함수를 사용하기 전에는 반드시 함수 선언을 해주어야 한다. 이 함수를 여러 곳에서 사용한다면 헤더 파일에 넣어 두는 것이 좋다. Edit 컨트롤의 EN_CHANGE 이벤트 처리기를 만들고, Square 함수를 호출하여 제곱 연산을 수행한다.
extern "C" __declspec(dllimport) double Square(double input);
void CSquareImplicitDlg::OnEnChangeEditInput()
{
UpdateData(TRUE);
m_dOutput = Square(m_dInput);
UpdateData(FALSE);
}
kimgopa/mfc_project
visual c++ mfc project. Contribute to kimgopa/mfc_project development by creating an account on GitHub.
github.com
명시적 연결 방법으로 DLL 사용하기
Import Library를 사용하지 않고 DLL의 파일명을 명시적으로 사용하여 DLL과 연결하는 방법이다.
프로젝트명은 SquareExplicit인 다이얼로그 기반 프로그램을 생성한다.
SquareDLL 프로젝트의 Debug 폴더에 만들어진 SquareDLL.dll 파일을 SquareExplicit 프로젝트 디렉터리로 복사한다. 그리고 명시적 연결 방법을 사용할 때는 LoadLibrary 함수에 연결할 DLL 파일의 파일명을 적어줌으로써 DLL 파일과 연결한다.
// SquareExplicitDlg.h
class CSquareExplicitDlg : public CDialogEx
{
...
HINSTANCE m_hDll;
};
// SquareExplicitDlg.cpp
BOOL CSquareExplicitDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
...
if ((m_hDll = LoadLibrary("SquareDLL.dll")) == NULL)
AfxMessageBox("SquareDLL.dll 파일을 찾을 수 없습니다.");
return TRUE; // return TRUE unless you set the focus to a control
}
DLL 파일과 연결되지 않으면 LoadLibrary 함수는 NULL을 반환하며, DLL이 제대로 로딩되었으면 HINSTANCE형의 값을 반환한다.
LoadLibrary 함수로 로딩한 DLL의 사용이 끝났으면 FreeLibrary 함수를 이용하여 명시적으로 해제시켜준다.
// SquareExplicitDlg.cpp
void CSquareExplicitDlg::OnDestroy()
{
CDialogEx::OnDestroy();
FreeLibrary(m_hDll);
}
명시적으로 링크된 DLL에 있는 함수를 사용하려면 GetProcAddress 함수를 이용하여 함수의 포인터를 얻어야 한다. GetProcAddress 함수는 첫번째 매개변수로 HINSTANCE를 두번째 매개변수로 함수명을 받아 그 함수의 포인터를 반환해 준다.
// SquareExplicitDlg.cpp
void CSquareExplicitDlg::OnEnChangeEditInput()
{
UpdateData();
typedef double (SquareInDLL)(double);
SquareInDLL *pSquare;
VERIFY(pSquare = (SquareInDLL *)GetProcAddress(m_hDll, "Square"));
m_dOutput = (*pSquare)(m_dInput);
UpdateData(FALSE);
}
kimgopa/mfc_project
visual c++ mfc project. Contribute to kimgopa/mfc_project development by creating an account on GitHub.
github.com
암시적 연결 / 명시적 연결
명시적 연결 방법이 훨씬 번거롭다. 따라서 일반적으로 암시적 연결 방법을 사용하는 것이 좋다. 명시적 연결 방법은 DLL 파일을 지정하여 사용할 경우에 따라서는 유용하게 사용할 수 있다.
DLL의 데이터
윈도우는 하나의 프로그램이 다른 프로그램의 영역에 침범하여 시스템에 손상을 주는 일을 방지하기 위해 각각의 프로세스가 다른 프로세스의 데이터 영역을 침범하지 못하도록 철저히 방어를 하고 있다. DLL의 데이터도 마찬가지여서 DLL의 코드는 여러 프로그램에서 공유되지만 데이터는 프로그램마다 각각 독립적으로 관리된다. 서로 다른 프로세스가 메모리를 공유하려면 메모리 맵 파일(Memory Mapped File)이라고 하는 기술을 사용해야 한다.
자동 변수는 스택에 저장된다. 각각의 프로세스는 자신의 스택을 갖고 있고, 거기에 자신의 자동 변수를 저장한다. 그런데 DLL은 자체 스택을 갖고 있지 않고, DLL을 호출한 프로그램의 스택을 이용한다. 따라서 DLL의 자동변수는 DLL을 호출한 프로그램의 스택에 각각 독립적으로 저장된다.
DLL에서 할당한 전역 변수 역시 DLL 자신의 소유가 아니라, DLL을 호출한 프로그램의 소유가 된다. 따라서 DLL을 호출한 프로그램이 종료되면, DLL은 아직 살아 있다고 하더라도 이 프로그램이 할당한 모든 전역 메모리 공간이 해제된다.
DLL 디버깅
DLL은 단독으로 실행될 수 없기 때문에 단독으로 디버깅도 할 수 없다. 따라서 DLL 프로젝트의 설정에서 아래와 같이 DLL을 사용하는 EXE 파일 경로를 적어주면 된다.

'C, C++, MFC' 카테고리의 다른 글
| 토이 프로젝트 - 캘린더 프로그램 (2) (0) | 2021.06.03 |
|---|---|
| 토이 프로젝트 - 캘린더 프로그램 (1) (0) | 2021.05.31 |
| 모달(Modal)과 모달리스(Modeless) (0) | 2021.02.05 |
| TCP/IP 프로그래밍 - 채팅 프로그램 (0) | 2021.01.06 |
| TCP/IP 프로그래밍 - 범용 소켓 클래스 (0) | 2020.12.31 |