动态链接库

動態連結函式庫（英語：Dynamic-link library，縮寫為DLL）是微軟公司在微軟視窗作業系統中實現共用函式函式庫概念的一種實作方式。這些函式庫函式的副檔名是.DLL、.OCX（包含ActiveX控制的函式庫）或者.DRV（舊式的系統驅動程式）。
所謂動態連結，就是把一些經常會共用的程式碼（靜態連結的OBJ程式庫）製作成DLL檔，當執行檔呼叫到DLL檔內的函式時，Windows作業系統才會把DLL檔載入記憶體內，DLL檔本身的結構就是可執行檔，當程式有需求時函式才進行連結。透過動態連結方式，記憶體浪費的情形將可大幅降低。靜態連結函式庫則是直接連結到執行檔。
DLL的檔案格式與視窗EXE檔案一樣——也就是說，等同於32位元視窗的可移植執行檔案（PE）和16位元視窗的New Executable（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問題當作自己的目標，它允許同一個共用函式庫的不同版本並列共存。由於現代的電腦有足夠的磁碟空間和記憶體，這也可以作為一個合理的實現方法。

符號解析和繫結

DLL輸出的每個函式都由一個數字序號唯一標識，也可以由可選的名字標識。同樣，DLL引入的函式也可以由序號或者名字標識。對於內部函式來說，只輸出序號的情形很常見。對於大多數視窗API函式來說名字是不同視窗版本之間保留不變的；序號有可能會發生變化。這樣，我們不能根據序號參照視窗API函式。
按照序號參照函式並不一定比按照名字參照函式效能更好：DLL輸出表是按照名字排列的，所以對半尋找可以用來在在這個表中根據名字尋找這個函式。另外一方面，只有線性尋找才可以用於根據序號尋找函式。
將一個可執行檔繫結到一個特定版本的DLL也是可能的，這也就是說，可以在編譯時解析輸入函式（imported functions）的位址。對於繫結的輸入函式，連結工具儲存了輸入函式繫結的DLL的時間戳和校驗和。在執行時Windows檢查是否正在使用同樣版本的函式庫，如果是的話，Windows將繞過處理輸入函式；否則如果函式庫與繫結的函式庫不同，Windows將按照正常的方式處理輸入函式。
繫結的可執行檔如果執行在與它們編譯所用的環境一樣，函式呼叫將會較快，如果是在一個不同的環境它們就等同於正常的呼叫，所以繫結輸入函式沒有任何的缺點。例如，所有的標準Windows應用程式都繫結到它們各自的Windows發布版本的系統DLL。將一個應用程式輸入函式繫結到它的目的環境的好機會是在應用程式安裝的過程。

執行時通知DLL處理程序/執行緒載入

處理程序/執行緒載入時，可以通過DllMain函式通知DLL相關資訊，提供對應處理的機會。
BOOL WINAPI DLLMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID 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);}

執行時顯式連結

對於一個處理程序，每個被載入的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 DLLvoid CDLL::ShowDlg(void){
       AFX_MANAGE_STATE(AfxGetStaticModuleState());
       CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
       dlg.DoModal();}
AFX_MANAGE_STATE(AfxGetStaticModuleState());必須作為介面函式的第一條語句。功能是在棧上建立一個AFX_MAINTAIN_STATE2類別的實體，利用其建構函式和解構函式對_afxThreadState.GetData()所指向記憶體儲存塊的模組狀態（AFX_MODULE_STATE類型）設定現場（AfxGetModuleState函式返回的值）及恢復現場。  
方法2：
// in MFC DLLvoid CDLL::ShowDlg(void){
       HINSTANCE save_hInstance = AfxGetResourceHandle();    //当前资源句柄
       AfxSetResourceHandle(theApp.m_hInstance);             //设置为当前DLL的实例句柄所对应的资源句柄
       CDialog dlg(IDD_DLL_DIALOG);                          //打开指定ID的对话框
       dlg.DoModal();
       AfxSetResourceHandle(save_hInstance);     
}
方法3：
// in EXEvoid CEXE::OnButtonClick(){
       HINSTANCE exe_hInstance = GetModuleHandle(NULL);
      HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll"); 
      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 "PackingDLL.dll" Alias "_test2@4" (ByVal param As Integer) As Integer
也可以用def檔案來定義dll輸出函式的名字。。另外需要注意，VB6呼叫的dll，如果還依賴其他的dll，那麼這些間接依賴的dll必須在VB6程式的dll搜尋路徑上，否則會報無法找到（被直接依賴的）dll的錯誤。
為了相容，Declare Function 的方式一直沿用到 VB.net。不過在 .net 中，平台呼叫提供了一種新的聲明DLL中的輸出函式方式，通過 System.Runtime.InteropServices.DllImportAttribute 提供。例如：
<DllImportAttribute("user32.dll", EntryPoint:="MessageBoxW", SetLastError:=True, CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>Public Function MessageBox(hWnd As Integer, lpText As String, lpCaption As String, uType As UInteger) As IntegerEnd Function
在同為 .net 語言的 C# 中，這是僅有的處理方式。當然代管 C/C++ 就不必受此限制，見下節。

建立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 <windows.h>
 
 // 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;
 }

執行時使用顯式呼叫

下面的例子展示了如何使用不同語言特有的WIN32 API繫結進行執行時的呼叫和連結。
Microsoft Visual Basic
 Option Explicit
 Declare Function AddNumbers Lib "Example.dll" (ByVal a As Double, ByVal b As Double) As Double
 
 Sub Main()
     Dim Result As Double
     Result = AddNumbers(1, 2)
     Debug.Print "The result was: " & Result
 End Sub
C 或 C++
 #include <windows.h>
 #include <stdio.h>
 
 // DLL function signature
 typedef double (*importFunction)(double, double); 

 int main(int argc, char **argv)
 {
     importFunction addNumbers;
     double result;
 
     // Load DLL file
     HINSTANCE hinstLib = LoadLibrary("Example.dll");
     if (hinstLib == NULL) {
         printf("ERROR: unable to load DLL\n");
         return 1;
     }
 
     // Get function pointer
     addNumbers = (importFunction)GetProcAddress(hinstLib, "AddNumbers");
     if (addNumbers == NULL) {
         printf("ERROR: unable to find DLL function\n");
         return 1;
     }
 
     // Call function.
     result = addNumbers(1, 2);
 
     // Unload DLL file
     FreeLibrary(hinstLib);
 
     // Display result
     printf("The result was: %f\n", result);
 
     return 0;
 }

元件物件模型

元件物件模型（COM）將DLL概念擴充到了物件導向程式設計。物件能夠從另外一個處理程序呼叫或者在另外一台機器上執行。COM物件有一個唯一的GUID並且能夠實現強大的後台以簡化如Visual Basic和ASP這樣的GUI前台套用。它們也可以使用手稿語言編程。COM物件的建立和使用比DLL更為複雜。