DLL 만들기
- DLL : 동적 연결 라이브러리
// dllmain.cpp : DLL 애플리케이션의 진입점을 정의합니다.
#include "pch.h"
//dll을 외부에서 사용하려면 __declspec(dllexport) 지시어를 통해 export 해주어야 한다.
//이를 export 심볼이라고 한다.
__declspec(dllexport) int add(int a, int b)
{
return a + b;
}
__declspec(dllexport) int __stdcall sub(int a, int b)
{
return a - b;
}
- dll 샘플 코드
DLL 빌드 방법
- 개발자 명령 프롬프트에서 빌드
2. 통합개발환경에서 빌드
- dll은 f5로 실행할 수 없고 ctrl + shift + b를 통해 빌드만 진행
Symbol Export
- dll도 PE포맷으로 되어 있어서 PE View로 볼 수 있음
- dll은 실행되는 파일에 포함되는 게 아니라 실행 파일에서 호출하는 것
- .edata 섹션에 보면 add, sub 함수의 export 정보를 볼 수 있다.
- .edata 섹션은 크기가 작아서 .rdata 섹션이나 .text 섹션에 포함되는 경우가 많다.
DLL 배포하기
- 위의 과정을 거치면서 .lib파일과 .dll 파일이 만들어지는데,
- 함수를 호출하려면 선언도 필요하기 때문에, 헤더 파일도 같이 배포한다.
- 헤더 파일에도 함수 선언 앞에 __declspec(dllexport)를 붙어야 한다.
- (생략 가능하나 붙이는 것이 효율적)
- 따라서 dll을 컴파일할 때 필요한 것은 dll, lib, h 총 3개의 파일이다.
- cpp에 헤더를 포함하고 #pragma comment(lib, "")을 통해 헤더와 lib를 가져온다.
- 이 시점에 lib를 통해 exe의 .idata영역에 어떤 dll에 어떤 함수가 사용되는지 주소와 같은 정보들이 들어간다.
- 따라서 빌드 시점에는 dll이 필요 없음
- idata에 name table은 어떤 함수를 쓰는지도 나오는데 이 주소를 조작할 수 있다고 함
#include <iostream>
#include "dllimport.h"
#pragma comment(lib, "dllmain.lib")
int main()
{
std::cout << add(3, 5) << std::endl;
}
CRT 함수와 라이브러리 개념
- 우리가 알고 있는 모든 라이브러리들은 dll로 배포가 된다.
- 윈도우 설치될 때 많은 dll이 설치되고 user32.dll, gdi32.dll, kernel32.dll이 유명하다.
dll을 쓰려면 dll과 lib와 header가 필요한데 그건 어디서?
- MessageBox의 경우를 예로 들면,
- 구현부는 User32.dll 안에 있는데 이는 시스템 폴더에 있어서 환경변수의 영향을 받는다.
- User32.lib는 VS의 경우 추가종속성 란에 들어가 있다.
- 헤더의 경우 WinUser.h 안에 있는데 이 헤더는 Windows.h안에 포함되어 있다.
- 그래서 개발자 명령 프롬프트에서 빌드하려면 cl main.c user32.lib 라고 뒤에 언급해주어야 한다.
CRT 함수란?
- C에서 제공하는 printf 등등 기본적인 함수들
- 이 함수들은 lib버전과 dll 버전을 모두 제공한다.
- VS에서 빌드하면 기본적으로 dll 버전으로 빌드되며,
- 옵션을 변경하려면 프로젝트 -> 속성메뉴 -> C/C++ -> 코드 생성 -> 런타임 라이브러리에서 변경 가능
- 다중 스레드 DLL -> DLL버전
- 다중 스레드 -> LIB버전
DLL의 명시적 연결
- 위에서 언급한 방법들은 프로그램 시작 시 DLL을 같이 로드하는 방법으로,
- 이 방법을 암시적 연결이라고 한다.
- 명시적 연결은 원하는 시점에 함수를 통해 dll을 load하는 방법이다.
- 이 방식의 경우 헤더와 lib가 필요 없어진다.
#include <iostream>
#include <Windows.h>
#include "dllimport.h"
#pragma comment(lib, "dllmain.lib")
typedef int (*dllFunc)(int, int);
int main()
{
HMODULE hMod = LoadLibrary(L"dllmain.dll");
dllFunc f = (dllFunc)GetProcAddress(hMod, "?add@@YAHHH@Z");
std::cout << f(3, 5) << std::endl;
FreeLibrary(hMod);
}
- 라이브러리를 LoadLibrary 함수로 로드한 후,
- GetProcAddress 함수를 이용해서 가져올 함수 주소를 가져와서 사용한다.
#include <iostream>
#include <Windows.h>
#include "dllimport.h"
#pragma comment(lib, "dllmain.lib")
typedef int (__stdcall *dllFunc)(int, int);
int main()
{
HMODULE hMod = LoadLibrary(L"dllmain.dll");
dllFunc f = (dllFunc)GetProcAddress(hMod, "?sub@@YGHHH@Z");
std::cout << f(3, 5) << std::endl;
FreeLibrary(hMod);
}
- __stdcall 처럼 함수호출규약을 변경한 경우 그 정보를 함수 포인터에 기재해주어야 한다.
'운영체제 > [ecourse] Windows Programming' 카테고리의 다른 글
4-1. Kernel Object (0) | 2022.09.13 |
---|---|
3-3. 가상 주소 공간과 DLL (0) | 2022.09.12 |
3-1. Static Library (0) | 2022.09.08 |
2-3. 라이브러리 개념 (0) | 2022.09.07 |
2-2. 실행 파일 포맷 (0) | 2022.09.07 |
댓글