(Win32 : WindowsProgramming-12) Dynamic Link Library(DLL)

Posted by : at

Category : win32   WindowsProgramming


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함수정보)를 포함되는 경우가 많다

  1. 함수 앞에 __declspec(dllexport) 지시어 추가
  2. 모듈 정의 파일(.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
  • 내 실행파일에 C표준 라이브러리 미포함
    • 동적라이브러리(ucrtbase.dll) : /MD
    • 동적라이브러리 디버그(ucrtbased.dll) : /MDd

일반적으로 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

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links