本節必須掌握的知識點:
??? ????kernel32.dll
??? ????user32.dll
??? ????gdi32.dll
■動態鏈接庫
最早的軟件開發過程,所有的功能實現都是有程序員獨立完成的。在這個過程中,我們很快就會發現,有很多常用的功能模塊是可以重復利用的,我們將其封裝為功能函數。我們將這些功能函數獨立編譯成統一格式的OBJ目標文件,并存放于一個LIB庫文件中。源程序中可以調用這些第三方功能函數,當編譯器編譯時,暫時將這些第三方功能函數的地址空下,待鏈接器鏈接時,在LIB庫中查找對應的OBJ功能模塊,并將其添加到源代碼中,填寫空下的函數地址。這就是我們所謂的靜態鏈接庫了。
在Windows操作系統中,采用的是另外一種形式的鏈接庫,我們將特定功能的第三方功能函數封裝到一個dll形式的庫中,我們將其稱為動態鏈接庫。動態鏈接庫dll文件通常保存在“C:\Windows\System32”系統目錄下。源程序中同樣直接調用第三方功能函數(需要頭文件聲明函數原型)。編譯器編譯鏈接時,會將第三方功能函數所在的dll動態鏈接庫名稱、函數名稱、函數序號以及函數相對地址記錄到一個PE文件(Windows指定的exe二進制格式文件)的節區。待編譯后的EXE文件由操作系統加載到低2GB空間時,加載dll動態鏈接庫(除了系統鏈接庫之外,也可以是自定義的dll動態鏈接庫)。此時,應用程序和dll動態鏈接庫同處于一個4GB虛擬空間內,然后根據所引用的函數名或序號在動態鏈接庫內查找該函數真實的地址,替換函數地址表內的相對地址,這樣形成一個完整的程序就可以正常運行了。
為何要采用動態鏈接庫這種形式呢?隨著應用程序的功能越來越多,所引用的庫函數也變得越來越多,如果繼續采用靜態鏈接庫的形式,無疑會大大增加程序的體積,基于有限的存儲空間和程序加載效率的考慮,設計動態鏈接庫的形式可以很好地解決上述問題。動態鏈接庫的缺點是對操作系統環境的依賴。
■API函數
對于程序員來說,Windows操作系統的功能完全由API(Application Programming?Interface首字母的縮寫,意思為程序之間的接口)來定義。API涵蓋了應用程序所能調用的全部操作系統函數,以及相關的數據類型和結構。在Windows中,API還隱含了一種特殊的程序結構,我們將在后續章節中詳細探討這種程序結構。
早期的Windows 1.0版本只能支持不到450個函數,而今天則支持數千個API函數。
我們把windows 1.0到3.1版本稱為win16,win95及98之后的所有NT版本稱為win32。從win16到win32轉化過程中大部分函數保持不變,但也有一些需要擴展,比如圖形坐標點數值從win16的16位擴展為32位。此外,win16中有些函數調用返回的二維坐標點被壓縮到一個32位的整數里,這在win32中就行不通了。為解決這些問題,win32增加了一些新的函數。
所有32位版本的windows既支持win16 API以保證和原先的程序兼容,也支持win32 API以運行新的應用程序。有意思的是,windows NT與win95和win98的工作方式不同。在WINDOWS NT中,win16的函數調用通過一個翻譯層先被轉換成win32的函數調用,然后再由操作系統來處理。而在windows95和windows 98中正好相反,win32的函數調用通過一個翻譯層先被轉換成win16的函數調用,然后再由操作系統來處理。
?????? 接下來,我們逐一介紹三個Windows應用程序必備的dll動態鏈接庫。
1.2.1 kernel32.dll
?????? kernel32.dll是一個Windows操作系統核心動態鏈接庫文件。庫中包含了九百多個API函數,這些API函數主要負責內存管理、進程和線程管理、調試、錯誤處理和時間處理等功能。
■內存管理:kernel32.dll包含了管理內存的API函數。例如:
●GlobalAlloc和LocalAlloc函數用于從堆中分配內存。
●GlobalFree和LocalFree函數用于釋放之前分配的內存。
●VirtualAlloc和VirtualFree函數用于分配和釋放大塊的進程虛擬地址空間(4GB)中的內存。
●HeapAlloc和HeapFree函數用于管理堆中的內存。
●HeapCreate函數創建私有堆。
類似的虛擬空間和堆棧的操作函數還有很多,不再一一列舉。
■進程和線程管理:Windows操作系統作為多任務系統,需要創建、管理、維護和切換多個任務。Windows操作系統是進程強相關的操作系統,進程與進程之間可以進行讀寫操作。Windows操作系統中的進程可以包含一個或多個線程,進程是一些資源的集合,進程內的所有線程可以共享這些資源。在多任務系統中,任務切換其實就是線程切換。可以是同一進程中的線程切換,也可以是不同進程間的線程切換。kernel32.dll包含的常用的進程和線程函數有:
●CreateProcess函數創建進程。
●CreateThread函數創建線程。
●CloseHandle函數減少一個內核對象計數。
●WaitForSingleObject函數檢測內核對象信號狀態。
●CreateEvent函數創建事件內核對象。
●TerminateProcess函數結束進程。
●TerminateThread函數結束線程。
■調試函數:kernel32.dll還包含了一些和調試相關的函數,包括進程間的遠程讀寫。例如:
●DebugActiveProcess函數使調試程序能夠附加到活動進程并對其進行調試。
●GetThreadContext函數可以檢索指定線程的上下文。
●ReadProcessMemory函數從指定進程中的內存區域讀取數據。
●WriteProcessMemory函數將數據寫入到指定進程中的內存區域。
■錯誤處理:在程序開發中,難免會出現一些錯誤和異常,檢測和處理這些錯誤和異常需要使用kernel32.dll中的函數。例如:
●SetUnhandledExceptionFilter函數安裝一個頂層的全局異常處理程序。
●GetExceptionCode函數用于獲取異常的類型。
●GetExceptionInformation函數用于獲取異常的信息。
●RaiseException函數在調用線程中引發異常。
●GetLastError函數檢索最后錯誤代碼。
●若要訪問消息字符串,需要使用 FormatMessage 函數。
■時間處理:Windows操作系統提供了各種各樣的時間處理函數,這些函數無疑都是在動態鏈接庫kernel32.dll中的。例如:
●系統時間函數GetSystemTime檢索 UTC 格式的當前系統日期和時間。
●SetSystemTime函數設置當前系統時間和日期。
●本地時間函數GetLocalTime檢索當前本地日期和時間。
●用于處理文件時間的函數GetFileTime檢索指定文件或目錄的創建、上次訪問和上次修改的日期和時間。
●FileTimeToLocalFileTime函數將 UTC 文件時間轉換為本地文件時間。●FileTimeToSystemTime函數將文件時間轉換為系統時間格式。
1.2.2 user32.dll
user32.dll主要負責處理用戶圖形界面,是Windows用戶界面相關應用程序接口。user32.dll中常用的API函數如下:
■窗口操作:
●CreateWindowEx函數用指定方式創建一個窗口。
●CreateMDIWindow函數創建一個多文檔界面窗口。
●DestroyWindow函數銷毀指定的窗口。
●FindWindow函數從類名或窗口名中返回一個相匹配的頂層窗口的句柄。
●GetWindow函數返回指定窗口的句柄。
●RegisterClass函數為以后調用CreatWindow函數注冊一個窗口類。
■菜單操作:
●CreateMenu函數創建一個菜單,然后用AppendMenu函數填充菜單項。
●DestroyMenu 函數銷毀指定的菜單。
●GetMenuItemInfo函數返回有關菜單項的信息。
●AppendMenu函數在給定菜單的尾不增加新項。
■對話框操作:
●CreateDialotParam函數從對話框模板資源中創建一個無模式對話框。
●DialogBoxParam 函數從對話框模板資源中創建一個模式對話框。
●MessageBox函數創建、顯示并操作一個消息框。
■GUI對象操作:
●CreateCursor函數用指定大小、位模式、熱點創建一個光標。
●CreateIcon 函數用指定大小、顏色和位模式創建一個圖標。
●CopyRect函數拷貝一個矩形坐標。
●GetScrollInfo函數返回滾動條的參數,包括最小/最大滾動位置,頁大小及拇指框的位置。
●LoadImage函數裝入一個圖標、光標或位圖。
●SetScrollRange函數設置滾動條最大或最小位置值。
提示
【注】Windows對象可以分為GUI對象、GDI對象和內核對象三類。
GUI對象包括窗口、客戶窗口、滾動條、菜單、標題欄等用戶圖形界面中的對象。
GDI對象包括畫筆、畫刷、字體、位圖、路徑、調色板等繪圖工具。
內核對象包括進程、線程、事件、信號量、互斥量等內核中創建的對象。
■消息操作:
●GetMessage函數從指定線程的消息隊列中檢取一條消息。
●PeekMessage函數檢查應用程序的消息隊列。
●PostMessage函數在指定的窗口消息隊列中放置一條消息。
●SendMessage函數把一消息發送給指定的多個窗口。
●TranslateMessage函數把虛鍵消息翻譯為字符消息。
●DispatchMessage函數傳送一個消息給指定的窗口過程。
1.2.3 gdi32.dll
gdi32.dll動態鏈接庫用于圖形設備接口(GDI)功能的實現。GDI是一個圖形接口,負責處理Windows系統中的圖形和字體操作,如繪制圖形、顯示文本、打印等操作。GDI函數有一個非常明顯的特征是函數的第一個參數一定是設備環境上下文句柄(跟蹤設備上下文地址的指針)。gdi32.dl中常用的API函數如下:
■GDI對象操作:
●CreateBitmap 函數創建具有指定寬度、高度和顏色格式的位圖, (顏色平面和每像素位) 。
●CreateBrushIndirect 函數創建具有指定樣式、顏色和圖案的邏輯畫筆。
●CreatePen 函數創建具有指定樣式、寬度和顏色的邏輯筆。 筆隨后可以選擇到設備上下文中,并用于繪制線條和曲線。
●CreateSolidBrush 函數創建具有指定純色的邏輯畫筆。
●CreateCompatibleDC 函數創建與指定設備兼容的內存設備上下文 (DC) 。
●CreateDIBitmap 函數從 DIB 創建兼容位圖 (DDB) ,并選擇性地設置位圖位。
●CreateFont 函數創建具有指定特征的邏輯字體。 隨后可以選擇邏輯字體作為任何設備的字體。
●CreatePalette 函數創建邏輯調色板。
●GetDC 函數檢索設備上下文的句柄, (指定窗口的工作區或整個屏幕的 DC) 。
●getObject 函數 (wingdi.h) 檢索指定圖形對象的信息。
■繪圖函數:
●LineTo 函數從當前位置繪制一條線,但不包括指定點。
●MoveToEx 函數將當前位置更新為指定點,并根據需要返回上一個位置。
●Rectangle 函數繪制矩形。 矩形使用當前筆輪廓,并使用當前畫筆填充。
●RoundRect 函數繪制帶圓角的矩形。 矩形使用當前筆輪廓,并使用當前畫筆填充。
●Ellipse 函數繪制橢圓形。 橢圓的中心是指定邊框的中心。 橢圓形使用當前筆輪廓,并使用當前畫筆填充。
●CreateEllipticRgn 函數創建橢圓區域。
●CreatePolygonRgn 函數創建多邊形區域。
●CreateRectRgn 函數創建矩形區域。
●DrawText 函數在指定矩形中繪制格式化文本。
●TextOut 函數使用當前所選字體、背景色和文本顏色在指定位置寫入字符串。
●FillPath 函數關閉當前路徑中所有打開的圖形,并使用當前畫筆和多邊形填充模式填充路徑的內部。
●FillRect 函數使用指定的畫筆填充矩形。 此函數包括左邊框和上邊框,但不包括矩形的右邊框和下邊框。
●FrameRect 函數使用指定的畫筆在指定矩形周圍繪制邊框。 邊框的寬度和高度始終是一個邏輯單元。
●FrameRgn 函數使用指定的畫筆在指定區域周圍繪制邊框。
●GetDIBits 函數檢索指定兼容位圖的位,并使用指定格式將其作為 DIB 復制到緩沖區中。
●PlayMetaFile 函數顯示存儲在指定設備上的給定 Windows 格式圖元文件中的圖片。PolyDraw 函數繪制一組線段和 B zier 曲線。
●PolyBezierTo 函數繪制一條或多條 B zier 曲線。
●PolylineTo 函數繪制一條或多條直線。
●SetMapMode 函數設置指定設備上下文的映射模式。 映射模式定義用于將頁面空間單位轉換為設備空間單位的度量單位,還定義設備的 x 和 y 軸的方向。
●BitBlt 函數將對應于像素矩形的顏色數據從指定的源設備上下文傳輸到目標設備上下文中。
●BeginPaint 函數準備用于繪制的指定窗口,并使用有關繪制的信息填充 PAINTSTRUCT 結構。
■打印輸出函數:
●StartDoc 函數啟動打印作業。
●EndDoc 函數結束打印作業。
●EndPage 函數通知設備應用程序已完成對頁面的寫入。 此函數通常用于指示設備驅動程序轉到新頁面。
●StartPage 函數準備打印機驅動程序以接受數據。
●GetPrinter 函數檢索有關指定打印機的信息。
●EnumPrinters 函數枚舉可用的打印機、打印服務器、域或打印提供程序。
本文摘自編程達人系列教材《Windows API每日一練》。