這是個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);