BeginPaint和GetDC有什麼區別?

這是個windows編程問題。 
第一種情況顯示出來的字很正常。 
case WM_PAINT: 
           gdc = BeginPaint (hwnd, &ps); 
           TextOut (gdc, 0, 0, s, strlen (s)); 
           EndPaint (hwnd, &ps); 
break; 
第二種情況顯示的字不停閃爍。 
case WM_PAINT: 
           gdc = GetDC (hwnd); 
           TextOut (gdc, 0, 0, s, strlen (s)); 
           ReleaseDC (hwnd, gdc); 
break; 
請教兩種函數的作用?

BeginPaint() 和EndPaint() 可以刪除消息隊列中的WM_PAINT消息,並使無效區域有效。 
GetDC()和ReleaseDC()並不刪除也不能使無效區域有效,因此當程序跳出 WM_PAINT 時 ,無效區域仍然存在。系統就回不斷髮送WM_PAINT消息,於是程序不斷處理WM_PAINT消息。

BeginPaint、EndPaint會告訴GDI內部,這個窗口需要重畫的地方已經重畫了,這樣WM_PAINT處理完返回給系統後,系統不會再重發WM_PAINT,而GetDC沒有告訴系統這個窗口需要重畫的地方已經畫過,在你把程序返回給系統後,系統一直以爲通知你的重畫命令你還沒有乖乖的執行或者執行出錯,所以在消息空閒時,它還會不斷地發WM_PAINT催促你畫,導致程序卡死。

無效區域 :

無效區域就是指需要重畫的區域,無效的意思是:當前內容是舊的,過時的。 
假設A是新彈出的一個對話框或被激活的現有對話框,A對話框置於原來的活動對話框B前面,造成對話框B的部分或全部被覆蓋,當對話框A移開或關閉後,使對話框B原來被覆蓋的地方重新可見。那部分被覆蓋的地方就稱爲無效區域。 
只有當一個窗口消息空閒時,系統纔會抽空檢查一下這個窗口的無效區域是否爲非空(WM_PAINT的優先級是最低的。這也就是爲什麼系統很忙時窗口和桌面往往會出現變白、刷新不了、留拖拽痕跡等現象的原因),如果非空,系統就發送WM_PAINT。所以一定要用BeginPaint、EndPaint把無效區域設爲空,否則WM_PAINT將一直被髮送。

爲什麼WINDOWS要提出無效區域的概念?

這是爲了加速。 
因爲BeginPaint和EndPaint用到的設備描述符只會在當前的無效區域內繪畫,在有效區域內的繪畫會自動被過濾,大家都知道,WIN GDI的繪畫速度是比較慢的,所以能節省一個象素就節省一個,不用吝嗇,這樣可以有效加快繪畫速度。 
可見BeginPaint、EndPaint是比較“被動”的,只在窗口新建時和被摧殘時才重畫。 
而GetDC用於主動繪製,只要你指到哪,它就打到哪。它不加判斷就都畫上去,無效區域跟它沒關係。對話框沒被覆蓋沒被摧殘,它很健康,系統沒要求它重畫,但開發者有些情況下需要它主動重畫:比如一個定時換外觀的窗口,這時候就要在WM_TIMER處理代碼用GetDC。這時候再用BeginPaint、EndPaint的話,會因爲無效區域爲空,所有繪畫操作都將被過濾掉。

eg:

PAINTSTRUCT ps; 
              HDC hdc = BeginPaint(hWnd,&ps); 
              //Create a DC that matches the device 
              HDC hdcMem = CreateCompatibleDC(hdc); 
              //Load the bitmap 
              HANDLE hBmp= LoadImage(g_hInst_MainWnd,MAKEINTRESOURCE(IDB_MAINWND),IMAGE_BITMAP,0,0,0); 
              //Select the bitmap into to the compatible device context 
              HGDIOBJ hOldSel = SelectObject(hdcMem,hBmp); 
              //Get the bitmap dimensions from the bitmap 
              BITMAP bmp; 
              GetObject(hBmp,sizeof(BITMAP),&bmp); 
              //Get the window area 
              RECT rc; 
              GetClientRect(hWnd,&rc); 
              //Copy the bitmap image from the memory DC to the screen DC 
              BitBlt(hdc,rc.left,rc.top,bmp.bmWidth,bmp.bmHeight,hdcMem,0,0,SRCCOPY); 
              //Restore original bitmap selection and destroy the memory DC 
              SelectObject(hdcMem,hOldSel);     
              DeleteDC(hdcMem); 
              EndPaint(hWnd,&ps); 
              return 0;

/////////////////////////

下面是更加詳細的介紹

//======================================================================== 
//TITLE: 
//     EVC繪製位圖--BeginPaint()與GetDC()的區別 
//AUTHOR: 
//     norains 
//DATE: 
//     Tuesday   29-August-2006 
//======================================================================== 
1.BeginPaint()和GetDC() 
         在EVC中繪製位圖比較方便,有不少現成的函數可供調用,我們所要注意的只是BeginPaint()或GetDC()的使用即可. 
         因爲代碼比較簡單,所以不做更多解釋. 
         這是消息循環函數: 
         LRESULT CALLBACK MainWndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam) 
         { 
             ...... 
             switch(wMsg) 
             { 
                 case WM_PAINT: 
                         OnPaintMainWnd(hWnd,wMsg,wParam,lParam); 
                         break; 
                 ......                 
             } 
             return DefWindowProc(hWnd,wMsg,wParam,lParam); 
             ...... 
         } 
         響應WM_PAINT消息的函數,在這裏進行位圖的繪製: 
         LRESULT OnPaintMainWnd(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam) 
         { 
             PAINTSTRUCT ps; 
             HDC hdc = BeginPaint(hWnd,&ps); 
             //Create a DC that matches the device 
             HDC hdcMem = CreateCompatibleDC(hdc); 
             //Load the bitmap 
             HANDLE hBmp= LoadImage(g_hInst_MainWnd,MAKEINTRESOURCE(IDB_MAINWND),IMAGE_BITMAP,0,0,0); 
             //Select the bitmap into to the compatible device context 
             HGDIOBJ hOldSel = SelectObject(hdcMem,hBmp); 
             //Get the bitmap dimensions from the bitmap 
             BITMAP bmp; 
             GetObject(hBmp,sizeof(BITMAP),&bmp); 
             //Get the window area 
             RECT rc; 
             GetClientRect(hWnd,&rc); 
             //Copy the bitmap image from the memory DC to the screen DC 
             BitBlt(hdc,rc.left,rc.top,bmp.bmWidth,bmp.bmHeight,hdcMem,0,0,SRCCOPY); 
             //Restore original bitmap selection and destroy the memory DC 
             SelectObject(hdcMem,hOldSel);     
             DeleteDC(hdcMem); 
             EndPaint(hWnd,&ps); 
             return 0; 
         } 
         我們都知道BeginPaint()和EndPaint()需要配套使用,並且這兩個函數也只能用在WM_PAINT消息的相應函數當中.如果我們在WM_PAINT的響應函數中將以上兩個繪製函數相應替換爲GetDC()和ReleaseDC()會有什麼結果呢? 
         即: 
         HDC hdc = BeginPaint(hWnd,&ps);     -->    HDC hdc = GetDC(hWnd); 
         EndPaint(hWnd,&ps);                 -->    ReleaseDC(hWnd,hdc); 
         編譯並運行程序,我們發現窗口一片空白,好像沒有繪製位圖.但其實不盡然,我們採用單步調試,可以發現其實位圖已經繪製出來,只不過又被背景顏色抹掉了.由此可知,如果需要使用GetDC(),我們對消息循環函數必須要加上對WM_ERASEBKGND的處理: 
         LRESULT CALLBACK MainWndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam) 
         { 
             switch(wMsg) 
             { 
                 case WM_PAINT: 
                         OnPaintMainWnd(hWnd,wMsg,wParam,lParam); 
                         break; 
                 case WM_ERASEBKGND     
                         return 0;             
             } 
             return DefWindowProc(hWnd,wMsg,wParam,lParam); 
         } 
         只要系統不對WM_ERASEBKGND進行默認處理,我們用GetDC()替代BeginPaint()就可以正常使用. 
         至此我們可以看出BeginPaint(),EndPaint()和GetDC(),ReleaseDC()的區別.前一對只能用在WM_PAINT響應函數中,並且繪製背景時不會被抹掉;後一對隨處可用,但如果用在WM_PAINT響應函數中,那麼接下來將會被WM_ERASEBKGND消息的響應函數的背景繪製給抹掉. 

2.繪圖閃爍問題    

    
     有時候我們大量繪製屏幕時,可能會出現屏幕閃爍問題,這時候可以採用雙緩衝的做法.步驟首先是創建一個內存DC,然後往內存DC中繪圖,最後把內存DC的內容複製到顯示DC中,完成繪製.具體過程並不複雜,結合代碼來說明一下.
     PS:這段代碼也是相應WM_PAINT 消息的. 
     PAINTSTRUCT ps; 
     HDC hdc; 
     //獲取屏幕顯示DC         
     hdc = BeginPaint (hWnd, &ps); 
     //創建內存DC 
     HDC hdcMem = CreateCompatibleDC(hdc); 
     //創建一個bmp內存空間 
     HBITMAP hBmp = CreateCompatibleBitmap(hdc,SCREEN_WIDTH,SCREEN_HEIGHT); 
     //將bmp內存空間分配給內存DC 
     HGDIOBJ hOldSel = SelectObject(hdcMem,hBmp); 
     //這是使用者需要繪製的畫面,全部往內存DC繪製 
     Rectangle(hdcMem,0,0,SCREEN_WIDTH,SCREEN_HEIGHT); 
     DrawMenuButton(hdcMem); 
     //將內存DC的內容複製到屏幕顯示DC中,完成顯示 
     BitBlt(hdc,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,hdcMem,0,0,SRCCOPY); 
     //清除資源 
     SelectObject(hdcMem,hOldSel);     
     DeleteDC(hdcMem);     
     EndPaint(hWnd,&ps);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章