Visual C++ DLL (Dynamic Link Library) 사용 방법 정리
차 례
DLL (Dynamic Link Library)
DLL 종류
DLL 링크 방법
선택의 길
Regular DLL(일반 DLL) 작성
Extention DLL(확장 DLL) 작성
Implicit Linkage (암시적 연결) 사용
Delay Loading(지연 로딩)
Explicit Linkage(명시적 연결) 사용
DEF 파일(.def) - Module Definition File
DllMain() 함수
DLL 디버깅
DLL과 EXE project를 한 곳에서 작업
DLL (Dynamic Link Library)
DLL은 코드만 공유될 뿐 데이터는 독립적으로 관리 됨.
DLL은 자체 스택이 없기 때문에 호출한 프로그램의 스택 사용.
DLL에서 여러 프로세스가 메모리 공유하려면 Memory Mapped File 사용해야 함.
DLL 종류
1. Regular DLL(일반 DLL): C함수 형태로 작성되어 C++, 다른 언어 에서도 사용할 수 있는 범용 DLL
2. Extention DLL(확장 DLL): C++로 작성하며 Visual C++에서만 사용 가능. (Class Export)
DLL 링크 방법
1. implicit linkage(암시적 연결)
.dll, .lib 파일 필요.
project에 lib 추가 후 header(.h) 정의하여 호출.
2. Delay Load (지연 로딩)
implicit linkage 와 explicit linkage의 혼합 방식.
특별한 경우에 사용될 수 있음.
3. explicit linkage(명시적 연결)
.dll 파일 경로 지정하여 사용.
LoadLibrary(), GetProcAddress(), FreeLibrary() 로 호출.
**.참고 : MFC DLL 링크 설정 MFC도 DLL 이므로 linkage 방식 설정 가능. MFC project property의 "Configuration Properties > General > Use of MFC"에서 Use MFC in a Shared DLL 혹은 Use MFC in a Static Library 로 동적 링크 사용 여부 선택. |
**.참고 : .dll 파일 찾는 순서 1).dll을 사용하는 .exe 파일의 디렉토리 2)process의 현재 디렉토리 3)윈도우 시스템 디렉토리(C:\WINDOWS\system32) 4)윈도우 디렉토리(C:\WINDOWS) 5)환경변수 PATH에 지정된 디렉토리 |
**. 참고 : extention DLL(확장 DLL) 과 explicit linkage(명시적 연결) 사용 별로 안 좋은 방법인가? 거의 안쓰이는 듯 하다. (시도 해봤는데 소스만 더럽히는 기분) 댓글에서 본 방법은 DLL의 class 안에 해당 클래스를 넘겨주는 static 팩토리 메소드 패턴을 구현하고 이 메소드를 노출시킨다. EXE project 에 .dll을 배포하고 DLL class의 헤더를 include 한다. LoadLibrary() 로 explicit linkage 한 후 GetProcAddress() 로 해당 메소드를 호출하여 DLL에 정의된 class를 리턴 받는다. 서핑으로 찾은 자세한 설명은 http://dolphin.ivyro.net/file/windows_api/dll_class.html <내용이 사라질까봐 저장 해둠> |
선택의 길
언어에 상관 없이 하려면 (C/C++/Delphi/VB) Regular DLL(일반 DLL)
clss 사용 및 VC++ 에서만 사용할거라면 Extention DLL(확장 DLL)
많이 이용될 거라면 implicit linkage(암시적 연결)
프로그램에 특화된거라면 explicit linkage(명시적 연결)
특수한 경우 Delay Load(지연 로딩)
Extention DLL과 explicit linkage의 조합은 최악이다.
(개인적인 생각 : 너무 지저분해 보인다.)
Regular DLL(일반 DLL) 작성
1. DLL project 생성
MFC 의 경우 project 생성
"new project > MFC > MFC DLL" 로 project 생성.
"Application Settings" 에서
"Regular DLL using shared MFC DLL" 혹은 "Regular DLL with MFC statically linked" 선택
Win32 의 경우 project 생성
"new project > Win32 > Win32 project" 로 project 생성.
"Application Settings" 에서
"DLL" 선택
("Static Library"는 컴파일시 exe 파일에 포함됨)
**. 참고 : Automation 설정 MFC DLL project 생성시 "Application Settings" 에서 "Additional features"의 Automation 을 체크할 경우 IDispatch 인터페이스를 구현하여 pointer 미지원 언어에서도 사용할 수 있게 한 기능. Dispatch ID를 이용하여 함수 호출. |
2. DLL의 C 함수 작성
extern "C" __declspec(dllexport) int plus(int a, int b) { AFX_MANAGE_STATE(AfxGetStaticModuleState());//MFC에서 호출 가능하도록 설정 return a+b; } |
보통 해더를 DLL 및 EXE(DLL 사용 프로그램) 양쪽에서 모두 사용하기 위해 다음과 같이 정의 한다. (관례적이 된듯)
//XXXX.h #ifdef DLLBUILD #define DLLEXPORT extern "C" __declspec(dllexport) #else #define DLLEXPORT extern "C" __declspec(dllimport) #endifDLLEXPORT int plus(int a, int b); //XXXX.cpp #define DLLEXPORT #include "XXXX.h" .. |
Extention DLL(확장 DLL) 작성
DLL에서 class 사용시 extention dll 임.
1. MFC의 경우
MFC dll은 MFC application에서만 사용 가능.
project 생성
"new project > MFC > MFC DLL" 로 project 생성.
"Application Settings" 에서
"MFC extension DLL" 선택
DLL 작성
class 생성 및
class 헤더에 AFX_EXT_CLASS 추가.
Test.h #pragma once class AFX_EXT_CLASS CTest { public: CTest(void); ~CTest(void); int plus(int a, int b); }; Test.cpp #include "StdAfx.h" #include "Test.h" CTest::CTest(void) {} CTest::~CTest(void){} int CTest::plus(int a, int b) { return a+b; } |
implecit linkage (암시적 연결) 사용
.h, .dll, .lib 을 읽을 수 있게 위치 시킴.
#include "Test1.h" #pragma comment(lib, "XXXXX.lib") CTest test; int ret = test.plus(1, 2) ; |
2. Win32 의 경우
win32/MFC application 에서 사용 가능.
project 생성
"new project > Win32 > Win32 project" 로 project 생성.
"Application Settings" 에서
"DLL" 선택
("Static Library"는 컴파일시 exe 파일에 포함됨)
class 생성
Test1.h #pragma once #ifdef DLLEXPORT #define CINTDLL __declspec(dllexport) #else #define CINTDLL __declspec(dllimport) #endif class CINTDLL CTest1 { public: CTest1(void); ~CTest1(void); int plus(int a, int b); }; Test1.cpp #include "StdAfx.h" #define DLLEXPORT #include "Test1.h" CTest1::CTest1(void) {} CTest1::~CTest1(void){} int CTest1::plus(int a, int b) { return a+b; } |
implecit linkage(암시적 연결) 사용
.dll, .lib 을 읽을 수 있게 위치 시킴.
#include "Test1.h" #pragma comment(lib, "XXXXX.lib") CTest test; int ret = test.plus(1, 2); |
3. DLL 에서 CString 매개변수 사용의 문제
CString을 매개변수로 갖는 DLL의 method 사용시 해당 method를 찾지 못하는 컴파일 에러가 발생한다.
작성한 코드
//호출쪽 코드(MFC)CString str = _T("test");cls.configure(str);//DLL쪽 코드 (Extention DLL)void CTest::configure(CString& str){//이런 저런 처리..} |
error LNK2001: unresolved external symbol "__declspec(dllimport) public: static void __cdecl CTest::configure(... 어쩌고 저쩌고.. |
원인은 CSttring의 실제 구현이 다르기 때문이다. (혹은 VC++ 버전에 따라 구현이 다를 수 있다.)
(MFC의 CString은 cstringt.h에 있고 Win32의 CString은 atlstr.h 이니 실제로 보면 다른듯..)
해결방법으론 CString 대신 LPCTSTR로 주고 받으면 되지만 가급적이면 char 같은 Native한 데이터형이나
STL을 사용하는게 나은 것 같다. (DLL에서 STL 사용은 Export 문제가 찝찝해질 수도 있지만 래핑한다면 뭐..)
어쩃든 꼭 CString을 써야 한다면 LPCTSTR 을 사용하는 방법이 있다.
수정된 코드
//호출쪽 코드(MFC)CString str = _T("test");cls.configure((LPCTSTR)str);//DLL쪽 코드 (Extention DLL)void CTest::configure(LPCTSTR str){//이런 저런 처리..} |
(난 이분들보다 3년이 늦었다.. 잃어버린 세월이여..)
.lib : "project property > Linker > General > Additional Library Directories" 에 해당 위치 지정
2. 라이브러리 추가
#pragma comment(lib, "XXXXX.lib") 혹은 "project property > Linker > Input > Additional Dependencies"에 .lib 파일 기술 |
3. 해더 정의 및 사용
extern "C" __declspec(dllimport) int plus(int a, int b); //편의를 위해 .h 정의 및 배포...int ret = plus(1, 2); |
Delay Loading(지연 로딩)implicit linkage 와 explicit linkage의 혼합 방식으로실제 DLL 함수가 처음 호출될떄 DLL이 로딩된다.사용방법은 implicit linkage 와 동일하며 project property 설정을 통해 Delay Loading을 설정한다.설정 방법은"exe project property > Link > Input > Additional Dependencies" 에 DelayImp.lib 추가."exe project property > Link > Input > Delay Loaded DLLs" 에 .lib 파일명 추가.
**. 참고 : DLL Unload 과 __FUnloadDelayLoadedDLL2() 함수"exe project property > Linker > Advanced > Delay Loaded DLL" 에서 Support Unload(/DELAY:UNLOAD) 선택시__FUnloadDelayLoadedDLL2("XXXXX.dll"); 를 통해 DLL Unload가 가능하다. |
Explicit Linkage(명시적 연결) 사용.dll 파일만 필요.
#include <iostream>#include <tchar.h>#include <windows.h>using namespace std;void printError(TCHAR *errMsg){TCHAR* lpOSMsg;FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpOSMsg, 0, NULL);_tprintf(_T("[ERROR] %s :: %s\n"), errMsg, lpOSMsg);LocalFree(lpOSMsg);}int main(){setlocale(LC_ALL, "");HMODULE hDLL;try{hDLL = LoadLibrary(_T("TestMFCDLL.dll"));if (hDLL == NULL)throw _T("LoadLibrary error");typedef int (DLL)(int, int);DLL* funcDLL = (DLL*)GetProcAddress(hDLL, "plus");//DLL* funcDLL = (int (*)(int, int))GetProcAddress(hDLL, "plus");int ret = (*funcDLL)(1, 5);_tprintf(_T("ret = %d\n"), ret);}catch (TCHAR* errmsg){printError(errmsg);}if (hDLL) FreeLibrary(hDLL);} |
DEF 파일(.def) - Module Definition File
.dll의 속성 정의 파일.
(.exe에선 더이상 사용되지 않으며 .dll 에서도 사라질 운명)
DLL프로젝트명.DEF 파일을 만들어 프로젝트에 포함시킨다.
ex]
LIBRARY DLLProject
EXPORTS
MyFunc1=fucntion1
MyFunc2=function2
LIBRARY는 프로젝트 명이며
EXPORTS 에서 function1 함수는 MyFunc1로 이름을 재정의 한다.
표현식] EXPORTS 익스포트명[=내부함수명][@서수]
.def 파일 사용하지 않고 export 하는 방법
파일 맨 아래에 다음처럼 정의
#pragma comment(linker, "/export:함수명=_함수명@인자총바이트수")
ex)
DLL_METHOD DWORD WINAPI PerfOpen(LPCTSTR lpDevNames) {...}
DLL_METHOD DWORD WINAPI PerfClose(VOID) {...}
#pragma comment(linker, "/export:PerfOpen=_PerfOpen@4")
#pragma comment(linker, "/export:PerfClose=_PerfClose@0")
자세한 설명 : http://b4you.net/blog/115?category=0
DllMain() 함수
DLL이 메모리에 처음 올라올때와 제거될때 호출됨.
int DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved);
hInstance : DLL의 인스턴스 핸들
dwReason : 함수 호출 이유
DLL_PROCESS_ATTACH : implicit linkage인 경우 DLL 사용 프로그램 실행시를 의미한다.
explicit linkage인 경우 LoadLibrary 실행시를 의미한다.
DLL_THREAD_ATTACH : 스레드 생성시 마다 호출되어 스레드별 초기화를 수행할 수 있다.
첫 스레드는 DLL_PROCESS_ATTACH 로 전달된다.
DLL_THREAD_DETACH : 스레드 종료시 마다 호출되어 스레드별 종료처리를 수행할 수 있다.
DLL 로드 전에 생성된 스레드가 있을 경우 DLL_THREAD_DETACH 만
전달 될 수 있으므로 초기화된 스레드인지 체크 필요.
DLL_PROCESS_DETACH : implicit linkage의 경우 DLL 사용 프로그램 종료시를 의미한다.
explicit linkage의 경우 FreeLibrary 실행시를 의미한다.
lpReserved: TRUE면 implicit linkage, FALSE면 explicit linkage
DLL 디버깅
DLL project 디버깅(F5)시 Executable file name 에 DLL 호출 프로그램 지정.
또는
"project property > Debugging > Command" 에서 DLL 호출 프로그램 지정.
혹은 아래 "DLL과 EXE project를 한 곳에서 작업"의 방법으로 디버깅 한다.
DLL과 EXE project를 한 곳에서 작업
(여러개 project를 한곳에서 사용)
1. EXE project 에서 DLL project 추가 "File 메뉴 > Add > Existing Project..."
(또는 "New Project..." 로 새로 생성)
2. Solution Explorer 에서 EXE project 이름 오른쪽 마우스 메뉴에서
"Set as StartUp project" 선택. (지정된 project는 bold체로 표시됨)
3. 각 project property의 "General > Output Directory"를 같은 곳으로 맞추어 동일 폴더에
.dll, .exe 생성하도록 설정.
또는
project property의 "Build Events > Post-Build Event" 에 직접 copy 명령등을 써서
사용할 수 있다.
출처 :
'프로그래밍 > MFC' 카테고리의 다른 글
MFC :: MFC 에서 RTTI 구현 원리 (0) | 2011.08.28 |
---|---|
MFC :: ASSERT_VALID 매크로 (0) | 2011.08.28 |
MFC :: 유니코드 to 멀티바이트 or 멀티바이트 to 유니코드 (0) | 2011.01.27 |
MFC :: 디버그 뷰 (실시간 데이터 출력) (1) | 2011.01.26 |
MFC :: MainFrame,Doc,View의 포인터 얻기 (1) | 2010.11.19 |
댓글 로드 중…