天天看點

GDI程式設計與動态連結庫

1、GDI(圖形裝置接口)

     Windows應用程式不支援标準輸出函數(如printf),應用程式輸出包括文字在内的所有資料都是以圖形方式“繪制”到視窗上。 Windows通過圖形裝置接口GDI對圖形輸出進行支援。GDI由函數和相關資料類型、宏定義以及結構體組成。主要有:擷取和釋放裝置描述表函數、擷取裝置資訊函數、使用GDI繪圖對象函數、繪圖函數、設定和擷取裝置參數函數等,其顯示的圖形類型有:直線、、曲線、填充、位圖、文本等。      Windows對圖形顯示裝置進行了封裝,形成了一個統一的虛拟圖形顯示裝置。應用程式可以在這個虛拟裝置上進行繪圖,而虛拟裝置圖形轉換為實體裝置圖形的任務則由裝置驅動程式完成。這個虛拟圖形裝置用一個包含各種裝置屬性的資料結構來表示,稱為裝置場鏡DC(device content),又稱裝置上下文。我們也可以這樣了解,從應用程式的角度來看,裝置場鏡DC就是Windows提供的一個“畫闆”,程式在其上進行繪圖。      當程式員希望在圖形輸出裝置上繪圖時,必須先擷取裝置場鏡句柄,然後以此為參數調用GDI函數繪圖。Windows給我們提供了幾種擷取裝置場鏡句柄的方法。若在處理消息時擷取了裝置場鏡句柄,那麼久應該在退出視窗過程WndProc之前釋放它。并且DC句柄一旦釋放之後就不能再使用了,而必須重新擷取。      幾種擷取裝置場鏡句柄的方法:      a.在處理WM_PAINT消息時,使用BeginPaint和EndPaint調用;      b.使用GetDC函數擷取客戶區是裝置場鏡句柄,使用ReleaseDC釋放句柄,繪圖結果将在下次WM_PAINT消息重新整理後消失;      c.使用GetWindowDC函數擷取整個視窗的裝置場鏡句柄,調用ReleaseDC釋放該句柄;      d.使用CreateDC函數擷取裝置場鏡句柄,使用DeleteDC釋放句柄,該函數更為通用;      e.使用CreateCompatibleDC建立記憶體裝置場鏡句柄,使用DeleteDC釋放句柄,在該句柄上繪制的圖形不會顯示,隻是在記憶體中“繪圖”,故而一般需要調用GDI位圖函數将它複制到可顯示DC句柄上才能看到繪圖。

     常用GDI繪圖對象有一下幾種:      a.畫筆(Pen)      b.畫刷(Brush)      c.字型(Font)      d.調色闆(Palette)      e.裁剪區(Region)      f.位圖(BitMap)      而我們在使用GDI繪圖對象時必須按一下步驟:      a.建立繪圖對象或調用GetStockObject擷取預定義繪圖對象;      b.調用SelectObject将繪圖對象選進裝置場鏡DC中;      c.調用DeleteObject删除繪圖對象(GetStockObject擷取的對象除外)。      建立和删除繪圖對象一般有兩個時機:一個時機是在WM_CREATE消息進行中建立繪圖對象,在WM_DESTROY消息進行中删除繪圖對象,即在視窗建立時建立對象,視窗銷毀時删除對象,這樣得到的繪圖對象在整個視窗運作期間一直有效,優點是不用頻繁地建立和删除對象,缺點是繪圖對象始終占用記憶體,存儲開銷大。另一個時機是在裝置場鏡句柄有效期間,例如在BeginPaint、GetDC、GetWindowDC、CreateDC、CreateCompatibleDC之後建立繪圖對象,在EndPaint、ReleaseDC、DeleteDC之後删除繪圖對象。并且Windows規定不能删除裝置場鏡目前選擇的繪圖對象。

     雙緩沖技術:      GDI将圖形直接繪制在顯示裝置上時,如果繪制内容較多或是繪制時間較長時,會出現顯示閃爍,為解決這一問題,可以采用雙緩沖技術。      雙緩沖技術原理:在記憶體中建立一個與螢幕繪圖區域一緻的記憶體DC,然後在其上繪制圖形,這樣由于閃爍發生在記憶體中,是以不可見,繪制完成後采用BitBlt快速将圖形送到顯示裝置上,進而可避免顯示閃爍。

2、動态連結庫

     動态連結庫(DLL)是Windows最重要的組成要素之一,大多數與Windows相關的磁盤檔案如果不是程式子產品,就是動态連結程式。動态連結庫是Windows系統中實作共享函數庫概念的一種實作方式,其中常見作為DLL實作的檔案有:      a.系統API子產品(.dll)檔案:如user32.dll、kernel32.dll;      b.ActiveX控件(.ocx)檔案:如Windows上的月曆控件;      c.控制台(.cpl)檔案:控制台中的每一項都是一個專用的DLL;      d.裝置驅動程式(.drv)檔案:如控制列印到列印機的列印機驅動程式。      動态連結庫的主要作用:允許同時運作的若幹程式共享一組函數的單一拷貝,不需要重複編譯或連結,而一旦裝入記憶體,DLL函數可以被系統中的任何正在運作的應用程式軟體所使用,不必再講DLL函數的另一拷貝裝入記憶體。按照自己的了解就是,在程式運作時動态地将DLL中的函數載入記憶體,避免重複載入。

自定義DLL:

a.建立DLL工程項目 首先通過向導建立一個Win32項目,項目名稱為Win32DLL; 點選下一步,應用程式類型設定為DLL,附加選項設定為空項目,點選完成即可; 在項目中使用快捷鍵Ctrl+Shift+A,建立cpp檔案DLL.cpp以及DLL.h檔案。

b.聲明導出函數 法一:使用__declspec(dllexport),添加到需要導出的函數前進行聲明; 法二:通過子產品定義檔案(Module-Definition File即.DEF)進行聲明。 注:extern “C”的作用是以C方式導出,導出函數預設調用方式是_cdecl,函數的調用方式可在項目屬性-配置屬性-C/C++-進階-調用約定中進行設定。

c.編寫DllMain函數和導出函數 DllMain函數是DLL子產品的預設入口點,當Windows加載DLL子產品時調用。系統首先調用全局對象的構造函數,然後調用全局函數DllMain,DllMain函數不僅在将DLL連結加載到程序時被調用,在DLL子產品與程序分離時亦被調用。

d.編譯得到DLL,可使用LordPE工具檢視導出函數資訊。

DLL的調用: 載入DLL的兩種方法: a.隐式連結:連結到.lib檔案并将.dll檔案置入新項目的路徑中。是以,我們需要建立一個新的預設Win32控制台項目并添加一個源檔案,将自定義DLL拷貝到新項目相同的目錄下。如: #include  “../Win32Dll/Dll.h” #pragma  comment(lib,“../Debug/Win32Dll.lib”) ... b.顯式連結 屬于動态加載DLL,需要函數指針和一些Windows函數。與隐式連結不同,顯式連結載入DLL時,不需要DLL中的.lib或頭檔案,而隻需要DLL。如: #include “stdafx.h” #include  <windows.h> int _tmain(int argc,_TCHAR* argv[ ]) {      // 1. 定義要調用的函數指針      typedef  void(*Fun)( );      // 2. 擷取DLL的執行個體句柄      HINSTANCE hInst = LoadLibrary(L“../Debug/Win32Dll.dll“);      // 3. 得到DLL中的函數位址      Fun pMsg = (Fun)GetProcAddress(hInst,”Msg“);      // 4. 使用函數      pMsg( );      // 5. 釋放DLL      FreeLibrary(hInst);      return 0; }

應用程式查找DLL      若應用程式使用LoadLibrary進行顯示連結,那麼在該函數的參數中可以指定DLL檔案的完整路徑,如果不指定路徑或是進行隐式連結,Windows将按照下面的搜尋順序來定義DLL: a.包含EXE檔案的目錄; b.程序的目前工作目錄; c.Windows系統目錄; d.Windows目錄; e.Path環境變量中的一系列目錄。

DLL的調試: a.若動态連結庫是自己編寫的,并且測試代碼也是自己編寫的,此時我們可将動态連結庫和測試代碼的工程建立在一起,在調試測試代碼時,可直接調試動态連結庫中的代碼; b.若動态連結庫是自己編寫的,而測試代碼不是自己編寫的,此時我們需要設定動态連結庫中的項目屬性,啟動調用動态連結庫的程式,形成連接配接之後,在調試的時候才可順利在動态連結庫中調試; c.若動态連結庫和測試代碼都不是自己編寫的,此時隻能通過OllyDbg或其他調試工具進行調試。使用OllyDbg調試DLL,需保證OllyDbg目錄下有loaddll.exe檔案。

3、靜态庫

     靜态庫将函數和資料編譯進一個二進制檔案,通常命名為*.lib,編譯器在連結過程中将這些二進制資料複制出來,并與調用庫的其他子產品資料組合在一起,形成最終的可執行檔案,以後運作該可執行檔案時,就不需要這個靜态庫的支援了。 建立靜态庫工程: a.在DLL的基礎上修改工程屬性中的配置類型位靜态庫即可; b.通過向導建立靜态庫,生成選擇應用程式類型位靜态庫即可。 編寫代碼: 當我們通過向導建立Win32Lib項目,此時我們需要在新項目中建立頭檔案lib.h,源檔案lib.cpp,編譯即可。 使用靜态庫:隻能是隐式連結 #include  ”stdafx.h“ #include ”../Win32lib/lib.h“ #pragma  comment(lib,”../Debug/Win32lib.lib“) #include ”windows.h“ int  _tmain(int  argc,_TCHAR* argv[ ]) { Msg( ); return 0; }