六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 300|回复: 0

Win32编程学习笔记

[复制链接]

升级  20%

76

主题

76

主题

76

主题

举人

Rank: 3Rank: 3

积分
260
 楼主| 发表于 2013-2-7 04:48:17 | 显示全部楼层 |阅读模式

之前也学了点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 = 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);
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表