動態連結庫（，縮寫為 DLL）是微軟公司在 Windows 作業系統中實現共享函式庫概念的一種實作方式。這些庫函式的副檔名是DLL、OCX（包含 ActiveX 控制的函式庫）或者DRV（舊式的系統驅動程式）。 所謂動態連結，就是把一些經常會共用的程式碼（靜態連結的程式庫）製作成 DLL 檔，當執行檔呼叫到 DLL 檔內的函式時，Windows 作業系統才會把 DLL 檔載入記憶體內，DLL 檔本身的結構就是可執行檔，當程式有需求時函式才進行連結。透過動態連結方式，記憶體浪費的情形將可大幅降低。靜態連結庫則是直接連結到執行檔。 DLL 的檔案格式與視窗 EXE 檔案一樣 也就是說，等同於 32 位視窗的可移植執行檔案（PE）和 16 位視窗的（NE）。作為 EXE 格式，DLL 可以包括原始碼、資料和資源的多種組合。 在更廣泛的意義上說，任何同樣檔案格式的電腦檔案都可以稱作資源 DLL。這樣的 DLL 的例子有副檔名為 ICL 的圖示函式庫、副檔名為 FON 和 FOT 的字型檔案。 背景 DLL 的最初目的是節約應用程式所需的磁碟和記憶體空間。在一個傳統的非共享函式庫中，一部分程式碼簡單地附加到呼叫的程式上。如果兩個程式呼叫同一個子程式，就會出現兩份那段程式碼。相反，許多應用共享的程式碼能夠切分到一個 DLL 中，在硬碟上存為一個檔案，在記憶體中使用一個例項（instance）。DLL 的廣泛應用使得早期的視窗能夠在緊張的記憶體條件下執行。 DLL 提供瞭如模組化這樣的共享函式庫的普通好處。模組化允許僅僅更改幾個應用程式共享使用的一個 DLL 中的程式碼和資料而不需要更改應用程式自身。這種模組化的基本形式允許如 Microsoft Office、Microsoft Visual Studio、甚至 Microsoft Windows 自身這樣大的應用程式使用較為緊湊的補丁和服務包。 模組化的另外一個好處是外掛的通用介面使用。單個的介面允許舊的模組與新的模組一樣能夠與以前的應用程式執行時無縫地整合到一起，而不需要對應用程式本身作任何更改。這種動態擴充套件的思想在 ActiveX 中發揮到了極致。 儘管有這麼多的優點，使用 DLL 也有一個缺點：DLL 地獄，也就是幾個應用程式在使用同一個共享 DLL 函式庫發生版本衝突。這樣的衝突可以透過將不同版本的問題 DLL 放到應用程式所在的資料夾而不是放到系統資料夾來解決；但是，這樣將抵消共享 DLL 節約的空間。目前，Microsoft NET 將解決 DLL hell 問題當作自己的目標，它允許同一個共享函式庫的不同版本並列共存（WinSxS）。由於現代的計算機有足夠的磁碟空間和記憶體，這也可以作為一個合理的實現方法。 技術 記憶體管理 在 Win32 中，DLL 檔案按照片段（sections）進行組織。每個片段有它自己的屬性，如可寫或是隻讀、可執行（程式碼）或者不可執行（資料）等等。這些 section 可分為兩種，一個是與絕對地址定址無關的，所以能被多程序公用；另一個是與絕對地址定址有關的，這個就必須由每個程序有自己的副本專用。sections 的這種二分類，在編譯 DLL 時就已經由編譯器、連結器給標註好了。所以在裝入 DLL 時，裝入器知道哪些 sections 在記憶體物理地址空間只需要有一份，供多個程序共用（對映到各個程序的記憶體邏輯地址空間，所以邏輯地址可以不同）； 哪些 sections 必須是程序使用自己的專用副本。 也可在程式編譯時透過編譯選項 section 的 S Shared 屬性，顯式指定哪個節是跨程序共享的。預設情況下，DLL 的資料節都是寫時複製（COW）。 具體說，DLL 裝入時需考慮下述情形： 區域性變數 每個執行緒都有自己的棧，DLL 內部的區域性變數隨所在函式被執行而在各自執行緒的呼叫棧上開闢儲存空間。 全域性變數 DLL 內部定義的全域性變數 const 全域性變數 放入 const 節中，但不是各個程序共享；因為程序載入 DLL 時會初始化只讀全域性變數的值，這個值由可能是依賴於所在的程序，如 DLL 的函式在該程序中的邏輯地址。 非 const 全域性變數 放入各個程序各自專用的 data 節中。即 DLL 裝入時各個程序複製一份自己專用的 DLL 的 data 節。但是，對於一個程序內的多個執行緒併發訪問這種程序空間全域性變數，仍然存線上程安全問題。例如，在一個 COM 的 DLL 載入入一個程序的空間後，該程序的多個執行緒可能會併發訪問該 COM 庫的 COM 物件。為此，Windows 與 COM 引入了執行緒 套間apartment 技術。一個程序內，應用程式與載入的各個 DLL 分屬於不同的 Module，如果 DLL 使用所在 Module 的全域性變數，例如動態連結 MFC 的 regular dll 在訪問自己的 MFC 全域性變數時，應該明確宣告。 訪問 DLL 以外定義的全域性變數 使用間址技術，在 DLL 的 data 節中用一個指標資料型別的記憶體空間來儲存一個外部全域性變數的地址。 函式呼叫 呼叫 DLL 內部定義的函式。這不是問題。 呼叫 DLL 外部定義的函式。例如，DLL 內部呼叫一個外部函式 foo 。這個 foo 函式在程序 1 中可能實現為 四捨五入，在程序 2 中實現為 下取整。所以呼叫外部函式是各個程序私用的事情。解決辦法是使用間址技術，在 data 節中用一個 函式指標 資料型別的記憶體空間來儲存這種外部函式的入口地址。 跳轉指令 DLL 內部跳轉，不是問題 跳轉到 DLL 外部，解決同上述 32 DLL 程式碼段通常被使用這個 DLL 的所有程序所共享。如果程式碼段所佔據的物理記憶體被收回，它的內容就會被放棄，後面如果需要的話就直接從 DLL 檔案重新載入。 與程式碼段不同，DLL 的資料段通常是私有的；也就是說，每個使用 DLL 的程序都有自己的 DLL 資料副本。作為選擇，資料段可以設定為共享，允許透過這個共享記憶體區域進行程序間通訊。但是，因為使用者許可權不能應用到這個共享 DLL 記憶體，這將產生一個安全漏洞；也就是一個程序能夠破壞共享資料，這將導致其它的共享程序異常。例如，一個使用訪客賬號的程序將可能透過這種方式破壞其它執行在特權賬號的程序。這是在 DLL 中避免使用共享片段的一個重要原因。 當 DLL 被如 UPX 這樣一個可執行的 packer 壓縮時，它的所有程式碼段都標記為可以讀寫並且是非共享的。可以讀寫的程式碼段，類似於私有資料段，是每個程序私有的並且被頁面檔案備份。這樣，壓縮 DLL 將同時增加記憶體和磁碟空間消耗，所以共享 DLL 應當避免使用壓縮 DLL。 符號解析和繫結 DLL 輸出的每個函式都由一個數字序號唯一標識，也可以由可選的名字標識。同樣，DLL 引入的函式也可以由序號或者名字標識。對於內部函式來說，只輸出序號的情形很常見。對於大多數視窗 API 函式來說名字是不同視窗版本之間保留不變的；序號有可能會發生變化。這樣，我們不能根據序號引用視窗 API 函式。 按照序號引用函式並不一定比按照名字引用函式效能更好：DLL 輸出表是按照名字排列的，所以對半查詢可以用來在在這個表中根據名字查詢這個函式。另外一方面，只有線性查詢才可以用於根據序號查詢函式。 將一個可執行檔案繫結到一個特定版本的 DLL 也是可能的，這也就是說，可以在編譯時解析輸入函式（imported functions）的地址。對於繫結的輸入函式，連結工具儲存了輸入函式繫結的 DLL 的時間戳和校驗和。在執行時 Windows 檢查是否正在使用同樣版本的函式庫，如果是的話，Windows 將繞過處理輸入函式；否則如果函式庫與繫結的函式庫不同，Windows 將按照正常的方式處理輸入函式。 繫結的可執行檔案如果執行在與它們編譯所用的環境一樣，函式呼叫將會較快，如果是在一個不同的環境它們就等同於正常的呼叫，所以繫結輸入函式沒有任何的缺點。例如，所有的標準 Windows 應用程式都繫結到它們各自的 Windows 釋出版本的系統 DLL。將一個應用程式輸入函式繫結到它的目的環境的好機會是在應用程式安裝的過程。 執行時通知 DLL 程序 執行緒載入 程序 執行緒載入時，可以透過 DllMain 函式通知 DLL 相關資訊，提供對應處理的機會。 BOOL WINAPI DLLMain HINSTANCE hinstDLLDWORD fdwReasonLPVOID fImpLoad switch fdwReason case DLL_PROCESS_ATTACH 當這個 DLL 第一次被對映到了這個程序的地址空間時。DLLMain 函式的返回值為 FALSE，說明 DLL 的初始化沒有成功，系統就會終結整個程序，去掉所有檔案映象，之後顯示一個對話方塊告訴使用者程序不能啟動。 break case DLL_THREAD_ATTACH 一個執行緒被建立，新建立的執行緒負責執行這次的 DllMain 函式。系統不會讓程序已經存在的執行緒以 DLL_THREAD_ATTACH 的值來呼叫 DllMain 函式。主執行緒永遠不會以 DLL_THREAD_ATTACH 的值來呼叫 DllMain 函式。系統是順序呼叫 DllMain 函式的，一個執行緒執行完 DllMain 函式才會讓另外一個執行緒執行 DllMain 函式。 break case DLL_THREAD_DETACH 如果執行緒呼叫了 ExitThread 來結束執行緒（執行緒函式返回時，系統也會自動呼叫 ExitThread）。執行緒呼叫了 TerminateThread，系統就不會用值 DLL_THREAD_DETACH 來呼叫所有 DLL 的 DllMain 函式。 break case DLL_PROCESS_DETACH 這個 DLL 從程序的地址空間中解除對映。如果程序的終結是因為呼叫了 TerminateProcess，系統就不會用 DLL_PROCESS_DETACH 來呼叫 DLL 的 DllMain 函式。這就意味著 DLL 在程序結束前沒有機會執行任何清理工作。 break return TRUE 動態連結庫搜尋順序 對於 Windows，載入動態連結庫時： 如果記憶體中已經有同 module 名的 DLL，除非是 DLL redirection 或 manifest，否則直接就用記憶體中這個 DLL 而不再搜尋。 如果 DLL 名字屬於當前 Windows 版本的 Known DLL，則必須用 Known DLL。清單見 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerKnownDLLs 如果 DLL 有依賴 DLL，作業系統按預設標準規則根據 module 名字搜尋依賴 DLL。即使第一個 DLL 指定了全路徑。 Windows Desktop 應用程式的 DLL 標準搜尋序： 應用程式所在目錄； 系統目錄。GetSystemDirectory 函式返回該目錄。 16 位元系統目錄； Windows 目錄。使用 GetWindowsDirectory 函式返回該目錄。 當前（工作）目錄； 環境變數 PATH 中列出的目錄。 如果 SafeDllSearchMode 被禁止，則當前目錄成為第二個被搜尋的目錄。 Windows Desktop 應用程式的 DLL 替代搜尋序。這包括兩種情況： LoadLibraryEx 函式使用引數 LOAD_WITH_ALTERED_SEARCH_PATH：LoadLibraryEx 函式不是從應用程式所在目錄開始，而是從引數 lpFileName（即要載入的 dll）所在目錄開始搜尋所有依賴 DLL。 用 SetDllDirectory 函式修改搜素序。 當所有 DLL 都完成搜尋，要開始執行 DLL 的初始化時，替代搜尋序結束，恢復標準搜尋序。 執行時顯式連結 對於一個程序，每個被載入的 DLL 在程序的 PEB 中有一個引用計數器，在當前程序中每多一次載入該計數器便加一。LoadLibrary 與 FreeLibrary 指令影響每一個行程內含的計數器；動態連結則不影響。因此藉由呼叫 FreeLibrary 而從記憶體解除安裝一 DLL 是很重要的。一個行程可以從它自己的 VAS 登出此計數器。該計數器變為 0 時，這個 DLL 從當前程序中解除安裝，但並不意味著會從物理記憶體中解除安裝，因為其它程序可能使用這個 DLL。 DLL 檔案能夠在執行時使用 LoadLibrary（或者 LoadLibraryEx）API 函式進行顯式呼叫，這個的過程微軟簡單地稱為執行時動態呼叫。如果在上述 API 的引數中指明瞭庫檔案的全路徑，則不再搜尋這個庫檔案。API 函式 GetProcAddress 查詢具有某名稱的輸出函式、FreeLibrary 解除安裝 DLL（實際上是引用計數器減一）。這些函式類似於 POSIX 標準 API 中的 dlopen、dlsym、和 dlclose。 注意微軟簡單稱為 執行時動態連結 的執行時隱式連結，如果不能找到連結的 DLL 檔案，Windows 將提示一個錯誤訊息並且呼叫應用程式失敗（準確地說是不能建立程序）。應用程式開發人員不能透過編譯連結來處理這種缺少 DLL 檔案的隱式連結問題，而且在更改了實現後還必須重新編譯連結整個程式 預設的增量連結模式會一直保留著那條缺少的函式引用，只有重新編譯連結才能去掉。相反，雖然顯式連結的程式碼量增多了，但開發人員有機會提供一個完善的出錯處理機制。 執行時顯式連結的過程在所有語言中都是相同的，因為它依賴於 Windows API 而不是語言結構。只要一種語言能夠呼叫上述的 LoadLibrary 等函式，就能執行執行時顯示連結。 Win16 下的 DLL 16 位 Windows，所有程序共用同一個記憶體地址空間。如果一個 DLL 被多個程序使用，它只被載入到記憶體中一次，只有一份資料節（data segment）。也就是說，DLL 是系統全域性而不是程序內的；程序不能得到 DLL 的一份獨立的複製。每個 DLL 在記憶體中只有一份例項（instance）。 如果一個 EXE 檔案同時執行多次，那麼在記憶體中只有該 EXE 的一套只讀複製（如程式碼或資源），但這個 EXE 的每個程序都有自己的一套資料段，即有多份例項（instance）。實際上，程序的例項控制代碼就是 data segment（存放程序的全域性變數）的記憶體起始地址。 模組（module）是指一個硬碟檔案，可被載入到記憶體中。模組控制代碼是一個數據結構，表示這個硬碟檔案的各部分（section）出自哪裡，是否已經載入到記憶體中。 所以程序只能用例項控制代碼標識，而不能用模組控制代碼標識。 Win32 的程序使用自己專用的邏輯記憶體空間。程序的全域性變數不再是跨程序邊界可見的。例項控制代碼與模組控制代碼相同，都是指向模組載入後的記憶體基地址。這樣規定實際上也相容了 Win16 時例項控制代碼與模組控制代碼的含義。 資源載入 EXE 和 DLL 都有其自己的資源（如對話方塊資源），而且這些資源的 ID 可能重複，預設使用 EXE 的資源。如果需要載入、使用 DLL 中的資源，需要透過 DLL 載入後的例項控制代碼（HINSTANCE）來找到 DLL 的資源。 應用程式程序本身及其呼叫的每個 DLL 模組都具有一個全域性唯一的 HINSTANCE 控制代碼，它們代表了 EXE 或 DLL 模組在程序邏輯地址空間中的起始地址。程序本身的模組控制代碼一般為 0x400000，而 DLL 模組預設載入地址為 0x10000000。如果程式同時載入了多個 DLL，則每個 DLL 模組都會有不同的 HINSTANCE。 幾種可行的辦法： 方法 1 in MFC DLL void CDLLShowDlg void AFX_MANAGE_STATE AfxGetStaticModuleState CDialog dlg IDD_DLL_DIALOG 開啟 ID 為 2000 的對話方塊 dlgDoModal AFX_MANAGE_STATE AfxGetStaticModuleState 必須作為介面函式的第一條語句。功能是在棧上建立一個 AFX_MAINTAIN_STATE2 類的例項，利用其建構函式和解構函式對_afxThreadStateGetData 所指向記憶體儲存塊的模組狀態（AFX_MODULE_STATE 型別）設定現場（AfxGetModuleState 函式返回的值）及恢復現場。 方法 2： in MFC DLL void CDLLShowDlg void HINSTANCE save_hInstance AfxGetResourceHandle 當前資源控制代碼 AfxSetResourceHandle theAppm_hInstance 設定為當前 DLL 的例項控制代碼所對應的資源控制代碼 CDialog dlg IDD_DLL_DIALOG 開啟指定 ID 的對話方塊 dlgDoModal AfxSetResourceHandle save_hInstance 方法 3： in EXE void CEXEOnButtonClick HINSTANCE exe_hInstance GetModuleHandle NULL HINSTANCE dll_hInstance GetModuleHandle SharedDlldll AfxSetResourceHandle dll_hInstance 切換狀態 ShowDlg AfxSetResourceHandle exe_hInstance 恢復狀態 編譯器和語言考慮 Delphi 在原始檔的開頭使用關鍵詞 library 而不是 program，在檔案的末尾輸出函式使用 exports 排列。 Delphi 不需要 LIB 檔案以從 DLL 中輸入函式。為了連結一個 DLL，在函式宣告中使用關鍵詞 external。 微軟 Visual Basic 及 Net 框架 在 Visual Basic（VB）中只支援執行時連結；但是除了使用 LoadLibrary 和 GetProcAddress 這兩個 API 函式之外，允許使用輸入函式的宣告來引入 DLL 函式，如果找不到 DLL 檔案，VB 將產生一個執行時異常。開發人員可以捕獲該異常並且進行適當的處理。 Visual Basic 6 這樣的較老的語言，只能呼叫__stdcall 呼叫約定修飾的函式，並需要在宣告 DLL 的輸出函式時使用 Alias，否則就會出現在 DLL 中找不到函式入口點的錯誤。例如： Public Declare Function test2 Lib PackingDLLdll Alias _test24 ByVal param As Integer As Integer 也可以用 def 檔案來定義 dll 輸出函式的名字。MSDNExporting from a DLL Using DEF Files。另外需要注意，VB6 呼叫的 dll，如果還依賴其他的 dll，那麼這些間接依賴的 dll 必須在 VB6 程式的 dll 搜尋路徑上，否則會報無法找到（被直接依賴的）dll 的錯誤。 為了相容，Declare Function 的方式一直沿用到 VBnet。不過在 net 中，平臺呼叫提供了一種新的宣告 DLL 中的輸出函式方式，透過 SystemRuntimeInteropServicesDllImportAttribute 提供。例如： DllImportAttribute user32dll EntryPointMessageBoxW SetLastErrorTrue CharSetCharSetUnicode ExactSpellingTrue CallingConventionCallingConventionStdCall Public Function MessageBox hWnd As Integer lpText As String lpCaption As String uType As UInteger As Integer End Function 在同為 net 語言的 C 中，這是僅有的處理方式。當然託管 CC 就不必受此限制，見下節。 C 和 C 微軟 Visual C（MSVC）提供了許多標準 C 的擴充套件，它允許直接在 C 程式碼中將函式（類、資料變數）宣告為輸入或輸出；這種做法已經被 Windows 平臺上其他的 C 和 C 編譯器採納，包括 Windows 平臺上的 GCC。這種擴充套件在函式宣告前使用__declspec 屬性： _declspec dllexport 用於在 DLL 原始檔中宣告要輸出的 C 類、函式以及資料。 _declspec dllimport 用於在外部程式宣告由 DLL 輸出的 C 類、函式以及資料。很多情況下不使用__declspec dllimport 也能正確編譯程式碼。但使用__declspec dllimport 使編譯器可以生成更好的程式碼，因為它可以確定函式是否存在於 DLL 中，這使得在跨 DLL 邊界的函式呼叫時編譯器可以生成跳過間接定址級別的程式碼。需要特別注意的是，必須使用__declspec dllimport 才能匯入 DLL 中輸出的變數。例如，DLL 輸出一個 C 類，該類有一個靜態變數，那麼在外部檔案使用這個類時必須用__declspec dllimport 宣告。 如果是遵從 C 命名規範（naming convention）的外部名字，它們必須在 C 程式碼中宣告為 extern C 以避免它們使用 C 命名規範。如果使用 dll 的語言（如 Fortran、Visual Basic 6）不能識別 C 命名規範，那麼需要採取辦法指出在 DLL 中輸出函式的名字。也可以透過 DEF 檔案來定義輸出函式的名字與序號。或者使用如下的連結指令來制定 dll 輸出函式名字： pragma comment linker exportaddadd8 除了使用__declspec 屬性定義輸入輸出函式之外，它們也可以列在專案 DEF 檔案的 IMPORT 或者 EXPORTS 部分。DEF 檔案不是由編譯器進行處理，而是由連結器據此生成 DLL 檔案中輸出函式的名字與順序（ordinal number），這樣 DEF 檔案就不是 CC 特有的，其它語言寫的程式如果想要編譯為 DLL 也可以使用 DEF。 DLL 的編譯將生成 DLL 和 LIB 兩個檔案。LIB 檔案被稱為輸入庫（import library），在編譯時為呼叫 DLL 的程式提供 樁（stub）實際上是間接跳轉到執行時載入的 DLL 的對應的函式上再繼續執行。這種透過輸入庫來使用 DLL 的方式，在程式執行時啟動程序時就會自動（隱式）載入所有用到的 DLL。另一種使用 DLL 的方式是透過 LoadLibrary（或者 LoadLibraryEx） API 函式進行顯式載入 DLL，用 GetProcAddress API 函式透過函式名稱獲取其載入後的記憶體地址、透過 FreeLibrary 解除安裝 DLL。 DLL 一般說來必須放在 PATH 環境變數、預設系統路經或者是使用它的程式所在路徑三個的一個之內。COM 伺服器 DLL 使用 regsvr32exe 註冊，它將 DLL 的路徑和全域性唯一身份（GUID）記錄在登錄檔中。應用程式能夠透過在登錄檔中查詢 GUID、找到它的路徑從而使用這個 DLL。 程式設計例項 建立 DLL 輸出函式 下面的例子展示了與特定語言相關的從 DLL 輸出符號表的方法。 Delphi library Example Function that adds two numbers function AddNumbers a b Double Double cdecl begin AddNumbers a b end Export this function exports AddNumbers DLL initialization code no special handling needed begin end C 或 C include windowsh Export this function extern C __declspec dllexport double AddNumbers double a double b DLL initialization function BOOL APIENTRY DllMain HANDLE hModule DWORD dwReason LPVOID lpReserved return TRUE Function that adds two numbers double AddNumbers double a double b return a b 使用 DLL 輸入 下面的例子展示了與特定語言相關的如何在編譯時連結 DLL 輸入符號表的方法。 Delphi program Example APPTYPE CONSOLE Import function that adds two numbers function AddNumbers a b Double Double cdecl external Exampledll var result Double begin result AddNumbers 1 2 Writeln The result was result end C 或 C include windowsh include stdioh Import function that adds two numbers extern C __declspec dllimport double AddNumbers double a double b int main int argc char argv double result AddNumbers 1 2 printf The result was fn result return 0 執行時使用顯式呼叫 下面的例子展示瞭如何使用不同語言特有的 WIN32 API 繫結進行執行時的呼叫和連結。 Microsoft Visual Basic Option Explicit Declare Function AddNumbers Lib Exampledll ByVal a As Double ByVal b As Double As Double Sub Main Dim Result As Double Result AddNumbers 1 2 DebugPrint The result was Result End Sub C 或 C include windowsh include stdioh DLL function signature typedef double importFunctiondouble double int main int argc char argv importFunction addNumbers double result Load DLL file HINSTANCE hinstLib LoadLibrary Exampledll if hinstLib NULL printf ERROR unable to load DLLn return 1 Get function pointer addNumbers importFunction GetProcAddress hinstLib AddNumbers if addNumbers NULL printf ERROR unable to find DLL functionn return 1 Call function result addNumbers 1 2 Unload DLL file FreeLibrary hinstLib Display result printf The result was fn result return 0 元件物件模型 元件物件模型（COM）將 DLL 概念擴充到了面向物件程式設計。物件能夠從另外一個程序呼叫或者在另外一臺機器上執行。COM 物件有一個唯一的 GUID 並且能夠實現強大的後臺以簡化如 Visual Basic 和 ASP 這樣的 GUI 前臺應用。它們也可以使用指令碼語言程式設計。COM 物件的建立和使用比 DLL 更為複雜。 參見 Dependency Walker 顯示 DLL 和 EXE 檔案輸出和引用函式的工具 Dynamic Library Library Linking Computer Science 連結器 載入器 目的碼 Shared Library 靜態庫 DLL 地獄 備註 外部連結 Dll Download 自由的 Windows 和第三方軟體擴充套件中用到的 Dll 下載 __declspec C Language Reference on MSDN dllexport dllimport on MSDN DynamicLink Libraries on MSDN DynamicLink Library Functions on MSDN Microsoft Portable Executable and Common Object File Format Specification Win32 DLL wwwfunctionxcom 上的 DLL 製作和使用教程 Delay Load Dlls Error Recovery on wwwcodemaestrocom Dll Download and Repair on wwwmydllorg 參考文獻 Hart Johnson Windows System Programming Third Edition AddisonWesley 2005 ISBN 0321256190 Rector Brent et al Win32 Programming AddisonWesley Developers Press 1997 ISBN 0201634929 Category 檔案格式 Category 計算機程式庫 CategoryWindows 管理