728x90

안녕하세요. 공유민입니다.

요즘 DLL에 관해 공부 중인데 정리한 것을 포스팅해봅니다. 틀린 점이 있다면 댓글로 피드백 주세요.







동적 링크 라이브러리(Dynamic Link Library)



실행 파일에 직접 덧붙여지지 않고, DLL 파일에 독립적으로 존재하다가 프로그램이 실행될 때 동적으로 링크되어 사용됩니다.

 

장점

㉠ 메모리와 하드 디스크를 절약할 수 있습니다. 여러 프로그램에서 공통으로 사용하는 경우, 하나의 DLL을 공유하면서 동적으로 링크되어 사용하기 때문입니다.

㉡ 프로그램 실행 속도가 빨라질 수 있습니다. 실행 프로그램이 작아지고 로드만 하면 되기 때문입니다.

㉢ 프로그램이 모듈화 됩니다. DLL 단위로 모듈화가 되기 때문에 각각을 독립적으로 개발할 수 있습니다. 업그레이드 시에도 해당 부분에 DLL 파일만 새로 설치하면 됩니다.

 

단, 동적 링크 라이브러리는 단독으로 실행될 수 없습니다.





DLL의 종류



㉠ Regular DLL with MFC statically linked

MFC 정적 라이브러리와 연결되는 일반 DLL로 내부적으로 MFC를 사용하는 DLL입니다. DLL의 Export 함수는 다른 언어 툴에서 작성된 어플리케이션에서도 호출할 수 있고, 표준 C언어 인터페이스를 사용하여 외부에 Export 시킬 수 있습니다. MFC 정적 라이브러리와 링크되는 일반 DLL은 DLL 자체가 CWinApp 클래스로부터 인스턴스여야 하고, MFC에서 제공하는 DllMain 함수를 사용합니다. 여기서 DllMain 함수는 DLL의 초기화와 종료 시 여러 가지 작업을 처리하는 함수입니다. 그리고 DLL의 함수를 Export 시키려면 함수의 선언 부분에 extern “C”와 같이 Export 함수 자체를 C언어 형태로 선언해야 합니다.


㉡ Regular DLL using shared MFC DLL

MFC 공유 라이브러리를 사용하는 일반 DLL로, MFC공유 라이브러리와 동적으로 연결됩니다. 이러한 DLL은 MFC 공유 DLL을 같이 제공해야 합니다. MFC 공유 라이브러리를 사용하는 일반 DLL과 MFC 정적 라이브러리와 연결되는 일반 DLL의 차이점은 MFC 라이브러리를 동적으로 연결하기 위해 MFC DLL을 같이 제공하는지, MFC 라이브러리를 정적으로 연결하기 위해 MFC DLL을 포함하는지만 다를 뿐, 두 일반 DLL은 별 차이가 없습니다. 즉, MFC 공유 라이브러리를 사용하는 일반 DDL도 CWinApp 클래스로부터 파생된 인스턴스여야 하고, DllMain 함수를 사용하며, DLL 함수를 Export 시키기 위해 함수의 선언 부분에 Export 함수 자체를 C언어 형태로 선언해야 합니다.


㉢ MFC Extension DLL (using shared MFC DLL)

MFC 확장 DLL은 MFC에서 제공하는 모든 클래스의 파생 클래스로 구성되는 DLL입니다. 확장 DLL은 MFC 라이브러리와 동적으로 연결되어 사용되지만 MFC로 작성된 어플리케이션 프로그램과 MFC 라이브러리와 동적으로 연결되어 있는 일반 DLL에서만 사용할 수 있습니다.

MFC 확장 DLL에서는 C 함수를 사용하여 Export 시키는 대신 AFX_EXT_CLASS 매크로를 사용하여 클래스 전체를 Export 시킵니다. 즉  다음과 같이 AFX_EXT_CLASS 매크로를 추가시켜 주기만 하면 됩니다.

 

 class AFX_EXT_CLASS CExDllDlg : public CDialog

 {

 }

 

이와 같이 MFC 확장 DLL은 어플리케이션과 DLL 사이에 C++ 언어 기반의 인터페이스를 사용합니다. 


 

DDL 유형

MFC 사용

일반 DLL

(Regular DLL)

정적

MFC 관련 DLL 포함되어 있기 때문에 공유에 비해 크기가 크지만 배포할 때에 DLL 파일만 있으면 된다.

C 언어 인터페이스 사용

공유

MFC 관련 DLL 포함되어 있지 않기 때문에 정적에 비해 크기는 작지만 배포할 때에 MFC 관련 DLL 같이 배포되어야 한다.

C 언어 인터페이스 사용

확장 DLL

(Extension DLL)

공유

MFC 어플리케이션 프로그램과 MFC 라이브러리와 연결된 일반 DLL에서만 호출된다.

C++ 인터페이스 사용






DLL과 프로그램 간의 연결 방법


㉠ 묵시적 연결(implicit linking)

묵시적 연결은 정적 로드 또는 로드 시간을 지정한 정적 연결이라고도 합니다. 묵시적 연결은 DLL을 생성할 때 DLL과 함께 제공되는 묵시적 연결을 사용하여 다른 어플리케이션에 연결하는 방법입니다. 여기서 import library란 *.lib 파일을 말합니다. import library로 사용되는 lib 파일의 경우에는 DLL 모듈을 로딩하고 export 함수의 위치를 자동적으로 찾아내는 역할을 하게 됩니다. 이러한 import library를 사용하여 DLL 을 연결하면 윈도우 운영체제는 프로그램이 실행될 때 프로그램에서 필요로 하는 DLL을 연결하면 윈도우 운영체제는 프로그램이 실행될 때 프로그램에서 필요로 하는 DLL을 함께 로드합니다. 그러면 프로그램에서는 DLL export 된 함수를 마치 프로그램 자체에 포함된 내부 함수처럼 사용할 수 있습니다.

 

㉡ 명시적 연결(explicit linking)

명시적 연결은 동적 로드, 또는 런타임 동적 연결이라고도 합니다. DLL을 사용하는 어플리케이션은  DLL을 로드하고, 해제하는 작업을 명시적으로 수행해야 하며 DLL의 export된 객체나 함수를 액세스 하는 함수를 포함하고 있어야 합니다. 어플리케이션이 export 객체를 액세스 할 때에는 객체 포인터를 사용해야 하며 export 함수를 호출할 때에는 함수 포인터를 이용하여 함수를 호출해야만 합니다.






파일러가 dll 파일을 찾는 순서



① DLL 호출한 EXE 파일이 있는 디렉터리
② 프로세스의 현재 디렉터리
③ 윈도우 시스템 디렉터리
④ 윈도우 디렉터리
⑤ PATH 환경 변수에 지정된 디렉터리

그러므로 묵시적 연결을 쓰신다면 위 5가지 경로 중 한 군데에 dll 파일을 위치시켜야 합니다.






DLL 만드는 Project



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// API.h
 
#ifndef __StrMakerAPI_H__
#define__StrMakerAPI_H__
 
#ifdef __cplusplus
 
// extern “C”가 계속 사용되므로 묶어서 간단하게 표현
extern "C" {
#endif
 
/* 
1. DLL 프로젝트에는 Project - Settings - C/C++ - Preprocessor – Preprocessor definitions 위치에 
STRMAKERDLL_EXPORTS 추가 
2. __declspec(dllexport)와 __declspec(dllimport)를 계속 사용하지 않게 정의함 
*/
#ifdef STRMAKERDLL_EXPORTS
    #define STRMAKERDLL_API      __declspec(dllexport)
    #define STRMAKERDLLCALL      __stdcall
#else
    #define STRMAKERDLL_API      __declspec(dllimport
    #define STRMAKERDLLCALL      __stdcall
#endif
 
STRMAKERDLL_API int STRMAKERDLLCALL CreateSTR(
    int   nLang,
    char* pszTitle,
    char* pszContents,
    char* pszFilePath
    );
 
#ifdef __cplusplus
// extern "C" 묶음 끝
#endif
#endif  // __STRMakerAPI_H__
cs



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//API.cpp
 
#include "stdafx.h"
#include <stdio.h>
#include "StrMakerAPI.h"
#include "resource.h"
 
int STRMAKERDLLCALL CreateStr(
    int   nLang,
    char* pszTitle,
    char* pszContents,
    char* pszFilePath
    )
{
    …함수 구현…
}
cs






DLL 사용하는(exe) Project



1. 명시적 연결 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// exe.cpp
// DLL파일(*.dll)을 실행할 exe 프로젝트 경로에 복사합니다. 
 
#include "stdafx.h"
#include "stdio.h"
#include <windows.h>
 
// DLL에서 호출할 함수형
typedef int (*CreateStr)(intchar*char*char*);
 
int main(int argc, char* argv[])
{
     CreateStr STRMaker;
 
    // DLL 로드
    HINSTANCE hmodule = LoadLibrary("DLL.dll");
 
    if(hmodule == NULL)
    {
        printf("dll 로드 실패 \n");
        return -1;
    }
    printf("dll 로드 성공\n");
    
    // 호출한 함수를 맵핑
    StrMaker = (CreateStr)GetProcAddress(hmodule, "CreateStr");
    
    int     nLang = 1;
    char*   pszTitle = "제목 샘플 1234";
    char*   pszContents = "내용 샘플 5678";
    char*   pszFilePath = "../../Bin/ExeSample.bmp";
    int nRet = StrMaker ( 
                        nLang, 
                        pszTitle, 
                        pszContents, 
                        pszFilePath
                        );
    printf("%d\n", nRet);
    FreeLibrary(hmodule);
    return nRet;
}
cs


* GetProcAddress 에서 에러발생시, GetLastError()를 호출하였을때, 리턴값이 127이면,

 

typedef int (__stdcall *CreateStr)(intchar*char*char*); 을 붙여서 시도해본다.

__stdcall, __cdecl 구분하기 때문이다.

* 혹은 def 파일에 이름을 정의안하면 네이밍이 안 맞아 불러오지 못하는 것일수도 있다.
아래처럼 def 파일에 네임을 추가한다.

EXPORTS
; Explicit exports can go here
CreateStr



2. 묵시적(암시적) 연결 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// exe.cpp
/*
1. 헤더파일(*.h), 라이브러리 파일(*.lib), DLL파일(*.dll)을 실행할 프로그램 경로에 복사합니다.
2. 만약, 다른 위치에 헤더파일과 라이브러리 파일이 있을 경우
project – settings – C/C++ - Category : Preprocessor Additional include directories – 헤더파일 경로 추가
project – settings – Link – Category : Input – Additional library path – 라이브러리 경로 추가
 */
 
#include "stdafx.h"
#include "stdio.h"
#include <windows.h>
#include "StrMakerAPI.h"
 
/* 
lib 파일을 포함시키는 명령입니다. 컴파일된 라이브러리의 lib파일을 해당 소스가 
컴파일시에 컴파일러가 필요하기 때문입니다. 반드시 문장 상위에 표시하여야 합니다. 
*/
#pragma comment(lib, "DLL.lib")  
 
int main(int argc, char* argv[])
{   
    int     nLang = 1;
    char*   pszTitle = "제목 샘플 1234";
    char*   pszContents = "내용 샘플 5678";
    char*   pszFilePath = "../../Bin/ExeSample.bmp";
    
    int nRet =  CreateStr(
                        nLang,
                        pszTitle,
                        pszContents,
                        pszFilePath
                        );
    printf("%d\n", nRet);
    return nRet;
}
cs

설명은 주석을 참조하세요.



+ Recent posts