DLL 만들기
// mymath.c
int add(int a, int b)
{
return a + b;
}
int __stdcall sub(int a, int b)
{
return a - b;
}
$ cl mymath.c /c
$ link mymath.obj /DLL # DLL이 생성됨
# or
$ cl mymath.c /LD # DLL이 생성됨
여기까지만들면 DLL은 만들어지지만 함수를 가져다 사용할순 없다
Symbol Export
DLL도 결국 PE 포맷이고 내부적으론
PE Header
===
.text(_add함수(Code))
.edata(_add함수의 export정보)
...
DLL을 call하는 exe는 DLL의 .edata정보를 보고 함수를 불러온다
참고로 릴리즈에서는 최적화를 위해 .edata를 따로 두기보단, .rdata .text에 .edata(DLL함수정보)를 포함되는 경우가 많다
- 함수 앞에
__declspec(dllexport)
지시어 추가 - 모듈 정의 파일(.def) 사용(나중에 정리)
// mymath.c
__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만 만들어지는게 아니라 .lib도 같이 만들어지게 된다.
- .lib파일의 종류
- 정적 라이브러리 : 함수의 기계어 코드를 담고있는 라이브러리
- DLL생성시 만들어지는 라이브러리 : export되는 함수의 링크 정보(링킹할때 필요한 정보)만을 담은 파일
배포
// mymath.h
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// 가져올 정보를 import한다
// 물론 __declspec(dllimport)를 생략해도 되지만 붙이는게 효율적
__declspec(dllimport) int add(int a, int b);
__declspec(dllimport) int sub(int a, int b);
#ifdef __cplusplus
}
#endif
DLL 사용하기
- 사용중인 DLL 함수 확인하기
- PEView.exe : 사용중인 exe확인
$ dumpbin.exe 파일이름 /import
: 사용중인 DLL 함수 확인가능
#include <stdio.h>
#include <mymath.h>
// 링킹정보를 가져온다
#pragma comment(lib, "mydynamic.lib")
int main()
{
int ret = add(1, 2)
printf("result %d\n", ret);
}
CRT 함수와 DLL
MessageBox와 같은 Win32 라이브러리는 동적/정적? 어떤 라이브러리일까?
#include <stdio.h>
#include <Windows.h>
int main()
{
MeesageBoxA(0, "A", "B", MB_OK);
// Windows API는 DLL로 제공이 된다.
// 그럼 dll, lib, h의 추가는 어디서 진행이 될까?
printf("hello\n");
}
- 일단 .h는
#include <Windows.h>
- lib는? 프로젝트 속성 -> 링커에 디폴트로 추가 되어있다.
- 위 방식은 IDE에서 빌드한 방식이고 명령프롬프트에서 빌드하려면
$ cl main.c user32.lib
- 사용하는 DLL을 넣어줘야함
그럼 printf와 같은 C표준 라이브러리는??
그럼 printf
는?
이런 C표준 함수르 CRT함수라 한다.
CRT함수는 정적/동적 라이브러리를 모두 제공한다.
- 내 실행파일에 C표준 라이브러리를 포함해 달라
- 정적라이브러리(libucrt.lib) :
/MT
- 정적라이브러리 디버그(libucrtd.lib) :
/MTd
- 정적라이브러리(libucrt.lib) :
- 내 실행파일에 C표준 라이브러리 미포함
- 동적라이브러리(ucrtbase.dll) :
/MD
- 동적라이브러리 디버그(ucrtbased.dll) :
/MDd
- 동적라이브러리(ucrtbase.dll) :
일반적으로 IDE를 쓰면 동적라이브러리 버전을 사용하게 됨.
CRT 라이브러리의 충돌
// main.exe
int main()
{
void* p = Alloc(100);
free(p);
// main.exe에서는 CRT 동적 라이브러리 사용
}
// Sample.dll
void * Alloc(int size)
{
return malloc(size);
// Sample.dll에서는 CRT 정적 라이브러리 사용
}
- CRT 라이브러리 버전을 맞추어야 한다
- DLL 내에서 자원 할당 한 경우 DLL 에서 자원 해지하는 것이 좋다.
Explicit Linking(DLL 명시적 연결)
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
#include "mydynamic.h"
#pragma comment(lib, "mydynamic.lib")
int main()
{
int ret = add(1, 2);
printf("result : %d\n", ret);
}
- 암시적 연결(implicit linking) : 실행파일 실행 시 동시에 DLL에 Load(.idata 섹션에 올라가게 된다)
- 명시적 연결(explicit linking) : DLL이 필요할때
LoadLibrary
함수를 사용해서 DLL 로드
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
typedef int (*F)(int, int);
int main()
{
getchar();
// 1. DLL 로드
HMODULE hDll = LoadLibrary(_T("MyDynamic.dll"));
printf("DLL 주소 : %p\n", hDll);
// 2. DLL 안에서 함수 찾기
F f = (F)GetProcAddress(hDll, "add");
// hDll에서 add를 찾아달라
printf("함수 주소 : %p\n", f);
int ret = f(1, 2);
printf("result : %d\n", ret);
// 3. DLL 언로드
FreeLibrary(hDll);
}
// 단, 콜링 컨벤션으로 인해서
F f = (F)GetProcAddress(hDll, "sub"); // 못찾음(sub는 __stdcall로 만들어짊)
F f2 = (F)GetProcAddress(hDll, "_sub@8"); // 찾음, 콜링 컨벤션에 따라 이름을 바꿔서 찾아야 함을 기억
printf("함수 주소 : %p\n", f); // 0
printf("함수 주소 : %p\n", f2);
int ret = f2(1, 2); // Error - 콜링 컨벤션이 달라 메모리해지 안됨.
printf("result : %d\n", ret);
해결책?
typedef int (__stdcall *F2)(int, int);
F2 f2 = (F2)GetProcAddress(hDll, "_sub@8");
int ret = f2(1, 2); // ok