DLLIMPORT / DLLEXPORT  MFC 

2008/01/25 14:41

복사http://blog.naver.com/niceyjb1024/46810517

dllimport와 dllexport는 DLL에서 function, variable, ***(class..)
를 import하거나 export하기 위해 사용합니다.

자세히 말하자면 DLL을 사용하는 프로그램이 DLL로 부터 function등을
가져다 쓰게 하기 위해 외부로 노출(?)시킬 필요가 있는데(.. 이런
함수, 데이터, 개체등을 DLL의 인터페이스라고 하죠..)
이런 목적으로 DLL내의 함수등을 정의할때 dllexport지시자를 붙여 줍니다.
그리고 dllexport를 사용한 함수등에 대해 일반적으로 DLL의 인터페이스를
정의해 두는 DEF파일(Module definition file)에 포함시킬 필요가 없습니다.

반대로 DLL을 가져다 쓰는 프로그램(or 다른 DLL)쪽에선 DLL의 인터페이스
를 사용하기 전에 당연히 미리 선언해 주어야 할 필요가 있습니다. 이때
선언문에서 dllimport 지시자를 함께 사용해 주는 것이죠..

__declspec( dllexport ) int func()
{
return 1;
}

....

__declspec(dllimport) int func() ;

이런 식으로 말이져..

함수의 경우에는 반드시 dllimport를 쓸 필요는 없지만
컴파일러가 좀더 효율적으로 코드를 만들어 주기때문에
함께 써주는 게 좋습니다. 그러나 변수등의 경우에는
꼭 dllimport를 붙여 줘야 함다.

[출처] DLLIMPORT / DLLEXPORT|작성자 완전초짜


 

 


 

 


 

__declspec(dllimport)의 호출 원리  Windows / Programming 

2009/01/28 09:12

복사http://blog.naver.com/iwillhackyou/110041460983

DLL을 통해서 함수를 제공하려면 DLL상의 Export Address Table(PE 포멧)에 함수의 위치를 나타내도록 __declspec(dllexport)지시자를 사용해서 함수를 노출시킨다. 반면에 함수를 호출하는 쪽에서는 특별한 코드 없이 헤더파일(.h)에 포함된 함수 이름을 직접 사용하면 링커가 해당 함수를 호출할 수 있도록 컴파일 시점에 연결시켜준다. 함수를 호출하는 쪽에서 사용한 헤더파일(.h)에 함수 원형이 __declspec(dllimport)으로 정의되지 않았도 정상적으로 DLL에서 제공된 함수를 사용 할 수 있다.

 

즉 다른 모듈에서 제공되는 함수를 호출하는데 있어서 함수 원형에 __declspec(dllimport)이 선언 유무와는 상관없이 항상 호출할 수 있다. 하지만 결론 부터 말하면 DLL에서 제공되는 함수를 사용할 때에는 항상 함수 선언 앞에 __declspec(dllimport) 지시자를 사용하는 것이 좋다.


대부분 DLL에서 제공되는 API는 헤더 파일에 다음과 같이 선언해서 제공한다.

 

// API Export/Import Header

#ifdef CALLEE_EXPORTS
#define CALLEE_API __declspec(dllexport)
#else
#define CALLEE_API __declspec(dllimport)
#endif

CALLEE_API DWORD __stdcall no_no_stdcall(DWORD);  // WINAPI
CALLEE_API DWORD __cdecl no_no_cdecl(DWORD);   // CDECL
CALLEE_API DWORD __fastcall no_no_fastcall(DWORD);

 

위 헤더 파일의 핵심은 모듈을 제공하는 쪽과 사용하는 쪽에서 동일한 하나의 헤더 파일을 공유해서 사용하도록 하는 것이다. (소스 관리 차원에서도 하나의 파일을 유지하는 것이 좋다. 특히 ThirdParty 벤더에서 제공되는 API모듈이라면 더욱더 그렇게 해야한다.)

즉, 프로젝트의 Preprocessor definitions에 특별히 CALLEE_EXPORTS를 선언하지 않은 사용자의 프로젝트에서는 함수를 __declspec(dllimport)형식으로 정의해서 링크하게 된다. 위의 예는 가장 확실하게 __declspec(dllimport)를 선언해서 사용할 수 있는 경우이고 헤더가 중복해서 관리해야하는 어려움도 필요없는 최적의 상태이다.



하지만 내부 프로젝트 모듈이건 다른 벤더의 모듈이던 위와 같이 정의해서 사용하지 않고 다음과 같이 API의 Import용으로 헤더 파일을 별도로 정의해서 제공하는 경우가 종종 있다.

 

 

// API Import Header

DWORD __stdcall no_no_stdcall(DWORD);  // WINAPI
DWORD __cdecl no_no_cdecl(DWORD);   // CDECL
DWORD __fastcall no_no_fastcall(DWORD);


이 경우는 소스 코드상의 헤더 관리의 어려움을 떠나서 성의가 없는 경우이다 -_-.

DWORD __stdcall no_no_stdcall(DWORD);
__declspec(dllimport) DWORD __stdcall no_no_stdcall(DWORD);  


일단 DLL을 static link 할 경우에 링커는 위의 두가지 경우를 구분없이 모두 정상적으로 처리해서 함수를 호출할 수 있도록 한다. 그렇다면 차이점이 무엇일까?
DWORD __stdcall no_no_stdcall(DWORD) 와 같이 선언된 상태에서 링커는 DLL과 함께 생성된 Lib 파일에서 가르키고 있는 Stub을 직접 호출하도록 한다. 컴파일러는 no_no_stdcall() 이라는 함수가 현재 프로젝트 내부에 다른 Obj에 선언된 함수인지 아니면 아예 다른 외부 모듈(DLL)에 선언된 함수인지 현 빌드 시점에는 알 수 없기 때문이다.

 

이렇게 static link용 Lib의 stub에 연결된 경우에도 실행시 실제 DLL의 함수는 정상적으로 호출된다. 그 이유는 DLL 생성시점에 함께 생성되는 Lib 파일의 Stub내부에는 실제 모듈(DLL)의 함수로 점프할 수 있는 코드가 이미 DLL 파일의 컴파일 타임에 이미 생성되어 Stub안에 존재하기 때문에 가능한 것이다. 즉 해당 Stub을 내부 함수처럼 호출하면 그곳에서 실제 모듈(DLL)의 함수를 가르키는 IAT(Import Address Table)를 참조해서 실제 API로 이동하는 코드가 수행되는 것이다. 


즉, DLL에서 제공되는 함수를 호출하는 쪽에서 사용한 함수의 선언 형태에 따라서 아래와 같은 두가지 형태의 코드 형식으로 호출할 수 있게 된다.

 

// 1. 함수원형 앞에 __declspec(dllimport) 지시자를 사용해서 호출할 경우
0x01002000 : CALL DWORD PTR [0x56780000]; // CALL DWORD PTR[_imp__MyFunc];


// 2. __declspec(dllimport) 지시자 없이 호출할 경우
0x01002000 : CALL 0x12340000;
0x12340000 : JMP DWORD PTR[0x56780000];



// (0x5678000 : 실제함수주소를 가르키고있는 IAT의 주소)

...

 

상기 코드 중에서 당연히 1번의 경우가 훨씬 효과적인 것을 알 수 있다. 2번의 경우에는 JMP를 위한 추가적인 5Byte의 Stub 코드를 더 수행하게 되므로 비효율적이다.

__declspec(dllimport)지시자를 사용함으로서 1번의 형태로 DLL에 제공되는 함수를 호출하는 것을 알 수 있다. 상기 코드에서 컴파일러는 __declspec(dllimport) 지시자를 보았을 경우에 실제 함수인 "MyFunc"을 참조하지 않고 "__imp__MyFunc"을 참조하는 코드를 만들어 놓는다. 그후에 링커는 함수 이름에 붙어있는 "__imp__" 라는 Prefix를 보고 외부 DLL에 존재하는 함수를 직접 호출하는 것을 알게 된다. (컴파일리가 링커에게 DLL에 존재하는 함수를 호출한다는 것을 알려주기 위해 "__imp__" Prefix를 함수앞에 추가한다.) DLL과 제공되는 Lib파일에는 "__imp__" prefix를 가지는 함수명이 함께 선언되어 있다.)

 

다음과 같은 링크 에러를 보면 실제 함수 이름 앞에 "__imp__" Prefix가 존재하는 것을 알 수 있다.

 

: error LNK2001: unresolved external symbol __imp__MyFunc

: fatal error LNK1120: 1 unresolved externals

 

위의 경우에는 링크 에러가 발생했는데 링크 에러가 발생하지 않으려면 빈 Lib안에 __imp__MyFunc에 대한 Stub이 존재하고 있어야 한다. DLL과 함께 생성된 Lib에는 "MyFunc", "__imp__MyFunc"에 대한 두가지 Stub이 모두 존재하고 있다. 컴파일러는 두 함수 이름을 Lib에 미리 생성해 놓고 있다.

 

반응형

+ Recent posts