Dynamic-Link Libraries Spring 2007 Windows Programming Chapter 20 Dynamic-Link Libraries 吳俊霖 Jiunn-Lin Wu jlwu@cs.nchu.edu.tw Dynamic-link libraries (DLLs) lie at the heart of the Microsoft Windows component. Windows is itself composed of DLLs, which are binary modules. Binary modularity is different from source code modularity, which is what C++ employs. Instead of programming giant EXEs that you must rebuild and test each time you make a change, you can build smaller DLL modules and test them individually. You can, for example, put a C++ class in a DLL, which might be as small as 12 KB after compiling and linking. Client programs can load and link your DLL very quickly when they run. Dynamic-Link Libraries Static Library DLLs have become quite easy to write. LIB Win32 has greatly simplified the programming model, and more and better support is available from the Microsoft Foundation Class (MFC) DLL Wizard and the MFC library.
Static Library Dynamic Link Library Binary 模組化 無法在 Windows 環境共用通用函式 浪費記憶體 程式沒有效率 別於 C++ 類別的建構時期模組化,DLL 乃是執行時期模組化 DLL 內的函式只有當應用程式執行時才被連結 副檔名通常是 DLL 也可以是 EXE 或 VBX, OCX ( 包含控制項的 DLL) DLL Dynamic Link Library Dynamic Link Library 將函式放在 DLL 中, 你可以在不影響使用它的程式下修改函式, 只要函式的介面 (interface) 維持一樣就可以 程式不需要重新編譯何連結就可以使用新的函式版本
有效率的重複使用程式碼 Advantages of DLLs 編譯器在編譯應用程式遇到函式庫時, 會把這些隱身在函式庫裡頭的函式實體內容如同我們在程式裡頭撰寫這些函式的原始碼般地加進應用程式的執行檔中, 也就是說當你所用的函式庫越多時, 你的執行檔也就相對的會越來越龐大, 這個做法也就是我們所謂的靜態連結 (Static Linking) 因此為了避免應用程式的過分龐大, 有人提出了動態連結 (Dynamic Linking) 的做法, 所謂動態連結就是提供了一個做法讓我們不需要把應用程式的執行檔變得如此龐大, 但一樣可以享用這些使用頻率高的函式 也就是說這些函式會在程式執行時才被載入, 而不是直接編譯在執行檔中 這樣一來可以讓我們更有效率使用這些函式 但相對的, 當你所撰寫的程式得交給他人使用時, 你除了得把你所編譯好的 EXE 檔交給他之外, 還得一併把編譯好的 DLLs 檔交給他, 否則執行起來一定會產生不可預期的錯誤 區分程式碼 Advantages of DLLs 若有朝一日發現這些被包裝在 DLLs 之中的函式的實作方法有點錯誤或是發現有更好的做法時, 需要更動僅只有部份的 DLLs 原始碼, 重新將修改過的 DLLs 給編譯後就可以達成更新程式的目的, 至於應用程式端連動都不需要動一下 ;(Dll 介面 Interface 不可以更改 ) 根據這些個特性, 咱們可以把整個應用程式中的函式依照功能或目的分類, 並將這些分類好的函式組合成許多個 DLLs 模組, 將執行檔給分割城數個小檔案, 讓這些 DLLs 模組分工合作來完成應用程式所要達到的目的 這樣一來, 對於應用程式的維護以及更新就不需要大費周章地從頭再編譯一次了, 僅需把要有修改到的 DLLs 從新編譯就可以達成程式的更新 節省記憶體的使用量 Advantages of DLLs DLLs 的載入是在應用程式執行時才被載入 (load-time dynamic linking, early binding), 甚至還可以是可以在應用程式所需用到函式時才被載入 (run-time dynamic binding) 此外 DLLs 還有一個很重要的特性 : 若不同的應用程式但需要相同的 DLL 中的函式時,DLL 僅在第一個使用到 DLL 的應用程式執行時載入, 只要這個應用程式尚未結束而其他的應用程式又正好需要使用到同一 DLL 中的函式時,DLL 不需要再重新被載入到記憶體中就可以供第一個應用程式以外需要用到 DLL 的應用程式使用, 一直到沒有任何應用程式使用這個 DLL 時,DLL 才會跟著最後一個使用 DLL 的應用程式一起從記憶體裡頭消失, 因此使用 DLLs 來包裝常用的函式是個不錯能夠節省記憶體與系統資源的做法 將程式推向國際舞台 Advantages of DLLs DLL 在設計時, 就已經被設計不只是能夠放入函式而已, 還能夠被放入許多的資源 (Resource), 如 : 可以放入選單資料 字串資料 圖形資料等等 也因此 DLL 很常被拿來作為應用程式邁向國際舞台的一個墊腳石 你可以在應用程式被執行時檢查執行應用程式的作業系統語言版本, 之後把當地語言版本的 Resource DLL 給載入, 讓所有的文字及畫面都達到當地語言化 (Localization) 的目的
Dynamic Link Library Run-Time Dynamic Linking Load-time dynamic linking (early binding) 所建立的 DLL 會在使用它的程式載入到記憶體時自動載入到記憶體中 函式的連結是在程式和 DLL 載入到記憶體時建立的 Run-time dynamic linking 程式執行後才將 DLL 載入 減少記憶體的需求量 LoadLibrary(): 將需要的 DLL 載入 (For example: Resource Dll) GetProcAddress(): 取得函式在 DLL 內的位址 FreeLibrary(): 將 DLL 從記憶體體中移除 Dynamic Link Library DLL 的內容 Functions Resource: String, Bitmap and Fonts Static global variable ( 少用 ) DLL 的介面 只有被標示為 Exported 的才可以從 DLL 外部存取 DllMain() Initialization Dynamic Link Library Types 擴充 (extension)dll 支援 C++ 介面. DLL 可以匯出整個類別而且用戶端可以建構這些類別的物件或從他們衍生類別 如果你需要可以被任何 Win32 程式設計環境載入的 DLL, 你應該使用正規 (regular)dll. 這裡較大的限制是正規 DLL 只能匯出 C 樣式的函式. 他無法匯出 C++ 類別, 成員函式或多載函式, 因為每個 C++ 編譯器有自己的裝飾名稱的方法. Note: 其實 Regular Dll 也可以匯出 C++ 類別, 只是如此一來, 所產生的 Dll 也無法給別的 C++ 編譯器使用了 這就喪失 Regular Dll 的意義了
Dynamic Link Library Types Regular DLL using shared MFC DLL 動態連結到 MFC 但是不加入自己的類別的 DLL 環境中必須有 MFC 存在 Regular DLL with MFC statically linked 靜態連結到 MFC 的 DLL 其使用不需要在環境有 MFC DLL 較大 可以被任何 Win32 程式使用 MFC extension DLL 每當需要將衍生自 MFC 的類別含入時, 你所建立的就會是這種 DLL 當你的 DLL 會從呼叫他的函式中取得指到 MFC 物件的指標時 在 MFC extension DLL 存取 MFC 類別會動態的連結 MFC 的共用版本 DLL MFC extension DLL The MFC code library used by Visual C++ is stored in a.dll. An MFC extension.dll dynamically links to the MFC code library.dll. The client application must also dynamically link to the MFC code library.dll. As the years have gone by the MFC library has grown. As a result, there are a few different versions of the MFC code library.dll out there. Both the client program and the extension.dll must be built using the same version of MFC. Therefore, for an MFC extension.dll to work, both the extension.dll and the client program must dynamically link to the same MFC code library.dll, and this.dll must be available on the computer where the application is running. Dynamic Link Library Types Dynamic Link Library Wizard
DllMain() Export Class, Functions or Resources DllMain(HINSTANCE hinstance, DWORD dwreason, LPVOID lpreserved) { // Remove this if you use lpreserved UNREFERENCED_PARAMETER(lpReserved); if (dwreason == DLL_PROCESS_ATTACH) { // Extension DLL one-time initialization if (!AfxInitExtensionModule(aaaDLL, hinstance)) return 0; new CDynLinkLibrary(aaaDLL); } else if (dwreason == DLL_PROCESS_DETACH) { // Terminate the library before destructors are called AfxTermExtensionModule(aaaDLL); } return 1; // ok } Symbolic linkage ( 符號連結 ) Microsoft 建議 Ordinal linkage ( 序號連結 ) 可讓程式 EXE 較小 CRenderDll.LIB Decorated Name MFC extension DLL AFX_EXT_CLASS Export Class Extension DLLs use the macro AFX_EXT_CLASS to export classes; the executables that link to the extension DLL use the macro to import classes. With the AFX_EXT_CLASS macro, the same header file(s) used to build the extension DLL can be used with the executables that link to the DLL. In the header file for your DLL, add the AFX_EXT_CLASS keyword to the declaration of your class as follows: class AFX_EXT_CLASS CMyClass : public CDocument { // <body of class> };
Exporting Individual Members in a Class Sometimes you may want to export individual members of your class. For example, if you are exporting a CDialog-derived class, you might only need to export the constructor and the DoModal call. You can use AFX_EXT_CLASS on the individual members you need to export. For example: class CExampleDialog : public CDialog { public: AFX_EXT_CLASS CExampleDialog(); AFX_EXT_CLASS int DoModal();... // rest of class definition... }; Export Variables and Fucntions (Regular DLLs) #ifdef EXPORTDLL #define DLLEXPORT declspec(dllexport) #else #define DLLEXPORT declspec(dllimport) #endif DLLEXPORT void RenderImage(int algorithm, int nvector, CVector *pvector, CSize imagesize, BYTE **pimage); Export Variables and Fucntions (Regular DLLs) 使用 DLL 所需要的檔案
Decorated Name DLLEXPORT extern "C" declspec(dllexport) 用戶端程式如何找到 DLL LoadLibray(): 你可以指定 DLL 的完整路徑名稱 如果你沒有指定路徑名稱, Windows 遵循下列搜尋順序 Can not export Class DLLEXPORT declspec(dllexport) 2007 by Jiunn-Lin Wu Demo <MyFunc_RegularDll> NCHU CS Export Class Functions Global variable NON-MFC DLL LIB and DLL 靜態鏈接庫與動態鏈接庫都是共享代碼的方式 如果採用靜態鏈接庫, 則無論你願不願意,lib 中的指令都被直接包含在最終產生的 EXE 文件中了 但是若使用 DLL, 該 DLL 不必被包含在最終 EXE 文件中, EXE 文件執行時可以 動態 地引用和卸載這個與 EXE 獨立的 DLL 文件 靜態鏈接庫和動態鏈接庫的另外一個區別在於靜態鏈接庫中不能再包含其他的動態鏈接庫或者靜態庫, 而在動態鏈接庫中還可以再包含其他的動態或靜態鏈接庫
DLL DLL 的編制與具體的程式設計語言及編譯器無關 只要遵循約定的 DLL 接口規范和調用方式, 用各種語言編寫的 DLL 都可以相互調用 譬如 Windows 提供的系統 DLL( 其中包括了 Windows 的 API), 在任何開發環境中都能被調用, 不在乎其是 Visual Basic Visual C++ 還是 Delphi 動態鏈接庫隨處可見 我們在 Windows 目錄下的 system32 文件夾中會看到 kernel32.dll user32.dll 和 gdi32.dll,windows 的大多數 API 都包含在這些 DLL 中 kernel32.dll 中的函數主要處理記憶體管理和進程調度 ;user32.dll 中的函數主要控制用戶介面 ;gdi32.dll 中的函數則負責圖形方面的操作 一般的程式員都用過類似 MessageBox 的函數, 其實它就包含在 user32.dll 這個動態鏈接庫中 由此可見 DLL 對我們來說其實並不陌生 VC 動態鏈接庫的分類 Visual C++ 支援三種 DLL, 它們分別是 Non-MFC DLL( 非 MFC 動態庫 ) MFC Regular DLL(MFC 規則 DLL) MFC Extension DLL(MFC 擴展 DLL) 它是 MFC 的 MFC Regular DLL 是 MFC 的 意味著可以在這種 DLL 的內部使用 MFC; 它是規則 (regular) 的 是規則的 意味著它不同於 MFC 擴展 DLL, 在 MFC 規則 DLL 的內部雖然可以使用 MFC, 但是其與應用程式的接口不能是 MFC 而 MFC 擴展 DLL 與應用程式的接口可以是 MFC, 可以從 MFC 擴展 DLL 中導出一個 MFC 類的派生類 Regular DLL 能夠被所有支援 DLL 技術的語言所編寫的應用程式調用, 當然也包括使用 MFC 的應用程式 在這種動態連接庫中, 包含一個從 CWinApp 繼承下來的類,DllMain 函數則由 MFC 自動提供 MFC Extension DLL Add More Than One Classes into a DLL MFC 擴展 (extension)dll 的內涵為 MFC 的擴展, 用戶使用 MFC 擴展 DLL 就像使用 MFC 本身的 DLL 一樣 除了可以在 MFC 擴展 DLL 的內部使用 MFC 以外,MFC 擴展 DLL 與應用程式的接口部分也可以是 MFC 我們一般使用 MFC 擴展 DLL 來包含一些 MFC 的增強功能, 譬如擴展 MFC 的 CStatic CButton 等類使之具備更強大的能力 MFC 擴展 DLL 主要強調對 MFC 進行功能擴展 因此, 如果 DLL 的目標不是增強 MFC 的功能, 其與應用程式的接口也不是 MFC, 請不要將 DLL 建立為 MFC 擴展 DLL
HW: Find Edges HW6: 寫一可以讀取 BMP 或 JPG 影像檔的程式, 並利用所提供之 CwImage 類別中的 FindEdges() 來計算影像的邊緣 (Edges) 並顯示出來 Deadline: 06/05 程式請注意以下之要求 : HW: Find Edges 請把 CwImage 類別加到自己的專案中 請寫一繼承自 CwImage 類別之影像類別, 例如 :CMyImage 你必須在 CMyImage 類別中, 實作 CwImage 類別中之純虛擬函式 Draw(CDC* pdc), 來把影像顯示在 View 中 你必須為你的 CMyImage 新增一建構式,CMyImage(int width, int height), 使該建構式可以直接設定影像類別中的影像大小, m_width 與 m_height 程式中使用 CMyImage 類別來計算影像的邊緣與顯示影像 你的程式有應該如下之片段 : CMyImage myimage(600, 400);... myimage.findedges(); 請以 Menu 中的 File/Open 來開啟一張新影像,Function/FindEdges 來計算影像邊緣 Multi-Language Support Multi-Language Support Create your own AP in English version
Resource DLL Resource DLL File/Add Project/New Project MFC DLL Resource DLL CMyAPApp::InitInstance()
Multi-Language Support 如果要中途改 resource, 那就 free 掉舊的 resource dll, 重 LoadLibrary() 一次 再呼叫 AfxSetResourceHandle(). Copy RC Copy MyAP.rc to \cht\cht.rc Edit ResorceDLL.RC Change the path of the file Resource.h Change the path of the folder Res Change the code_page and LANGUAGE Edit ResorceDLL.RC (1/3) Change Resource.h to..\\resource.h" Edit ResorceDLL.RC (2/3) Change res\\..." to..\\res\\...
Edit ResorceDLL.RC (3/3) Code_page and LANGUAGE #if!defined(afx_resource_dll) defined(afx_targ_enu) LANGUAGE 9, 1 #pragma code_page(1252) #if!defined(afx_resource_dll) defined(afx_targ_cht) LANGUAGE 4, 1 #pragma code_page(950) Translate RC Multi-Language Support
Message Box and String Table Message Box and String Table Resource.h MyAP.rc Cht.rc Message Box and String Table Export Dialogs in MFC Extension DLLs It is quite easy to export dialogs from mfc-extension dll's. Just export the corresponding class with AFX_EXT_CLASS and your done. If you do so with an application and a dll you create from scratch you probably even will succeed. But if you insert more and more resources in both the application and the dll, you will get some serious bugs, especially when we have the same string IDs in the application and the dll. Demo: <MyDialogDll_Solution1, TestDlgDll_Solution1>
Solution 1 Use the following codes in your application: Solution 2 Use the class CwDllRes by Jiunn-Lin Wu Reference: The article"export dialogs in MFC Extension DLLs" by Andreas Leitner, August 7, 1998. Demo: <MyDialogDll_Solution2, TestDlgDll_Solution2> Export Dialogs in MFC Extension DLLs CwDllRes.h Export Dialogs in MFC Extension DLLs Assume CDemoDlg is the Dialog class in your extension Dll, you should override CDemoDlg::DoMoal() function as following: Demo: <MyDialogDll_Solution2, TestDlgDll_Solution2>
Export Dialogs in MFC Extension DLLs CwDllRes.cpp