MFC中的DC,CDC和HDC
設備描述表(DC)是Windows中的一種數據結構,它包含GDI需要的所有關於顯示界面情況的描述字段,包括相連的物理設備和各種各樣的狀態信息。
圖形顯示功能是由顯卡來完成的,你想要利用顯卡的圖形顯示功能來完成你的圖形輸出,怎麼辦,給你一塊硬件能用嗎?好,現在廠商給你提供一個驅動程序,給你一些調用的接口,你就可以訪問了吧。但這種訪問是在驅動層,很麻煩,更麻煩的是,每個廠商給自己顯卡提供的驅動程序還不一樣,那我們寫的圖形輸出程序豈不是換臺機器(不一樣的顯卡)就得修改,一個字:累,兩個字:麻煩。現在一個救世主出現了,Bill兄,他讓他的兄弟們在驅動之上實現一個抽象層,當然也可以認爲是一箇中間層的軟件代碼(我們管他叫設備上下文如何,也叫設備描述表DC),由這些代碼和驅動打交道(當然,這些驅動得支持Windows平臺)。然後,他將這種實現封裝到動態鏈接庫中提供給我們使用,我們能通過動態鏈接庫暴露的API函數(相關概念見VC下半部分),得到一個這種實現(可以說是一種資源)的句柄,設備上下文的句柄(HDC),利用的這個句柄,就好像有了訪問這中實現的鑰匙了,以後就可以.......,剩下的參照VC上半部分的內容。
設備描述表DC是一個定義一組圖形對象及其屬性、影響輸出的數據結構。windows提供設備描述表,用於應用程序和物理設備之間進行交互,從而提供了應用程序設計的平臺無關性。設備描述表又稱爲設備上下文,或者設備環境。
設備描述表是一種數據結構,它包括了一個設備(如顯示器和打印機)的繪製屬性相關的信息。所有的繪製操作通過設備描述表進行。設備描述表與大多WIN32結構不同,應用程序不能直接訪問設備描述表,只能由各種相關API函數通過設備描述表的句柄(HDC)間接訪問該結構。
設備描述表總是與某種系統硬件設備相關。比如屏幕設備描述表與顯示設備相關,打印機設備描述表與打印設備相關等等。
屏幕設備描述表,一般我們簡單地稱其爲設備描述表。它與顯示設備具有一定的對應關係,在windows GDI界面下,它總是相關於某個窗口或這窗口上的某個顯示區域。通常意義上窗口的設備描述表,一般指的是窗口的客戶區,不包括標題欄、菜單欄所佔有的區域,而對於整個窗口來說,其設備描述表嚴格意義上來講應該稱爲窗口設備描述表,它包含窗口的全部顯示區域。二者的操作方法完全一致,所不同的僅僅是可操作的範圍不同而已。
windows 窗口一旦創建,它就自動地產生了與之相對應的設備描述表數據結構,用戶可運用該結構,實現對窗口顯示區域的GDI操作,如劃線、寫文本、繪製位圖、填充等,並且所有這些操作均要通過設備描述表句柄了進行。
要說設備描述表就必須先說GDI(圖形設備接口)。我們要在程序窗口上顯示圖形或文本等,就可以使用這些GDI函數在程序上“畫畫”、“寫字”。設備描述表(DC)實際上是GDI內部保存的數據結構。設備描述表中的有些值是圖形化的“屬性”,這些屬性定義了一些GDI函數工作的情況,如:文本顏色、圖形填充的情況等。HDC是設備描述表句柄類型,句柄可以簡單的理解爲指針,被定義爲32位的無符號整數。
以上內容轉自:http://longzxr.blog.sohu.com/187934817.html
DC(設備描述表):Windows應用程序通過爲指定設備(屏幕,打印機等)創建一個設備描述表(Device Context, DC)在DC表示的邏輯意義的“畫布”上進行圖形的繪製。DC是一種包含設備信息的數據結構,它包含了物理設備所需的各種狀態信息。Win32程序在繪製圖形之前需要獲取DC的句柄HDC,並在不繼續使用時釋放掉。
在c++ 編程中常會見到HDC,CDC,CClientDC,CPaintDC,CWindowDC這樣的類。
HDC是DC的句柄,API中的一個類似指針的數據類型。
CDC是MFC的DC的一個類。
CDC等設備上下分類,都含有一個類的成員變量:m_nHdc;即HDC類型的句柄。
CDC及其派生類的繼承視圖:
CObject
public |------CDC
public |------|------CClientDC
public |------|------CPaintDC
public |------|------CWindowDC
public |------|------CMetaFileDC
(注意: 除CMetaFileDC以外的三個派生類用於圖形繪製.)
CDC類定義了一個設備描述表相關的類,其對象提供成員函數操作設備描述表進行工作,如顯示器,打印機,以及顯示器描述
表相關的窗口客戶區域。
通過CDC的成員函數可進行一切繪圖操作。CDC提供成員函數進行設備描述表的基本操作,使用繪圖工具,選擇類型安全的圖形設備結構(GDI),以及色彩,調色板。除此之外還提供成員函數獲取和設置繪圖屬性,映射,控制視口,窗體範圍,轉換座標,區域操作,裁減,劃線以及繪製簡單圖形(橢圓,多邊形等)。成員函數也提供繪製文本,設置字體,打印機換碼,滾動,處理元文件。
其派生類:
1.CPaintDC:封裝BeginPaint和EndPaint兩個API的調用。
(1)用於響應窗口重繪消息(WM_PAINT)的繪圖輸出。
(2)CPaintDC在構造函數中調用BeginPaint()取得設備上下文,在析構函數中調用EndPaint()釋放設備上下文。 EndPaint()除了釋放設備上下文外,還負責從消息隊列中清除WM_PAINT消息。因此,在處理窗口重畫時,必須使用CPaintDC,否則 WM_PAINT消息無法從消息隊列中清除,將引起不斷的窗口重畫。
(3)CPaintDC也只能用在WM_PAINT消息處理之中。
2.CClientDC(客戶區設備上下文): 處理顯示器描述表的相關的窗體客戶區域。
構造時自動調用GetDC函數,析構時自動調用ReleaseDC函數.一般應用於客戶區窗口的繪製。
當需要處理一個鼠標的單擊,然後馬上畫出一個圓,你不能等到下一個WM_PAINT的消息到來才畫圖,而是馬上,這是就需要CclientDC了。它可以在OnPaint的外面創建一個客戶區域DC
void CMainWindow::OnLButtonDown (UINT nFlags, CPoint point)
{
CRect rect;
GetClientRect (&rect);
CClientDC dc (this);
dc.MoveTo (rect.left, rect.top);
dc.LineTo (rect.right, rect.bottom);
dc.MoveTo (rect.right, rect.top);
dc.LineTo (rect.left, rect.bottom);
}
3.CWindowDC: 處理顯示器描述表相關的整個窗體區域,包括了框架和控 件(子窗體)。
(1)可在非客戶區繪製圖形,而CClientDC,CPaintDC只能在客戶區繪製圖形。
(2)座標原點是在屏幕的左上角,CClientDC,CPaintDC下座標原點是在客戶區的左上角。
(3)關聯一特定窗口,允許開發者在目標窗口的任何一部分進行繪圖,包含邊界與標題,這種DC同WM_NCPAINT消息一起發送。
4.CMetaFileDC:與元文件相關的設備描述表關聯。
下面說下一些細點的知識點
1、CClientDC,CWindowDC 區別不大, 可以說 CWindowDC包含了CClientDC。 就拿記事本來說,CClientDC 就只是我們可以編輯文字的那個區域,是客戶區,CWindowDC 除了上面說的區域, 還包括菜單欄和工具欄等。
2、CClientDC和CWindowDC與 CPaintDC 的區別大點,在DC的獲取方面 CClientDC和CWindowDC 使用的是並只能是GetDC 和 ReleaseDC。CPaintDC 使用的是並只能是 BeginPaint 和 EndPaint。
3、CPaintDC只能用在響應 WM_PAINT 事件CClientDC,CWindowDC 只能用在響應非WM_PAINT 事件
4、關於 WM_PAINT事件
系統會在多個不同的時機發送WM_PAINT消息:當第一次創建一個窗口時,當改變窗口的大小時,當把窗口從另一個窗口背後移出時,當最大化或最小化窗口時,等等,這些動作都是由系統管理的,應用只是被動地接收該消息,在消息處理函數中進行繪製操作;大多數的時候應用也需要能夠主動引發窗口中的繪製操作,比如當窗口顯示的數據改變的時候,這一般是通過InvalidateRect和InvalidateRgn函數來完成的。InvalidateRect和 InvalidateRgn把指定的區域加到窗口的Update
Region中,當應用的消息隊列沒有其他消息時,如果窗口的Update Region不爲空時,系統就會自動產生WM_PAINT消息。
系統爲什麼不在調用Invalidate時發送WM_PAINT消息呢?又爲什麼非要等應用消息隊列爲空時才發送WM_PAINT消息呢?這是因爲系統把在窗口中的繪製操作當作一種低優先級的操作,於是儘可能地推後做。不過這樣也有利於提高繪製的效率:兩個WM_PAINT消息之間通過 InvalidateRect和InvaliateRgn使之失效的區域就會被累加起來,然後在一個WM_PAINT消息中一次得到更新,不僅能避免多次重複地更新同一區域,也優化了應用的更新操作。像這種通過InvalidateRect和InvalidateRgn來使窗口區域無效,依賴於系統在合適的時機發送WM_PAINT消息的機制實際上是一種異步工作方式,也就是說,在無效化窗口區域和發送WM_PAINT消息之間是有延遲的;有時候這種延遲並不是我們希望的,這時我們當然可以在無效化窗口區域後利用SendMessage
發送一條WM_PAINT消息來強制立即重畫,但不如使用Windows GDI爲我們提供的更方便和強大的函數:UpdateWindow和RedrawWindow。UpdateWindow會檢查窗口的Update Region,當其不爲空時才發送WM_PAINT消息;RedrawWindow則給我們更多的控制:是否重畫非客戶區和背景,是否總是發送 WM_PAINT消息而不管Update Region是否爲空等。
5、HDC和CDC相互轉換:
(1)、HDC到CDC的轉換:
方法一: 此方法在設備結束時不會銷燬原來的資源(即:hDC,hBitmap)
CDC *pDC = CDC::FromHandle(hDC);
方法二:此方法在設備結束時會銷燬原來的資源(即:hDC,hBitmap)
CDC dc;
dc.Attach(hDC);
(2)、CDC到HDC的轉換:
CDC dc;
HDC hDC;
hDC = dc.GetSafeHdc();
6、首先:CDC 不可以釋放。 FromHandle 是通過 HDC 來創建了一個CDC 對象,以方便操作,釋放 DC 的操作應該針對於HDC 而非此CDC , 如果釋放了它 pDC->ReleaseDC,就會造成隱患。
HDC hDC = GetDC(hWnd);
CDC *pDC = CDC::FromHandle(hDC);
這兩者是指向的一個DC對象,只能釋放一次,而這個釋放應該針對 hDC 而非 pDC。
然後:GetDC和ReleaseDC的調用匹配,CreateDC和DeleteDC的調用匹配。GetDC是從窗口獲取現有的DC,而CreateDC是創建DC,所以ReleaseDC和DeleteDC的作用一個是釋放,一個是銷燬。