Search
Duplicate

C++/ DLL 만들고 사용하기

C++에서 DLL 만들기

일단 새 프로젝트를 DLL(동적 연결 라이브러리)로 만든다.

전처리기

DLL로 내보낼 —외부에서 사용될— 파일의 header 파일에 다음과 같은 전처리문을 추가한다. —cpp 파일에는 별도로 설정할 필요가 없다. cpp는 일반적인 프로젝트와 동일하게 사용하면 된다.
#ifdef (프로젝트 이름)_EXPORTS #define (매크로 이름) __declspec(dllexport) #else #define (매크로 이름) __declspec(dllimport) #endif
C++
복사
여기서 (프로젝트 이름)은 Visual Studio에서 프로젝트 속성 → C/C++ → 전처리기 → 전처리기 정의에 있는 이름을 의미한다.
이렇게 정의된 전처리문은 DLL로 내보낼 함수와 클래스에 동일하게 사용된다. 이를 사용하는 방법은 아래 코드와 같다. —아직 아래 코드만으로는 내보내기가 안되므로 주의
#ifdef MATHLIBRARY_EXPORTS #define MATHLIBRARY_API __declspec(dllexport) #else #define MATHLIBRARY_API __declspec(dllimport) #endif MATHLIBRARY_API void fibonacci_init(const unsigned long long a, const unsigned long long b); MATHLIBRARY_API bool fibonacci_next(); MATHLIBRARY_API unsigned long long fibonacci_current(); MATHLIBRARY_API unsigned fibonacci_index();
C++
복사
함수 앞에 전처리문 MATHLIBRARY_API를 쓰지 않고 그냥 __declspec(dllexport)라고 써도 무방하다. 다만 그렇게 쓰면 코드가 지저분해 보이기 때문에 MATHLIBRARY_API와 같이 별도의 전처리문을 사용하는 것이다.

extern "C"

__declspec(dllexport) 키워드만으로 DLL export가 되면 편할텐데 추가로 한 단계가 더 필요한데, 그것이 바로 extern "C" 키워드 이다. —extern 키워드에 "C"가 붙은 것은 C 스타일로 한다는 뜻이다. C 스타일로 extern 하는 것이기 때문에 Template에 대해서는 사용할 수 없다.
extern "C" 의 의미는 C++ 컴파일러에게 네임 맹글링(Name Mangling)을 하지 말라고 선언하는 내용인데, 네임 맹글링이란 C++ 링커가 동일한 함수명을 구분하기 위해 적절한 규칙을 이용해서 함수명을 변형하는 것을 말한다.
이게 무슨 말이냐면 C++은 C와 달리 다른 네임스페이스나 클래스에 속한 함수가 같은 이름을 가질 수 있고, 또 같은 범위(scope)에 존재해도 매개변수가 다르면 같은 함수 이름을 가질 수 있는데, 링커는 이것을 모르기 때문에, 컴파일러가 함수명을 바꾸어서 링커가 헷갈리지 않게 한다는 것이다.
그런데 이렇게 네임 맹글링을 해버리면 이번에는 외부 모듈이 링크를 할 수 없기 때문에 —분명 코드에서는 finocacci_init 이라는 이름이었는데, 컴파일러가 멋대로 이름을 바꾸어 버리면 찾을 수가 없는 것이다— 이를 방지하기 위해 네임 맹글링을 하지 말라고 선언하는 것이다.
쉽게 요약하면 외부로 내보낼 함수나 클래스 등에 대해서는 extern "C" 키워드를 써줘야 외부 모듈에서 해당 함수나 클래스를 찾을 수 있게 된다는 것이다.
extern "C" 키워드는 아래와 같이 함수의 가장 앞에 사용한다.
extern "C" MATHLIBRARY_API void fibonacci_init(const unsigned long long a, const unsigned long long b); extern "C" MATHLIBRARY_API bool fibonacci_next(); extern "C" MATHLIBRARY_API unsigned long long fibonacci_current(); extern "C" MATHLIBRARY_API unsigned fibonacci_index();
C++
복사
그런데 이 extern "C" 키워드는 위와 같이 번거롭게 쓰지 않고 아래와 같이 하나로 묶을 수도 있다.
extern "C" { MATHLIBRARY_API void fibonacci_init(const unsigned long long a, const unsigned long long b); MATHLIBRARY_API bool fibonacci_next(); MATHLIBRARY_API unsigned long long fibonacci_current(); MATHLIBRARY_API unsigned fibonacci_index(); }
C++
복사
주의) extern "C" 키워드는 C++의 Template과 함께 쓰일 수 없다.

함수 내보내기

함수 내보내기는 쉽다. 위와 같은 코드를 헤더파일에 선언하고, 해당 헤더 파일을 참조하는 cpp 파일을 만들고, 구현을 작성하면 된다. cpp 파일에서는 위와 같은 전처리문이나 extern "C" 키워드를 쓰지 않아도 된다.
// MathLibrary.h 파일 #pragma once #ifdef MATHLIBRARY_EXPORTS #define MATHLIBRARY_API __declspec(dllexport) #else #define MATHLIBRARY_API __declspec(dllimport) #endif extern "C" { MATHLIBRARY_API void fibonacci_init(const unsigned long long a, const unsigned long long b); MATHLIBRARY_API bool fibonacci_next(); MATHLIBRARY_API unsigned long long fibonacci_current(); MATHLIBRARY_API unsigned fibonacci_index(); }
C++
복사
// MathLibrary.cpp 파일 #include "pch.h" // use stdafx.h in Visual Studio 2017 and earlier #include <utility> #include <limits.h> #include "MathLibrary.h" static unsigned long long previous_; static unsigned long long current_; static unsigned index_; void fibonacci_init(const unsigned long long a, const unsigned long long b) { index_ = 0; current_ = a; previous_ = b; // see special case when initialized } bool fibonacci_next() { // check to see if we'd overflow result or position if ((ULLONG_MAX - previous_ < current_) || (UINT_MAX == index_)) { return false; } // Special case when index == 0, just return b value if (index_ > 0) { // otherwise, calculate next sequence value previous_ += current_; } std::swap(current_, previous_); ++index_; return true; } unsigned long long fibonacci_current() { return current_; } unsigned fibonacci_index() { return index_; }
C++
복사

클래스 내보내기

클래스도 내보내는 방법은 비슷하다. 아래와 같이 사칙연산을 하는 클래스가 있다고 가정하자. —아래 코드는 static 클래스를 예시로 하였지만 static이 아닌 클래스도 동일하다.
// MathUtil.h 파일 #pragma once #ifdef MATHLIBRARY_EXPORTS #define MATHLIBRARY_API __declspec(dllexport) #else #define MATHLIBRARY_API __declspec(dllimport) #endif extern "C" { MATHLIBRARY_API static class MathUtil1 { public: static int Sum(int a, int b); static int Subtract(int a, int b); static int Multiply(int a, int b); static int Divide(int a, int b); } static class MathUtil2 { public: MATHLIBRARY_API static int Sum(int a, int b); MATHLIBRARY_API static int Subtract(int a, int b); static int Multiply(int a, int b); static int Divide(int a, int b); } }
C++
복사
위의 코드에서 MathUtil1 클래스는 MATHLIBRARY_API 전처리문을 클래스 앞에 선언했고, MathUtil2 클래스는 내부의 함수 앞에 선언했다는 점에만 차이가 있다.
상식적으로 예상 할 수 있듯이, 클래스 앞에 MATHLIBRARY_API 를 선언한 MathUtil1에 속한 함수는 모두 내보내기가 되고, MathUtil2 클래스에 속한 함수는 Sum과 Subtract 함수만 내보내기가 된다. —아래에서 DLL을 참조하는 부분을 보면 알겠지만, 외부 모듈에서 MathUtil2의 Multiply, Divide 함수가 보이기는 한다. 다만 컴파일하면 링크 에러가 발생한다.

추가

위에서 extern “C”를 별도의 블록으로 묶는 식으로 정리하였는데, 아래와 같이 extern “C” 구문 자체를 #define에 넣으면 블록으로 따로 묶을 필요도 없다.
// MathLibrary.h 파일 #pragma once #ifdef MATHLIBRARY_EXPORTS #define MATHLIBRARY_API extern "C" __declspec(dllexport) #else #define MATHLIBRARY_API extern "C" __declspec(dllimport) #endif MATHLIBRARY_API void fibonacci_init(const unsigned long long a, const unsigned long long b); MATHLIBRARY_API bool fibonacci_next(); MATHLIBRARY_API unsigned long long fibonacci_current(); MATHLIBRARY_API unsigned fibonacci_index();
C++
복사

C++에서 DLL 사용하기

DLL을 참조하기 위해 필요한 것

앞서 만든 DLL을 다른 프로젝트에서 사용하려면 소스 내에서 include를 해야 하므로, DLL의 header 파일이 필요하다.
그런데 header 파일에는 구현이 포함되어 있지 않으므로, DLL을 참조하려는 프로젝트에서는 DLL의 lib 파일이 추가로 필요하다.
마지막으로 DLL을 참조하는 프로젝트를 빌드한 후 실행할 때는 실제 dll 파일이 필요하다.
따라서 DLL을 참조하려는 프로젝트에서는 다음의 3가지가 필요하다.
1.
DLL 프로젝트의 header 파일
2.
DLL 프로젝트의 lib 파일
3.
DLL 프로젝트의 dll 파일
이 중 header와 lib 파일은 프로젝트를 빌드할 때 필요하므로 다음과 같이 DLL을 참조하려는 프로젝트의 속성 페이지에서 설정해 주면 되고, dll 파일은 빌드할 때는 사용되지 않으므로 별도로 설정해 줄 필요는 없고, 대신 실행 할 때는 필요하므로 DLL을 참조하려는 프로젝트의 실행 파일이 있는 경로에 있으면 된다.

프로젝트에 DLL 참조 설정

일단 DLL을 참조하려는 프로젝트를 우클릭해서 속성 페이지를 연다. —Debug/Release와 Win32/x64를 잘 구분해야 함
1.
우선 DLL 프로젝트의 header 파일들이 있는 경로를 C/C++ → 일반 → 추가 포함 디렉터리에 추가한다.
이것은 VC++ 디렉터리의 포함 디렉터리에 추가해도 되지만, 거기에는 프로젝트와 Window SDK 등이 포함되어 있으므로 1번 경로에 넣는게 깔끔하다.
2.
다음으로 DLL 프로젝트의 lib 파일들이 있는 경로를 링커 → 일반 → 추가 라이브러리에 추가한다.
1번과 마찬가지로 VC++ 디렉터리의 라이브러리 디렉터리에 추가해도 되지만, 2번 경로에 넣는게 깔끔하다.
3.
마지막으로 실제 참조하는 lib 파일 이름을 링커 → 입력 → 추가 종속성에 추가한다.
위 DLL 이름이 MathLibrary.lib 였다면 그 이름을 입력한다.
위와 같이 설정하면 프로젝트 내에서 C++ DLL 파일을 include하여 사용할 수 있다.

참조 자료