之前也学了点window编程,但零零碎碎的,知识体系不完整。现在开始,边复习边学新知识。
下面都是在vs2010上写的程序。
#include<windows.h> #include<stdio.h> /** * 本程序中用了Unicode的charset,所以下在用了一些定义的宏。 * 这可以通过:project->……property->configuration properties-> character set来更改。 */ LRESULT CALLBACK WinLingProc( HWND hwnd, UINT uMsg, WPARAM wPar, LPARAM lPar ); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndcls.hCursor = LoadCursor(NULL,IDC_HAND); wndcls.hIcon = LoadIcon(NULL,IDI_HAND); wndcls.hInstance = hInstance; wndcls.lpfnWndProc = WinLingProc; wndcls.lpszClassName = L"ling"; wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW; RegisterClass(&wndcls); HWND hwnd; hwnd = CreateWindow(L"ling",L"小凌的窗口",WS_OVERLAPPEDWINDOW, 0,0,600,400,NULL,NULL,hInstance,NULL); ShowWindow(hwnd,1); MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } LRESULT CALLBACK WinLingProc( HWND hwnd, UINT uMsg, WPARAM wPar, LPARAM lPar ) { static int cxChar, cxCaps, cyChar, cyClient, iVscrollPos ; HDC hdc ; int i, y ; PAINTSTRUCT ps ; TCHAR szBuffer[10] ; TEXTMETRIC tm ; const int NUMLINES = 20; switch(uMsg) { case WM_CREATE: hdc = GetDC (hwnd) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ; cyChar = tm.tmHeight + tm.tmExternalLeading ; ReleaseDC (hwnd, hdc) ; SetScrollRange(hwnd,SB_VERT,0,NUMLINES-1,FALSE); SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ; break; case WM_CHAR: TCHAR szChar[20]; wsprintf(szChar ,L"the str is %d",wPar ); MessageBox(hwnd,szChar,L"lingyibin",0); break; case WM_LBUTTONDOWN: MessageBox(hwnd,L"mouse clicked!",L"lingyibin",0); HDC hdc; hdc=GetDC(hwnd); TextOut(hdc,0,50,L"计算机程序设计",wcslen(L"计算机程序设计")); ReleaseDC(hwnd,hdc); break; case WM_PAINT: HDC hdc2; PAINTSTRUCT ps; hdc2 = BeginPaint(hwnd,&ps); TextOut(hdc2,0,50,L"计算机程序设计",wcslen(L"计算机程序设计")); EndPaint(hwnd,&ps); break; case WM_CLOSE: if(IDYES == MessageBox(hwnd,L"是否真的关闭窗口?",L"ling",MB_YESNO)) DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_SIZE: cyClient = HIWORD (lPar) ; return 0 ; case WM_VSCROLL: switch (LOWORD (wPar)) { case SB_LINEUP: iVscrollPos -= 1 ; break ; case SB_LINEDOWN: iVscrollPos += 1 ; break ; case SB_PAGEUP: iVscrollPos -= cyClient / cyChar ; break ; case SB_PAGEDOWN: iVscrollPos += cyClient / cyChar ; break ; case SB_THUMBPOSITION: iVscrollPos = HIWORD (wPar) ; break ; } default: return DefWindowProc(hwnd,uMsg,wPar,lPar); } }
下面的程序是在窗口中打出字符串,并当鼠标左击时画一个圆。
#include<Windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam,LPARAM lParam) { HDC hdc;//设备环境句柄 PAINTSTRUCT ps; RECT rect; POINT point; switch(iMsg) { case WM_PAINT: { hdc = BeginPaint(hwnd,&ps); GetClientRect(hwnd,&rect); DrawText(hdc,"Hello,xiaoling!",-1, //-1 means print all of the characters in "Hello,xiaoling!" &rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER); EndPaint(hwnd,&ps); return 0; } case WM_LBUTTONDOWN: { hdc = GetDC(hwnd); point.x = LOWORD(lParam); point.y = HIWORD(lParam); Ellipse(hdc,point.x-50,point.y-50,point.x+50,point.y+50); ReleaseDC(hwnd,hdc); return 0; } case WM_DESTROY: { PostQuitMessage(0); return 0; } } return DefWindowProc(hwnd,iMsg,wParam,lParam); } int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { static char szAppName[] = "HelloWin32";//应用程序名 HWND hwnd;//窗口句柄 MSG msg;//消息 WNDCLASSEX wndclass;//窗口类 wndclass.cbSize = sizeof(wndclass);//窗口类结构的大小 wndclass.style = CS_HREDRAW|CS_VREDRAW;//类风格:水平和垂直方向重画 wndclass.lpfnWndProc = WndProc;//窗口过程 wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL,IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszClassName = szAppName; wndclass.lpszMenuName = NULL; wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION); RegisterClassEx(&wndclass); hwnd=CreateWindow(szAppName, "The Hello App", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd,nShowCmd); UpdateWindow(hwnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }其中有一点要注意的,就是 BeginPaint和GetDC区别
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的话,会因为无效区域为空,所有绘画操作都将被过滤掉。
例子:
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; //获取屏幕显示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);