태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

DLL용 함수 선언

__declspec(속성) 선언

이 키워드는 Microsoft의 Visual C++에서 함수나 변수의 선언에 별도의 정보를 제공하기 위해 사용하는 것이다. 다음과 같은 속성이 사용될 수 있다.

  • thread: Thread Local Storage 데이터를 말하며, 이 지정자가 붙은 함수는 해당 스레드에서만 사용할 수 있다.
  • naked: 함수에서 접두어 접미어를 생성하지 않는다.
  • dllimport: DLL에 있는 데이터, 오브젝트, 함수를 임포트 한다.
  • dllexport: DLL에 있는 데이터, 오브젝트, 함수를 익스포트 한다. 함수를 선언할 때 dllexport로 선언하면, DEF 파일의 Exports란에 이 함수를 명시하지 않아도 되며, __export 키워드를 대신한다.

extern "C"

C++로 작성된 함수는 mangled name으로 바뀌기 때문에, 다른 언어에서는 이를 사용할 수 없다. 이 키워드는 mangled name으로 바꾸지 않는다.

그러므로 DLL에서 함수를 엑스포트 할 때는

extern "C" __declspec(dllexport) 함수 원형;

클라이언트에서 이 함수를 사용할 때는

extern "C" __declspec(dllimport) 함수 원형;

을 사용한다.

DLL의 연결

묵시적 연결

묵시적 연결에는 임포트 라이브러리를 사용한다. 임포트 라이브러리는 해당 DLL과 같은 이름을 가지며, 확장자는 LIB인 파일이다. 이 라이브러리는 Visual C++에서 DLL을 만들 때 자동으로 함께 만들어 준다.

임포트 라이브러리에는 DLL 내에 들어있는 함수들에 대한 정보가 들어있으므로, DLL은 별도의 로드 명령이 없어도 클라이언트가 실행될 때 로드되며, 만일 로드된 상태라면, 참조 계수가 1 증가한다.

명시적 연결

LoadLibrary 함수를 사용해서 직접 어느 DLL을 로드하라는 명령을 내린다.(임포트 라이브러리는 불필요하다.) 만일 로드된 상태라면, 참조 계수가 1 증가한다. 이 함수의 원형은 다음과 같다.

HINSTANCE LoadLibrary(LPCTSTR lpLibFileName);

확장자가 생략될 경우에 기본으로 ".DLL"을 가진다. DLL을 읽어오는데 성공하면, DLL의 모듈 핸들을 리턴하며 이 핸들로부터 함수의 번지를 얻어올 수 있다.

FARPROC GetProcAddress(HMODULE hLibModule, LPCSTR lpProcName);

DLL을 다 사용하고 난 후에는 해제한다.

BOOL FreeLibrary(HMODULE hLibModule);

이 함수는 DLL의 참조 카운터를 1 감소시키며, 카운트가 0이 되었을 때 DLL을 메모리에서 내린다.

다음은 예제이다.

HINSTANCE hInst = LoadLibrary("MyAdd.dll");
int (*p) (int, int);
p = (int (*)(int, int)) GetProcAddress(hInst, "AddInteger");
int res = (*p)(1,2);
FreeLibrary(hInst);

묵시적 연결이건 명시적 연결이건 간에 DLL 파일의 이름만 지정할 뿐 경로는 지정하지 않는다. DLL을 찾는 순서는 다음과 같다.

  1. 클라이언트 프로그램이 포함된 디렉토리
  2. 현재 디렉토리
  3. 윈도우즈의 시스템 디렉토리
  4. 윈도우즈 디렉토리
  5. Path 환경 변수가 지정하는 모든 디렉토리

명시적 연결의 경우 에러의 소지를 많이 가지고 있다. DLL이 제대로 로드되나, 해당 함수가 존재하나를 반드시 검사하고, 에러가 발생할 경우에는 GetLastError 함수를 호출해서 그 정보를 FormatMessage에 넘겨주면 에러 메시지를 얻을 수 있다. FreeLibrary에서는 에러가 거의 발생하지 않는다.

DllMain

DLL 전체의 초기화와 종료시에 불려지는 함수들을 만들 수 있다.

BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpRes);

fdwReason 인자는 다음과 같은 값을 가질 수 있다.

  • DLL_PROCESS_ATTACH: DLL이 클라이언트의 주소 공간에 매핑되었다는 것을 말한다.(명시적 연결의 경우 LoadLibrary가 호출되었을 때, 묵시적 연결의 경우 클라이언트가 실행될 때)
  • DLL_PROCESS_DETACH: DLL이 클라이언트의 주소 공간에서 분리되었다는 것을 말한다.(명시적 연결의 경우 FreeLibrary가 호출되었을 때, 묵시적 연결의 경우 클라이언트가 종료될 때)
  • DLL_THREAD_ATTACH: 클라이언트에서 새로운 스레드를 만들었다는 것을 말한다.
  • DLL_THREAD_DETACH: 클라이언트에서 스래드가 종료되었다는 것을 말한다.

DLL이 단순한 함수의 집합이라면 DllMain은 작성하지 않아도 된다. 그러나 동적으로 메모리를 할당해야 하는 등의 작업을 해야 한다면 DllMain에서 할당하고 종료하는 것이 가장 적합하다.

DllMain은 BOOL형을 리턴하는데 이 값이 FALSE이면 클라이언트 프로그램이 종료된다.

이 글을 공유하세요.

Tag
질문이나 의견을 댓글로 달아 주세요