Microsoft Specific

dllimport, dllexport

DLL에서(로) 함수, 데이터, 개체를 내보내는(가져오는) 속성. 함수를 dllexport로 선언하면, 적어도 export된 함수 스펙과 관련된 .DEF (모듈 정의) 파일이 필요없다. 또한 dllexport는 __export 키워드 (16비트 버전의 VC++에서 사용) 를 대체한다.

declspec(dllexport) 클래스의 클래스 템플릿 특수화(specialization)는 암묵적으로 declspec(dllexport)가 된다. 즉 템플릿은 명시적으로 인스턴스화(==구체화)되고, 그 멤버들은 반드시 정의되어야 한다.

dllexport C++ 함수는 이름장식을 가진 함수를 노출시킨다. C++ 이름장식이 필요없다면, .def 파일 (EXPORTS(MSDN) 키워드)를 사용하거나 함수를 extern "C"로 선언한다.

dllexport와 dllimport는 반드시 declspec과 확장된 속성 구문을 사용해야 한다.

__declspecdllimport int i;

__declspecdllexport void func();

좀 더 보기좋게 다음과 같이 매크로를 쓸 수도 있겠다.

#define DllImport __declspecdllimport )

#define DllExport __declspecdllexport )

정의와 선언

DLL 인터페이스는 일부 프로그램이 export한 모든 항목 (함수와 데이터) 을 참조한다; 즉 모든 항목은 dllimport나 dllexport로 선언되며, DLL에 선언된 것들은 반드시 둘 중 하나를 가져야 한다. 하지만, 그 정의는 반드시 dllexport여야한다. 즉 다음과 같은 코드는 컴파일 에러란 말이다:

DllImport int func() { return 1; } // 둘 다 정의

DllImport int i = 10;

dllexport는 정의를 dllimport는 선언을 의미한다. dllexport와 extern 키워드를 같이 사용하면 강제로 선언할 수 있다; 그렇지 않으면 정의를 의미한다. 다음 코드들을 보자:

extern DllImport int k; // 둘 다 선언을 의미하며 정확하다

DllImport int j;

static DllImport int l; // 에러; extern으로 선언되지 않음

void func() {

static DllImport int s; // 에러. extern으로 선언되지 않음

DllImport int m; // OK. 이것은 선언

DllExport int n; // 에러. 로컬 범위에서 외부정의를 포함

extern DllImport int i; // OK. 이것은 선언

extern DllExport int k; // OK. extern은 선언을 의미한다

DllExport int x = 5; // 에러. 로컬 범위에서 외부정의를 포함

}

dllexport/ dllimport로 선언된 인라인 C++ 함수 정의

dllexport 함수를 인라인으로 선언하면, 어떤 모듈이 그 함수를 참조하던말던 항상 인스턴스화되고 export되며, 다른 프로그램에서 import한다고 가정된다.

dllimport 함수를 인라인으로 선언하면, 그 함수는 확장될 수 있지만 (/Ob(VS6.0) 설정에 따라) 절대 인스턴스화되지는 않는다. 특히 인라인 import 함수의 주소가 있다면, DLL 안에 존재하는 그 함수의 주소가 리턴된다. 이는 인라인이 아닌 import 함수의 주소를 얻는 것과 같은 방식이다.

위 규칙들은 클래스 안에 정의된 인라인 함수에 적용된다. 게다가, 인라인 함수 안의 정적 로컬 데이터와 스트링은 마치 하나의 프로그램 (즉 DLL 인터페이스가 없는 실행 파일) 안에 있는 것처럼 DLL과 클라이언트 사이에서 동일성을 유지한다.

DLL을 업데이트할 때, 클라이언트가 바뀐 DLL을 사용할 거라고 가정하지 않는다. 올바른 버전의 DLL을 로딩하려면, 해당 클라이언트 또한 재빌드한다.

일반적인 규칙과 제한사항

─ dllimport나 dllexport 없이 선언된 함수나 개체는 DLL 인터페이스로 간주되지 않으며, DLL 인터페이스로 만들려면 dllexport로 정의한다. 이런 경우든 dllexport로 선언된 경우든, 같은 프로그램 안에 정의되어야 한다. 아니면, 링커 에러가 발생한다.

─ 하나의 모듈 안에 동일한 함수나 개체가 dllimport와 dllexport 2가지로 선언되면 dllexport 속성이 우선하지만, 이는 파일러 경고를 일으킨다:

__declspecdllimport ) int i;

__declspecdllexport ) int i; // 경고; 불일치; dllexport가 우선한다

─ C++에서는 글로벌/ 정적 로컬 데이터 포인터를 dllimort로 선언된 데이터 개체의 주소로 초기화할 수 있다. 하지만 이는 C에서 에러다. 또한, 정적 로컬 함수 포인터를 dllimport로 선언된 함수의 주소로 초기화할 수 있다. 이 포인터는 C에서 DLL import thunk (함수로 제어를 넘기는 코드 토막) 의 주소, C++에서는 함수의 주소가 할당된다:

__declspecdllimport ) void func1( void );

__declspecdllimport ) int i;

int *pi = &i; // C에서는 에러

static void ( *pf )( void ) = &func1; // C에서는 thunk 주소, C++에선 함수 주소

void func2() {

static int *pi = &i; // C에서는 에러

static void ( *pf )( void ) = &func1; // C에서는 thunk 주소, C++에선 함수 주소

}

하지만 dllexport 개체 선언을 포함하는 프로그램은 반드시 정의도 가져야하므로, 글로벌/ 로컬 정적 함수 포인터를 dllexport 함수의 주소로 초기화할 수 있다. 또한 글로벌/ 로컬 정적 데이터 포인터를 dllexport 데이터 개체의 주소로 초기화할 수 있다. 가령 다음 코드는 C/ C++에서 모두 에러가 아니다:

__declspecdllexport ) void func1( void );

__declspecdllexport ) int i;

int *pi = &i;

static void ( *pf )( void ) = &func1;

void func2() {

static int *pi = &i;

static void ( *pf )( void ) = &func1;

}

─ VC++ .NET에서는 regular 클래스와 클래스 템플릿의 특수화 사이에서 dllexport를 가진 애플리케이션이 보다 일관되도록 하기위해, dllexport가 아닌 기본 클래스를 가진 regular 클래스에 dllexport를 적용하면 C4275(한글) 컴파일러 경고가 발생한다.

이 경고는 기본 클래스가 클래스 템플릿의 특수화일 경우도 발생하는데, 이 때는 dllexport를 표시해주면 된다. 즉 클래스 템플릿에 __declspec(dllexport)을 쓸 수 없다. 대신 다음처럼, 명시적으로 클래스 템플릿을 인스턴스화하고 여기에 dllexport를 사용한다:

template class __declspecdllexport ) B<int>);

class __declspecdllexport ) D : public B<int> {}

이 방법은 다음처럼 템플릿 인자가 부모 클래스인 경우 에러가 발생한다:

class __declspecdllexport ) D : public B<D> {}

이는 템플릿에서 흔한 패턴이기 때문에, VC++ .NET 컴파일러는 하나 이상의 기본 클래스를 가지는 클래스에 적용될 때와 하나 이상의 기본 클래스가 클래스 템플릿의 특수화일 경우 dllexport 구문을 바꿨다. 이 경우 컴파일러는 암묵적으로 클래스 템플릿의 특수화에 dllexport를 사용하며, 위 문장은 문제없이 컴파일된다.

dllexport/ dllimport를 C++ 클래스에서 사용하기

dllimportdllexport로 클래스를 선언하면, 그 클래스 전체가 import/ export되며, 이렇게 export된 클래스를 exportable 클래스라 한다.

#define DllExport __declspecdllexport )

class DllExport C {

int i;

virtual int func( void ){ return 1; }

};

참고 exportable 클래스의 멤버는 dllimport와 dllexport를 명시적으로 쓸 수 없다.

─ dllexport 클래스는 모든 멤버함수와 정적 데이터멤버가 export되므로, 동일 프로그램 안에서 모든 멤버를 정의해야 한다. 그렇지않으면 링커에러가 발생한다. 단 순수 가상함수의 경우 예외이며, 가상 클래스의 소멸자는 항상 기본클래스의 소멸자에서 호출되므로, 순수 가상 소멸자는 반드시 정의해야 한다. 이 규칙들은 exportable 클래스가 아니더라도 적용된다.

클래스타입의 데이터나 클래스를 리턴하는 함수를 export하려면, 클래스를 export해야만 한다.

─ dllimport 클래스는 모든 멤버함수와 정적 데이터멤버가 import된다. 비클래스 타입에서의 dllimportdllexport와는 달리, 정적 데이터멤버는 dllimport 클래스가 정의된 프로그램에서 정의할 수 없다.

─ exportable 클래스의 모든 기본 클래스는 exportable이어야 한다. 아니면, 컴파일러 경고가 발생한다. 게다가, 클래스이기도 한 모든 accessible 멤버는 exportable이어야 한다. 이로써 dllexport 클래스는 dllimport 클래스로부터 상속받을 수 있고, dllimport 클래스는 dllexport 클래스에서 상속받을 수 있다 (후자는 권장되지 않는다). 대개, (C++ 접근 규칙에 따라) DLL 클라이언트에서 accessible한 모든 것들은 exportable 인터페이스의 일부여야 한다(should). 이는 인라인 함수에서 참조되는 private 데이터멤버에도 해당된다.

End Microsoft Specific

출처 : http://redsky84.tistory.com/entry/declspec-%ED%99%95%EC%9E%A5%EB%90%9C-%EC%86%8D%EC%84%B1-%EA%B5%AC%EB%AC%B8-III

원문: C++ Language Reference, __declspec, dllexport/ dllimport(CE3.0)

참고: VC++ 개념, 가져오기 및 내보내기

반응형

+ Recent posts