dll basics

27
DLL Basics DLL Basics Reference: Reference: Programming Applications for Programming Applications for Microsoft Windows Fourth Microsoft Windows Fourth Edition p.p. 675 Edition p.p. 675

Upload: noura

Post on 24-Jan-2016

120 views

Category:

Documents


2 download

DESCRIPTION

DLL Basics. Reference: Programming Applications for Microsoft Windows Fourth Edition p.p. 675. Introduction. All the functions in the Windows API are contained in DLLs The three DLLs Kernel32.dll : managing memory, processes, threads User32.dll : user-interface tasks - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: DLL Basics

DLL BasicsDLL BasicsReference: Reference:

Programming Applications for Microsoft Programming Applications for Microsoft Windows Fourth Edition p.p. 675Windows Fourth Edition p.p. 675

Page 2: DLL Basics

IntroductionIntroduction

All the functionsAll the functions in the Windows API are in the Windows API are contained contained in DLLsin DLLs

The three DLLsThe three DLLs Kernel32.dllKernel32.dll: managing memory, processes, : managing memory, processes,

threadsthreads User32.dllUser32.dll: user-interface tasks: user-interface tasks GDI32.dllGDI32.dll: drawing image, display text: drawing image, display text

AdvAPI32.dll object security, registry manipulationComDlg32.dll common dialog boxesComCtl32.dll all of the common window controls

other DLLs

你可以下 dumpbin /exports User32.dll 看看到底他提供了哪些 function?

Page 3: DLL Basics

IntroductionIntroduction

DLL DLL 為一堆 為一堆 function function 的集合的集合 ,, 通常不包含通常不包含 message-loopmessage-loop 建立視窗建立視窗

DLL DLL 的 的 function function 可以被可以被其他執行檔其他執行檔或 或 DLLDLL 呼叫呼叫 DLL DLL 的執行的執行

使用使用呼叫者 呼叫者 thread thread 的 的 stackstack 儲存 儲存 local variablelocal variable 記憶體配置記憶體配置 , , 使用呼叫者 使用呼叫者 threadthread 的 的 virtual space virtual space 配置配置 全區域全區域變數與變數與靜態靜態變數變數 ::

Win98 Win98 為每一個為每一個 process process 配置一份複製品配置一份複製品 Win2000Win2000 以後 以後 使用 使用 copy-on-write copy-on-write 策略策略

Page 4: DLL Basics

要注意的事情要注意的事情 一個一個 Process address space Process address space 中中 ,, 可能包含可能包含

執行檔 執行檔 image + image + 數個 數個 DLL modulesDLL modules

可能使用 可能使用 DLL DLL 的 的 module :module : 有些 有些 modules modules 使用 使用 C++ C++ run-time libraryrun-time library 有些使用 有些使用 staticstatic 版本的 版本的 librarylibrary 有些 有些 modules modules 不使用 不使用 C++ libraryC++ library

VB 或 其他程式語言

Page 5: DLL Basics

要注意的事情要注意的事情VOID EXEFunc() { PVOID pv = DLLFunc(); // Access the storage pointed to by pv... // Assumes that pv is in EXE's C/C++ run-time heap free(pv); }

PVOID DLLFunc() { // Allocate block from DLL's // C/C++ run-time heap return(malloc(100));}

VOID EXEFunc() { PVOID pv = DLLFunc(); // Access the storage pointed to by pv... // Assumes that pv is in EXE's C/C++ run-time heap free(pv); }

PVOID DLLFunc() { // Allocate block from DLL's // C/C++ run-time heap return(malloc(100));}

使用你 DLL 的人可能使用的是 static 版本的 free

在你的 DLL 中 , 使用 run-time版本的 malloc

VOID EXEFunc() { PVOID pv = DLLFunc(); // Access the storage pointed to by pv... // Assumes that pv is in EXE's C/C++ run-time heap DLLFreeFunc(pv);}

PVOID DLLFunc() { // Allocate block from DLL's C/C++ run-time heap return(malloc(100));}BOOL DLLFreeFunc(PVOID pv) { // Free block from DLL's C/C++ run-time heap return(free(pv)); }

VOID EXEFunc() { PVOID pv = DLLFunc(); // Access the storage pointed to by pv... // Assumes that pv is in EXE's C/C++ run-time heap DLLFreeFunc(pv);}

PVOID DLLFunc() { // Allocate block from DLL's C/C++ run-time heap return(malloc(100));}BOOL DLLFreeFunc(PVOID pv) { // Free block from DLL's C/C++ run-time heap return(free(pv)); }

比較好的策略 在 DLL 配置記憶體 , 在 DLL 釋放他

全部放在 DLL 中處理全部放在 DLL 中處理

Page 6: DLL Basics

The Overall PictureThe Overall Picture

名詞的定義名詞的定義 Executable moduleExecutable module 從 從 DLL importing DLL importing 函式函式

或變數來使用的 或變數來使用的 modulemodule DLL modulesDLL modules exporting function exporting function 或變數 給 或變數 給

executable moduleexecutable module 使用的 使用的 modulesmodules

DLL modules 也可以 importing 其他人的 function 與變數使用

使用 DLL 的 Module

Page 7: DLL Basics

DLL DLL 是是如何被建立如何被建立的的 以及如何被應用程式 以及如何被應用程式 implicitly linkimplicitly link

export 函式原型 , 結構Symbol 的 header file

export 函式原型 , 結構Symbol 的 header file

函式的實作函式的實作

產生 obj 檔產生 obj 檔

組合成 DLL 檔並且產生 lib 檔

組合成 DLL 檔並且產生 lib 檔

DLL export 的部分DLL export 的部分 使用 DLL 的部分使用 DLL 的部分

Import 你要使用的函式原型

Import 你要使用的函式原型

呼叫 DLL 函式呼叫 DLL 函式

產生 obj 產生 obj

結合 lib 解析出呼叫的函式 symbol

結合 lib 解析出呼叫的函式 symbol

執行的部分執行的部分

Loader 建立 address space 給 .exe Loader 建立 address space 給 .exe Loader 載入必要的 DLL 到 process的 address space

Loader 載入必要的 DLL 到 process的 address space

Lib 檔dll 檔

exe 檔

COFF 格式

COFF 格式 =Common Object File Format

Page 8: DLL Basics

建立一個最簡單的 建立一個最簡單的 DLL ModuleDLL Module

DLL DLL 可以 可以 exportexport Function, variable Function, variable 或 或 C++ classC++ class

全區域變數 : 影響抽象性 , 且不易維護全區域變數 : 影響抽象性 , 且不易維護

必須使用相同的編譯器才能 reference class

必須使用相同的編譯器才能 reference class

選擇 DLL

選擇 DLL選擇 Empty Project選擇 Empty Project

1

2

3 把後面的 MyDLL.h 與 MyDLL.cpp 加到 project 中

把後面的 MyDLL.h 與 MyDLL.cpp 加到 project 中

Page 9: DLL Basics

Export Symbol Export Symbol

下面這個檔案下面這個檔案 ,, 必須被散佈 必須被散佈

#ifdef MYLIBExport #define MYLIBAPI extern "C" __declspec(dllexport)#else #define MYLIBAPI extern "C" __declspec(dllimport)#endif

MYLIBAPI int g_nResult;MYLIBAPI int Add(int nLeft, int nRight);

#ifdef MYLIBExport #define MYLIBAPI extern "C" __declspec(dllexport)#else #define MYLIBAPI extern "C" __declspec(dllimport)#endif

MYLIBAPI int g_nResult;MYLIBAPI int Add(int nLeft, int nRight);

MyLib.h

定義 exported 函式及變數的地方

當你要 export symbol 時 , 使用這個

當你要 export symbol 時 , 使用這個

當你要 import symbol 時 , 使用這個

當你要 import symbol 時 , 使用這個

Page 10: DLL Basics

實作 實作 export export 函式函式

#include <windows.h>#define MYLIBExport #include "..\\MyLib.h"

int g_nResult;int Add(int nLeft, int nRight) { g_nResult = nLeft + nRight; return(g_nResult);}

#include <windows.h>#define MYLIBExport #include "..\\MyLib.h"

int g_nResult;int Add(int nLeft, int nRight) { g_nResult = nLeft + nRight; return(g_nResult);}

Include 標準的 windows 及 C++ run-timeLibrary 標頭檔

Include 標準的 windows 及 C++ run-timeLibrary 標頭檔

定義目前是 export定義目前是 export

Function 的 prototypeFunction 的 prototype

實作函式及變數的地方

實作函式及變數的地方

MyLibFile1.cpp

#ifdef MYLIBExport #define MYLIBAPI extern "C" __declspec(dllexport)#else #define MYLIBAPI extern "C" __declspec(dllimport)#endifMYLIBAPI int g_nResult;MYLIBAPI int Add(int nLeft, int nRight);

#ifdef MYLIBExport #define MYLIBAPI extern "C" __declspec(dllexport)#else #define MYLIBAPI extern "C" __declspec(dllimport)#endifMYLIBAPI int g_nResult;MYLIBAPI int Add(int nLeft, int nRight);

MyLib.h

Page 11: DLL Basics

為甚麼要加 為甚麼要加 extern “C” ? extern “C” ?

因為 因為 C++ C++ 編譯器會在 編譯器會在 function function 前面加上一些修前面加上一些修飾飾 ((manglemangle), ), 這樣會造成 這樣會造成 C C 程式無法 程式無法 link link 到正到正確的 確的 functionfunction

加入 加入 extern “C” function(); extern “C” function(); 可以要求編譯器可以要求編譯器不要不要加入 加入 manglemangle

為何 C++ 會對 function name 做 name name decorationdecoration 呢呢 ?? Function overloading Function overloading 與 與 c++ type-safe linkagec++ type-safe linkage

extern "C" __declspec(dllimport) void myfunction() { … }

void foo(int ,double);Comeau C++ 會把 foo 改成 foo

例如 :

void foo(int ,double);Comeau C++ 會把 foo 改成 foo_Fid F function name I 第一個參數是 int; d 第二個參數是 double

各家編譯器有不同的作法

參考資料 : http://www.comeaucomputing.com/techtalk/#externc

Page 12: DLL Basics

我們來看看差別在哪裡我們來看看差別在哪裡 !!

#ifdef MYLIBExport #define MYLIBAPI extern “C” __declspec(dllexport)#else #define MYLIBAPI extern "C" __declspec(dllimport)#endifMYLIBAPI int g_nResult;MYLIBAPI int Add(int nLeft, int nRight);

#ifdef MYLIBExport #define MYLIBAPI extern “C” __declspec(dllexport)#else #define MYLIBAPI extern "C" __declspec(dllimport)#endifMYLIBAPI int g_nResult;MYLIBAPI int Add(int nLeft, int nRight);

如果你的 MyLib.h 的內容如下使用 “ C” 的方式命名

利用 dumpbin /exports 察看 MyDll.lib 結果

看到了嗎Symbol 沒有加上 mangle

那請問一下 : 加上 extern “C” 後 , 你的 DLL 是否還支援 function overloading ?

Page 13: DLL Basics

如果你的 MyLib.h 的內容如下

#ifdef MYLIBExport #define MYLIBAPI extern __declspec(dllexport)#else #define MYLIBAPI extern __declspec(dllimport)#endifMYLIBAPI int g_nResult;MYLIBAPI int Add(int nLeft, int nRight);

#ifdef MYLIBExport #define MYLIBAPI extern __declspec(dllexport)#else #define MYLIBAPI extern __declspec(dllimport)#endifMYLIBAPI int g_nResult;MYLIBAPI int Add(int nLeft, int nRight);

沒有使用 “ C”

利用 dumpbin 察看 MyDll.lib 結果

看到了嗎Symbol 後面加了一些料

Page 14: DLL Basics

#include “MyLib.h”void main(){ int Sum=Add(1,2);}

好了 , 接下來 , 就是直接使用 function

若你是由 VC 幫你建立 DLL export symbol骨架 , 那麼預設值是不加上” C”

要注意 : 別弄錯了 ,否則會發生 link 錯誤

Page 15: DLL Basics

使用 使用 MyDLL.dllMyDLL.dll

Copy MyDLL.lib Copy MyDLL.lib 與 與 MyDLL.dllMyDLL.dll 到可以搜尋到可以搜尋到的地方 到的地方

選擇 Linker Input

選擇 Linker Input

1

設定 MyDLL.lib

設定 MyDLL.lib

2

#include “MyLib.h”void main(){ int Sum=Add(1,2);}

呼叫 MyDLL.dll export 的 function

呼叫 MyDLL.dll export 的 function

3

你也可以直接在 Source code 中加入這行 #pragma comment( lib, "c:\\kk\\MyDll.lib" )

Page 16: DLL Basics

__declspec(dllexport) 的意義 ..

當 當 linker linker 產生 產生 DLLDLL 檔時檔時 ,, 會會 合併上述資訊產生 合併上述資訊產生 .lib .lib 檔檔 並且 並且 embed embed 一個 一個 symbol table symbol table 到 到 DLLDLL 檔中檔中

extern "C" __declspec(dllexport) void myfunction() { … }

Microsoft VC 發現 __declspec(dllexport)會在 .obj 中加入額外的鏈結資訊

亦即 lib 檔中包含 DLL 所輸出的 symbol 以及相對的位址 (Relative Virtual Address)

Page 17: DLL Basics

查閱查閱 DLL DLL 檔所 檔所 export export 的 的 symbolsymbol – – 使用 使用 DumpBinDumpBin 公用程式公用程式

使用前使用前 ,, 先加入先加入 Path,Path, 使得相關的使得相關的 DLLDLL 檔可檔可以找到以找到

F:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDEF:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE

執行 執行 DumpBin.exe /exports DumpBin.exe /exports MyDll.dllMyDll.dll

Export 的 symbol相對位址

Page 18: DLL Basics

讓其他的讓其他的 IDE IDE 也能使用 也能使用 Visual C++Visual C++的的 DLLDLL

問題是問題是 對於 對於 __stdcall__stdcall 修飾字的 修飾字的 function,function, 無論是 無論是

C/C++, Visual C++ C/C++, Visual C++ 都會對 都會對 function name function name 加加入修飾符號入修飾符號 . .

__declspec(dllexport) LONG __stdcall MyFunc(int a, int b);

_MyFunc@8

後面有 8 個 bytes__stdcall: 1. 最右邊的參數先推入堆疊2. 函式返回前 , 自行移除堆疊中的參數*Win32 API 使用 __stdcall

因為 C/C++ 不定參數的關係所以必須要在 function 後面加上需要 pop 的數字

Page 19: DLL Basics

讓其他的讓其他的 IDE IDE 也能使用 也能使用 Visual C++Visual C++的的 DLLDLL

解決方案解決方案 : : 要求 要求 VCVC 不要加額外的資訊到不要加額外的資訊到DLLDLL 中中 方法一方法一 : : 編輯 編輯 .def .def 檔檔

方法二方法二 : : 在 在 DLLDLL 原始程式中加入原始程式中加入

EXPORTS MyFunc

#pragma comment(linker, "/export:MyFunc=_MyFunc@8")

Page 20: DLL Basics

沒有加入 .DEF 檔

加入 .DEF 檔

LIBRARY MYDllEXPORTS Add

dumpbin /exports MyDLL.dll

dumpbin /exports MyDLL.dll

補充

Page 21: DLL Basics

Import Import 的實際意義的實際意義 在使用 在使用 DLL import DLL import 的的 symbol symbol 時你可以用 時你可以用

externextern 代替 代替 __declspec(dllimport)__declspec(dllimport)

extern "C" __declspec(dllimport) void myfunction() { … }extern "C" __declspec(dllimport) void myfunction() { … }

extern "C“ extern void myfunction() { … }extern "C“ extern void myfunction() { … }

但是 , 如果你想產生比較有效率的程式碼 . 請使用 __declspec(dllimport)

因為 __declspec(dllimport) 可以讓編譯器知道這個 symbol 確定是由 DLL export 出來的

因為 __declspec(dllimport) 可以讓編譯器知道這個 symbol 確定是由 DLL export 出來的

Page 22: DLL Basics

Import Import 的實際意義的實際意義 Linker Linker 發現 發現 __declspec(dllimport) __declspec(dllimport) 時時 , , 會在執行檔會在執行檔

中的 中的 import sectionimport section 列出該檔所參考的 列出該檔所參考的 symbolsymbol

查閱執行檔所需的 查閱執行檔所需的 symbol symbol

使用 MyDLL.dll 的部分

使用 Kernel32.dll 的部分

DumpBin –imports UseDLL.exeDumpBin –imports UseDLL.exe

Page 23: DLL Basics

執行 執行 Executable ModuleExecutable ModuleStep1: Loader 會為該 Process 建立 virtual address

Step1: Loader 會為該 Process 建立 virtual address

Step2: 將 executable module map 到 virtual address

Step2: 將 executable module map 到 virtual address

Step3: 檢查 import section 並且 map 需要的 DLL 到 virtual space 中

Step3: 檢查 import section 並且 map 需要的 DLL 到 virtual space 中

搜尋 DLL 的順序1. 目前執行檔的位置2. 該 process 的目前目錄3. Windows 的系統目錄4. Windows 目錄5. Path 環境變數

Step4: 檢查每個 DLL 的 import section 並且 map 需要的 DLL

Step4: 檢查每個 DLL 的 import section 並且 map 需要的 DLL 系統會紀錄每個 DLL 載入的次數

使得 DLL 不會重複載入

Page 24: DLL Basics

當系統找不到 DLL 時 ,所發出的錯誤訊息

Step5: 檢查所有模組的 import symbol 與 載入 DLL 的 export 是否一樣

Step5: 檢查所有模組的 import symbol 與 載入 DLL 的 export 是否一樣

Step6: 計算每個 symbol 的實際位址 將其放到 executable module 的 import section

Step6: 計算每個 symbol 的實際位址 將其放到 executable module 的 import section

DLL base addr + RVA

事實上 , 是 update import section 中的 Imported Address Table (ITA)

事實上 , 是 update import section 中的 Imported Address Table (ITA)

Reference: ms-help://MS.MSDNQTR.2004JAN.1033/dnmsj00/html/hood0200.htm

Page 25: DLL Basics

產生你的執行程式產生你的執行程式 當你的程式碼 當你的程式碼 reference reference 一個 一個 import import

symbolsymbol 可以由 可以由 import section import section 得到真實的位址得到真實的位址 , , 因此因此

可以成功的存取 可以成功的存取 DLL DLL 的變數的變數 ,function,function 和類別和類別

缺點缺點 Loader Loader 啟動你的程式前啟動你的程式前 ,, 需要需要

重新計算重新計算每個 每個 module module 呼叫位址以及呼叫位址以及載入載入 DLL DLL 都都需要時間需要時間

由於 DLL 的 base address 可能會改變 , 所以必須在 loading time 時 ,才能決定 import function 真實所在位址

由於 DLL 的 base address 可能會改變 , 所以必須在 loading time 時 ,才能決定 import function 真實所在位址

Page 26: DLL Basics

EndEnd

Page 27: DLL Basics

附錄附錄

使用 Borland’s IMPLIB.exe 可直接將 COFF 的 library format 轉成 OMF 格式 IMPLIB (destination lib name) (source dll) 例如 : IMPLIB mydll.lib mydll.dll

OMF: Object Module Format (BCB 使用 )COFF: Common Object File Format (VC 使用 )