Visual Studio 정적(Static)/동적(Dynamic) 라이브러리 만들기

 
 
Introduction

블로그에 포스팅을 하지 않으려고 하였지만, 어느새 자료를 찾아 하나씩 정리하다보니, 블로그에 포스팅을 해야겠다는 생각을 했습니다. 당연히 향후 작업할 때 찾아보기 쉽게 정리하기 위해서죠. 소스코드도 정리해서 올려둬야 할까? 고민이 되네요. 
 
일단 궁금한 점들이 있어 먼저 이야기를 해볼까 합니다. Windows 기반에서 Library를 사용하는데 있어 정적(static)/동적(Dynamic) 라이브러리를 사용하게 됩니다.  이부분에 대해서는 크게 설명하지 않아도 인터넷자료를 찾아보면 많이 나옵니다. 하지만, Visual Studio를 열고나서 라이브러리를 만들고자 할 때, 당황스러운 상황이 발생하게 됩니다. 아래의 설명을 보시면 알겠지만, 라이브러리를 만들 수 있는 방법이 여러가지가 있습니다. 이부분에 대해 조금 더 정리 해두고, 사용이 뜸한 DLL 프로젝트의 설정방법을 정리하고자 합니다.  
 
먼저 하나씩 차근차근 살펴 보도록 하겠습니다. 
 
 
정적 라이브리리(Static Link) Vs 동적 라이브러리(Dynamic Link)

 
#정적(static) 라이브러리 
참조: Windows API 정복 책에서 발췌
 
 
#동적(Dynamic) 라이브러리 
참조: Windows API 정복 책에서 발췌
 

 

 

 
#정적 Vs  동적 라이브러리의 차이점
  • 정적(static) 링크
    • 컴파일 시 함수가 실행 파일에 연결
    • 실행 파일에 함수의 코드가 복사되기 때문에 실행 파일의 크기가 커지는 단점이 있으나, 실행 파일은 단독 실행 가능
    • 실행 파일에 함수의 코드가 포함되어 있어 컴파일이 끝나면 라이브러리 파일(Lib) 가 없어도 프로그램 실행 가능
  • 동적(Dynamic) 링크
    • 메모리 효율성 증대
    • DLL 파일의 교체로 알고리즘 변경 가능
    • 리소스의 교체 가능
      • 한글/영문/ 등등 변환 
    • 실행 시 함수가 실행 파일에 연결
    • 실행 파일에는 호출할 함수의 정보만 포함되고 실제 함수 코드는 복사되지 않으므로 실행 파일의 크기가 작아짐
    • 하지만 실행 파일은 함수에 대한 정보만 가지고 있기때문에 실행시 DLL 파일이 꼭 있어야 함
    • 장점
 
 
라이브러리의 묵시젹 연결 Vs 명시적 연결 방법

  • 묵시적 연결(Implicit)
    • 함수가 어느 DLL에 있는지 밝히지 않고 그냥 사용
    • 프로젝트에 임포트 라이브러리를 포함해 줘야 함
    • 실행시 연결(Load time linking)
  • 명시적 연결(Explicit)
    • 어느 DLL에 있는 함수인지 밝히고 사용하는 방법
    • 클라이언트 프로그램이 실행될 때 DLL이 로드 되는 것이 아니라 DLL을 로드하라는 명령이 있을 때 로드 됨
    • 필요할 때 선택적으로 DLL을 로드하므로 상황에 따른 리소스 교체가 가능함
    • DLL의 이름을 밝히고 사용하므로 임포트 라이브러리는 불필요
    • 클라이언트 프로그램 실행중에 DLL이 메모리로 읽혀지므로 실행중 연결(Run time Linking) 이라고 함
 
 
묵시적(Implicit) 연결 사용방법

 
 
  • Linker > input > Additional Dependencies 에 해당 DLL 파일 추가
 
#소스코드
#include <iostream>
using namespace std;
extern "C" __declspec(dllimport) int AddInteger(int, int);
void main() {
       int _result = AddInteger(1, 2);
       cout << _result << endl;
}
묵시적인 방법은 현재 대부분의 라이브러리를 사용할 때, 사용하는 방법입니다. 익숙한 편이죠? 
 
 
명시적 연결 사용방법

 
명시적 연결 방법은 아래와 같이 함수포인터를 이용하여 사용하고 있습니다. 특히, LoadLibrary() 함수를 이용하여 원하는 파일을 직접 호출하기도 하죠. 그 외에는 visual studio에 설정할 필요가 없습니다. 함수 포인터를 사용해야하는 점을 빼고는 코드만 붙여넣으면 되기때문에 간단하게 사용할 수 있습니다. 하지만, 코드의 충분한 테스트를 거치지 못한 경우, 아래와 같이 사용할 때 많은 문제점들이 나타난다면 또다시 온라인 바다를 떠도는 상황이 벌어지지 않을까 걱정되네요. 
 
#소스코드
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;
HINSTANCE hInst;

int _tmain(int argc, _TCHAR* argv[])
{
       int (*pFunc)(int, int);    
       if ((hInst = LoadLibrary(L"MyDll.dll")) == NULL){
              // error
       } else {
              if ((pFunc = (int(*)(int, int))GetProcAddress(hInst, "AddInteger"))  == NULL) {
                     // error
              } else {
                     cout << "1+2 = " << (*pFunc)(1, 2) << endl;
                     FreeLibrary(hInst);
              }
       }
       return 0;
}
#DLL 라이브러리 코드 참고용 
 
상위 라이브러리에서 필요한 소스코드 참고용으로 올려둡니다. (프로젝트 생성 후 아래의 코드 사용하시면 됩니다.)
extern "C" __declspec(dllexport) int AddInteger(int a, int b)
{
       return a + b;
}
 

 

 

 
생각하기

(VS2013에서 "Library" 라고 검색하면 상위 그림과 같이 다양한 라이브러리가 나타남)
 
이제 Visaul studio를 열고 DLL 파일을 만드는 방법을 알아볼까요? 그전에 아래와 같은 종류의 프로젝트에 대해 Library를 생성 할 수 있는 것을 알 수 있습니다. 
 
  • console
  • win 32 API Project
  • MFC Project
  • C# Project
  • 기타(F#, 및 기타 등등...)
 
생각보다 많이 나오더군요. 그래서 하나씩 다 만들어보았습니다. 결과를 먼저 정리하자면 다음과 같습니다. 
 
  • Win 32 API Project
    • Static/Dynamic Library 생성 가능(각 프로젝트는 개별 파일만 생성됨)
    • Console부터 Win32 API, MFC, C#, VB 등 윈도우에서 사용하는 개발툴에 다양하게 사용 가능 함
 
  • Static Library(Windows)
    • lib, dll 파일이 같이 생성됨
 
  • MFC DLL
    • 정적으로 MFC를 연결하는 기본 MFC DLL 빌드 (MFC관련 DLL 포함)
    • 동적으로 MFC를 연결하는 기본 MFC DLL 빌드 (MFC관련 DLL 미포함)
    • 항상 MFC를 동적으로 연결하는 MFC 확장 DLL 빌드 (MFC관련 DLL 미포함)
 
MSDN을 살펴보면 다음과 같이 작성되어있습니다. 라이브러리를 생성 할 때, 어떤 프로젝트를 이용하여 생성 할 수 있는지에 대한 의문이 풀릴 것입니다. 
 
#요약
  • DLL에서 MFC를 사용하지 않는 경우 Visual studio를 사용하여 Win32 DLL를 빌드 
  • MFC에 동적으로 연결 할 때 아래의 파일이 필요함(공유 DLL)
    • MFCX0.dll
    • MSVCRXX.dll (또는 유사한 파일 필요)
    • 이런 경우 VS 배포 패키지를 설치하면 해결 됨
 
내용을 참조하여 발췌하려고 하였으나 (원..하는 내용이 없네요) .. 
프로젝트를 하나씩 생성해서 조금만 살펴보면, 어떤 프로젝트를 생성해야하는지 알수 있습니다. 상단에 적어둔 내용으로 파악 할 수 있을텐데요. MFC DLL을 사용해야하는 경우는 기본적으로 MFC기반에 리소스 전용 DLL을 만들기 위함입니다.  리소스라고하면, 아이콘, 비트맵, 문자열, 대화상자(Dialog) 등을 사용할 수 있습니다.  그외에는 Win32 API Project를 이용하여 라이브러리를 생성하시는 것을 추천드립니다. (또는 static Library)
 
그럼 이제 프로젝트를 하나씩 생성해볼까요? 
 
 
Win32 Project 기반 Library 만들기 

 
 
  • Win32 Project
    • *.lib 파일만 생성되는 프로젝트
    • *.dll 파일만 생성되는 프로젝트
    • Static Library
    • DLL
 
win32 프로젝트는 라이브러리가 각각 생성되는 형태로만 생성 됩니다. 하지만, DLL, LIB 파일을 같이 생성하고 싶으시다면 아래의 프로젝트로 생성하시면 됩니다. 
 
Static Library(Windows)

 
 
  • Win32 Project 기반의 라이브러리가 생성됨 (lib,dll 파일이 생성된다)
 
물론 설정에 따라, 상위 프로젝트에서도 사용이 가능할텐데, 아직 모를 뿐..이죠. 
 

 

 

 
MFC DLL

 
 
MFC DLL을 생성하면 다음과 같이 3가지 항목을 선택 할 수 있습니다. 
 
 
3가지 항목의 차이점은 무엇일까? (상단 내용 참조)
 
  • 항목 1(Regular DLL using shared MFC DLL) / 2(Regular DLL with MFC statically linked)
    • MFC 기본 CWinApp Class를 상속받는 클래스가 생성됨
    • MFCDLL.def 파일의 생성
  • 항목 3(MFC Extension DLL)
    • DllMain() 함수가 생성되고 CWinApp Class를 상속받는 클래스 모듈이 없음
 
즉, 항목 1,2,는 MFC기반 라이브러리를 만들 때 사용하고, 그외 추가 알고리즘을 만들고자 할 때는 3번째 항목을 선택하여 만들면 된다. 
 
  • 출력 : 항목1,2,3 전부 아래의 파일이 생성됨 
    • *.dll, *.lib
 
 
실습 MFC Library 만들기

소스코드는 MSDN에서 확인 가능합니다. 
 
#순서
  • MFC DLL Project 생성
    • Regular DLL using shared MFC DLL 프로젝트 선택 (다른거 하셔도 됩니다)
  • 테스트 Project 생성
    • ..\DialogHideDLL 추가 (사용자 프로젝트에 맞춰서 수정 바람)
    • DialogHideDLL.lib 추가 (사용자 프로젝트에 맞춰서 수정 바람)
    • ..\$(IntDir) (사용자 프로젝트에 맞춰서 수정 바람 Library에서 생성되는 dll 파일 path)
    • MFCDLLTester Proejct 생성(라이브러리 프로젝트 아님)
    • [참고1] Properties > C/C++ > General > Additional include Directories
    • [참고2] MFCxxxDlg.h 파일에 DLL에 정의된 header 추가
    • [참고3] Properties > Linker > Input > Additional Dependencies
    • [참고4] Properties > Linker > General > Additional Library Directories
 
  각각의 단계는 아래 그림을 참고 해주시기 바랍니다. 기본적으로 필요한 설정인데, 익숙해지면 조금 더 편리한(?) 방법을 이용하여 프로젝트를 설정 하시면 됩니다. 무엇을 작업하시던지 기초가 튼튼해야 다음 작업을 할 수 있으니 아래의 내용도 숙지(?) 하는게 좋겠죠? : )
 
 
참고1: (Properties > C/C++ > General > Additional include Directories)
 
참고2: MFCxxxDlg.h 파일에 DLL에 정의된 header 추가
#pragma once

// include Library files 추가 
#include "fibonacci.h"
#include "CalcManager.h"

class CDialogHideTestDlg : public CDialogEx
{
    // Construction
    // 생략 ...
}
 
참고3: Properties > Linker > Input > Additional Dependencies
 
 
#참고4 : Properties > Linker > General > Additional Library Directories
 

 

 

 
빌드 후 DLL 파일을 테스트 프로젝트로 복사하는 방법

이렇게 빌드 되고나서 Test하는 프로젝트에 설정하는 방법입니다. 다른 방법은 DLL 프로젝트에서 빌드 후 테스트 프로젝트로 DLL 파일을 복사하는 방법 입니다. 방법은 간단히 post-build event 를 이용하면 됩니다. 
 
주의 : DLL 프로젝트에서 설정해야함
 
  • Property > Build Events > Post-Build Event > Command Line 추가 
 
코드 
xcopy /y /d "..\..\MathLibrary\$(IntDir)MathLibrary.dll" "$(OutDir)"
 
 
원하는 경로로 수정하여 입력을 하시면 됩니다.  저는 상단 그림과 같이 하나의 솔루션에 2개의 프로젝트를 생성해두었기때문에 동일한 Debug폴더에 생성됩니다. 참고로 Visual Studio 버전에 따라 생성되는 폴더가 다르기때문에 꼭 확인 후 작업을 진행하셔야 합니다. 
 
 
#결과

 
MSDN의 코드를 이용하여 작업을 진행하시면 상단 그림과 같이 결과물이 출력됩니다. 그럼 기본적인 라이브러리 만들기가 완성되는 것이죠. 하지만, 이제 시작 일뿐 알아가야하는 부분들이 더 많이 있습니다. 어떤 것이 있는지 다음 파트에서 정리해보도록 할까요? 
 
 
마무리 전 단계

제가 젤 처음 궁금했던 부분은 여기까지였습니다. 그럼 또 다른 궁금증이 발생한 부분이 무엇인지 알아볼까요?
 
#Think
  • MFC extension DLL 을 이용하여 Dialog DLL 만들기 및 호출 방법
  • 함수/클래스를 DLL로 만드는 방법 정리
 
몇가지 의문점들이 남았는데, 테스트 후 금방 해결이 되는 경우 포스팅에 추가할 예정입니다. (그럼 뚜뚜뚜..작업중입니다.)
 

 

 

 
MFC extension DLL 을 이용하여 Dialog DLL 만들기 및 호출 방법

  MFC에서 다이얼로그기반 DLL을 만들고, 이를 활용하는 방법에 대해 알아보도록 하겠습니다.  포스팅을 위해 대략 2시간 정도 서치를 하며 삽질 했네요. 아주아주 간단하니 사용방법의 요약을 먼저 하도록 하겠습니다.
 
#순서
  • MFC extension DLL 생성
    • 리소스(*.rc)에서 Dialog 추가
    • Dialog 클래스 추가
    • 기타 버튼 및 UI 작업 등등 (사용자 맘대로 작업)
    • [참조1] AFX_EXT_CLASS 추가 (아래 참조)
  • MFC 테스트 Project 생성
    • 상단에 테스트 관련 dll 및 path 연결 작업 수행 (궁금하면 블로그 처음부터 읽어주세요)
    • [참조2] 사용방법은 Modal/Modaless 형태로 사용 가능(아래 코드참조)
 
#참조1 : AFX_EXT_CLASS 추가
class AFX_EXT_CLASS ExtensionDLLDlg : public CDialogEx
    // 생략 ...
}
 
#참조2 : 다이얼로그 호출 방법 (테스트 프로젝트)
 
  • define_extensionDlg.h
#pragma once
// include for ExtensionDLL Library
//
#include "../ExtensionDLL/ExtensionDLLDlg.h"
#include "../ExtensionDLL/resource.h"

#define EXPORT_API __declspec(dllexport)
#define IMPORT_API __declspec(dllimport)

// additional Dependencies for ExtensionDLL files
#pragma comment(lib, "ExtensionDLL.lib" )
프로젝트를 새로 생성하면서 extension DLL과 관련된 define을 따로 관리하는 것이 좋을 것 같아 생성하였습니다. 
 
  • MFCDialogXXX.h 헤더파일 
// MFC DLL extension DLL
#include "define_extensionDlg.h"

class CDialogHideTestDlg : public CDialogEx
{
private:
    extensionDLLDlg  *pModalessDlg;
}
다이얼로그를 Modal만 사용한다면 상위 코드가 필요없지만, Modaless형태로 출력하기 원한다면 상단 소스코드 처럼 정의 하시면 됩니다. 
 
 
  • MFCDialogXXX.h 소스코드
// modal 출력
void CMFCDialogDLLTestDlg::OnBnClickedBtnLoadExtensionDlgModal()
{      
       ExtensionDLLDlg dlg;
       dlg.DoModal();
}

// modaless 출력
void CMFCDialogDLLTestDlg::OnBnClickedBtnLoadExtensionDlgModaless()
{
       if (pModalessDlg == NULL){
              pModalessDlg = new ExtensionDLLDlg(this);
              pModalessDlg->Create(IDD_EXTENSION_DIALOG1, this);
              //m_ModelessDlg->SetWindowPos(NULL, newRect.left, newRect.top,  rectParent.Width(), rectParent.Height(), NULL);
              //pModalessDlg->Setter(this);
              pModalessDlg->ShowWindow(SW_SHOW);
       }
}
 
Model 과 Modeless 출력하는 방법은 따로 함수를 만들어두었습니다. 마지막으로 프로젝트 설정과 관련하여 수정한 부분을 이야기 드리도록 하겠습니다. 
 
define_extensionDlg.h 헤더파일을 보게되면, 몇가지 추가 작업을 하였는데 포스팅된 내용을 살펴보면 아래와 같은 작업을 수행해야 합니다. 이런 작업을 매번 진행하기 번거롭기 때문에 다음과 같이 작업을 하시면 됩니다. 
 
#참고: 테스트 Project 생성
  • MFCDLLTester Proejct 생성(라이브러리 프로젝트 아님)
  • [참고1] Properties > C/C++ > General > Additional include Directories
    • ..\DialogHideDLL 추가 (사용자 프로젝트에 맞춰서 수정 바람)
  • [참고2] MFCxxxDlg.h 파일에 DLL에 정의된 header 추가
  • [참고3] Properties > Linker > Input > Additional Dependencies
    • DialogHideDLL.lib 추가 (사용자 프로젝트에 맞춰서 수정 바람)
  • [참고4] Properties > Linker > General > Additional Library Directories
    • ..\$(IntDir) (사용자 프로젝트에 맞춰서 수정 바람 Library에서 생성되는 dll 파일 path)
 
#추가 작업 
  • define_extensionDlg.h파일과 같이 작업 할 경우, 상단 참고1~4의 방법은 전부 설정하지 않아도 됨
  • [방법1] (extentionDLL 프로젝트설정) Property > Build Events > Post-Build Event > Command Line 추가 (아래 코드 참조)
    • 상단 "참고: 빌드 후 DLL 파일을 테스트 프로젝트로 복사하는 방법" 을 참고하시기 바람
 
[방법1] 코드
xcopy /y /d "$(OutDir)ExtensionDLL.dll" "..\MFCDialogDLLTest\"
xcopy /y /d "$(OutDir)ExtensionDLL.lib" "..\MFCDialogDLLTest\"
 
 
  • [방법2] (테스트 프로젝트에서의 설정) Properties > Linker > General > Additional Library Directories
 
[방법2] 코드
..\$(IntDir)
 
테스트 프로젝트에서 dll, lib 파일을 필요로 하기 때문에 상위 코드를 추가함으로써 해결할 수 있습니다. 그래서 전체 빌드(1회) 수행 시, 테스트 프로그램에서는 dll, lib 파일이 없다고 오류가 뜨게 됩니다. 그래서 생성 된 후 파일이 복사가 수행되기 때문에 한번 더 빌드를 수행하시면, 오류가 없어지니 참고 하시기 바랍니다. 
 
추가로 [방법2] 도 동일하게 사용 가능합니다. 이 해결방법도 2번 빌드해야합니다. 그리고 Debug/Release 설정은 각각 한다는 것 아시죠? 
 

 

 

 
#결과
버튼 선택시, DLL에서 만든 Dialog가 아래와 같이 출력 됩니다. 
 
 
소스코드는 (작업중...) 뚝딱. 
아래에 있으니 참고하여 사용하시기 바랍니다. 
 
그외 다른 리소스도 이렇게 활용하면 되지 않을까 합니다. 단지 문제점은 resource.h 파일을 추가할 때, 꼭 해당 MFC 프로젝트와 혼돈되지 않도록 직접 path를 입력해주시기 바랍니다. 그외 큰 문제는 없을 것이라 생각되네요. 
 
 
 
함수/클래스를 DLL로 만드는 방법 정리

이번 파트를 마지막으로 하려고 합니다. 포스팅 된 내용을 따라하여 Dialog를 DLL로 생성하여 호출하는 과정을 살펴보았습니다. 이번에는 기존에 만들어둔 Class를 DLL로 만들어서 호출 하는 방법을 알아보려고 합니다. 그 전에 함수를 이용하여 호출 하는 방법은 상단에 MSDN(연습: 자체 동적 연결 라이브러리 만들기 및 사용(C++))을 참조하면 쉽게 해결 할 수 있습니다. 함수의 모양만 바꾸면 되는 것이죠. 
 
물론, 클래스의 경우도 wrapping 하는 함수를 만들어서 사용할 수 있습니다. 하지만, 클래스 원형 그대로 사용하고 싶지 않나요? 그래서 몇가지 테스트를 해본 결과 사용방법을 이야기 드리도록 하겠습니다. 
 
#참고1 : AFX_EXT_CLASS 
class AFX_EXT_CLASS ExtensionDLLDlg : public CDialogEx
{
}
 
앞서 사용했던 AFX_EXT_CLASS를 기존의 다른 소스코드에도 추가해보았습니다. 
 
ExtensionDLL 프로젝트 : Calcmanager.h 
#pragma once
class AFX_EXT_CLASS CCalcManager
{
public:
       CCalcManager();
       ~CCalcManager();
public:
       int add(int a, int b);
       int mul(int a, int b);
};
 
ExtensionDLL 프로젝트 : Calcmanager.cpp
#include "stdafx.h"
#include "CalcManager.h"
CCalcManager::CCalcManager()
{
}
CCalcManager::~CCalcManager()
{
}
int CCalcManager::add(int a, int b)
{
       return a + b;
}
int CCalcManager::mul(int a, int b)
{
       return a * b;
}
 

 

테스트 프로젝트 : define_extensionDlg.h
// 생략 ...
// 아래 추가 
#include "../ExtensionDLL/CalcManager.h"
 
테스트 프로젝트 내부 소스코드
void CMFCDialogDLLTestDlg::CalcManagerTest()
{
       CCalcManager Manager;
       int ret1 = Manager.add(1, 5);
       int ret2 = Manager.mul(2, 10);
       int a = 5;
}
간단하게 사용 가능하였습니다.  복잡한 코드 일 경우, 어떻게, 어떤 오류가 발생할지는 잘 모르겠으나 그래도 문제가 생기면 찾으면서 해결하면 되지 않을까 하네요. 
이제 마음 것 C/C++의 코드를 Library형태로 만들어서 각종(?) 코드에 이식해서 사용하도록 해야겠습니다. 
 
이모티콘이 없는게 슬프지만, /웃음/
 
다른 포스팅 보다 조금 짧은 시간 내 작성했는데, 이만 휴식하로 가야겠습니다. (5일 뒤에 업로드 되는 포스팅...!!)

이 글을 공유하기

댓글(0)

Designed by JB FACTORY