DLL usage

link DLL 分成 implicit link 及 explicit link。

DLL library 需要 export 出 symbol,使用 DLL 的程式則需要 import symbol。VC 裡透過 __declspec(export) 來標示要 export 的 symbol,以及 __declspec(import) 標示要從外面 import 的 symbol。如果要讓 C++ 的 symbol 跟 C 相容,需要加 extern "C"(不做 C++ 名稱修飾)。

使用 library 的程式需要:

  • compile 時需要 library export 的 symbol 的 header file
  • 需 link library 的 .lib.lib 會在 build DLL 時一起 build 出來
  • 執行時需 .dll

build DLL 產生的 .lib 跟 static library 的不一樣,DLL 的 .lib 只是告訴使用的程式去哪找到 DLL,不會包含實際功能,所以檔案比較小。使用 implicitly link DLL 的程式必須要在 load 時可以找到 DLL,否則會跳錯誤訊息而且無法繼續執行。

作為 library 的 project 需在 VC 的 project properties→General→Configuration Type 設為 Dynamic Library (.dll)、Target Extension 設為 .dll。另外,如果 code 實際上沒有 export symbol,build DLL 時不會生出 .lib

Sample

Foo library

compile 時需 define FOO_DLL_EXPORTS

Foo.h
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef FOO_H
#define FOO_H

#ifdef FOO_DLL_EXPORTS
#define FOO_API __declspec(dllexport)
#else
#define FOO_API __declspec(dllimport)
#endif

FOO_API int Add(int a, int b);
extern "C" FOO_API int Sub(int a, int b);
#endif
Foo.cpp
1
2
3
#include "Foo.h"
int Add(int a, int b) { return (a + b); }
int Sub(int a, int b) { return (a - b); }

dumpbin 要從 VC 的 command prompt 才叫得出來。

export symbol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
D:\tmp\FooLibrary\Debug>dumpbin /EXPORTS FooLibrary.dll
Dump of file FooLibrary.dll

File Type: DLL

Section contains the following exports for FooLibrary.dll

00000000 characteristics
56507D3B time date stamp Sat Nov 21 22:18:35 2015
0.00 version
1 ordinal base
2 number of functions
2 number of names

ordinal hint RVA name

1 0 0001107D ?Add@@YAHHH@Z = @ILT+120(?Add@@YAHHH@Z)
2 1 000110FA Sub = @ILT+245(_Sub)

Test program

project 的 include file 中須包含 Foo.h,link library 需有 FooLibrary.lib,執行檔旁則需放 FooLibrary.dll

main.cpp
1
2
3
4
5
6
7
#include <iostream>
#include "Foo.h"
int main()
{
std::cout << Add(1, 2) << ", " << Sub(1, 2) << endl;
return 0;
}

在 runtime 時才 load DLL。因為 runtime 才 load,即使 load DLL 失敗也可以在程式裡處理錯誤並繼續執行下去。

使用 library 的程式需要:

  • call LoadLibrary() load DLL
  • call GetProcAddress() 取得想要的 function 的 address
  • 用完 library 需 call FreeLibrary()
  • 程式 compile 時不一定需要 library 的 header file(但需要知道要 call 的 function 的 prototype),link 時不需要 .lib,僅在執行時需要 .dll

GetProcAddress() 需指定的 function name 是 library export 出來的 symbol,不是 library source code 裡的 function name。經過 C++ 名稱修飾,需要指定的 function name 會變得難以理解,這種 interface 應該沒人想用。除了 __declspec(dllexport) 外,export function symbol 的另一個做法是使用 .def 模組定義檔來宣告名稱。實際上是指定 alias 給原本的 symbol。

Sample load DLL

library source code 同上。

有加 extern "C"Sub() 因為沒經過 C++ 名稱修飾,所以能直接用 function name,但 Add() 就得寫出 C++ 修飾後的 symbol name 才拿得到 function pointer。

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <windows.h>

typedef int(*pfn)(int, int);

int main()
{
HINSTANCE dllHandle = LoadLibrary("FooLibrary.dll");

if (dllHandle != NULL)
{
// Get address of function
pfn pSubFunc = (pfn)GetProcAddress(dllHandle, "Sub");

if (!pSubFunc)
{
std::cout << "Load Sub() fail" << std::endl; // handle the error
}
else
{
std::cout << pSubFunc(2, 3) << std::endl; // call the function
}

pfn pAddFunc = (pfn)GetProcAddress(dllHandle, "?Add@@YAHHH@Z");

if (!pAddFunc)
{
std::cout << "Load Add() fail" << std::endl;
}
else
{
std::cout << pAddFunc(2, 3) << std::endl;
}

FreeLibrary(dllHandle);
}
else
{
std::cout << "Load FooLibrary.dll fail" << std::endl;
}

return 0;
}

Sample 模組定義檔

刪掉 Foo.h 裡的 __declspec(dllexport)

Foo.def
1
2
3
4
LIBRARY FooLibrary
EXPORTS
Add
Sub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
D:\tmp\FooLibrary\Debug>dumpbin /EXPORTS FooLibrary.dll
Dump of file FooLibrary.dll

File Type: DLL

Section contains the following exports for FooLibrary.dll

00000000 characteristics
56516FE9 time date stamp Sun Nov 22 15:34:01 2015
0.00 version
1 ordinal base
2 number of functions
2 number of names

ordinal hint RVA name

1 0 0001107D Add = @ILT+120(?Add@@YAHHH@Z)
2 1 000110FA Sub = @ILT+245(_Sub)

main.cppGetProcAddress() 可以直接寫 AddSub

其實這是小實驗筆記吧…

Ref