一步步学破解-sdk用实例讲解GDI(含各个消息的调用时机)

 

窗口过程函数传入的参数lParam包含了鼠标的位置,其中底位为x坐标,高位为y坐标,这些坐标值都是相对于窗口客户区的左上角的值,wParam中则包含了鼠标按钮的状态.  
   
 1
、窗口消息:WM_CREATEWM_DESTROYWM_CLOSE  
   
         
我们创建一个窗口对象的时候,这个窗口对象在创建过程中收到的就是WM_CREATE消息,对这个消息的处理过程一般用来设置一些显示窗口前的初始化工作,如设置窗口的大小,背景颜色等,WM_DESTROY消息指示窗口即将要被撤消,在这个消息处理过程中,我们就可以做窗口撤消前的一些工作。WM_CLOSE消息发生在窗口将要被关闭之前,在收到这个消息后,一般性的操作是回收所有分配给这个窗口的各种资源。在windows系统中资源是很有限的,所以回收资源的工作还是非常重要的。  


 2
、键盘消息:WM_CHARWM_KEYDOWNWM_KEYUP  
   
         
这三个消息用来处理用户的键盘数据,当用户在键盘上按下某个键的时候,会产生WM_KEYDOWN消息,释放按键的时候又回产生WM_KEYUP消息,所以WM_KEYDOWNWM_KEYUP消息一般总是成对出现的,至于WM_CHAR消息是在用户的键盘输入能产生有效的ASCII码时才会发生。这里特别提醒要注意前两个消息与WM_CHAR消息在使用上是有区别的。在前两个消息中,伴随消息传递的是按键的虚拟键码,所以这两个消息可以处理非打印字符,如方向键,功能键等。而伴随WM_CHAR消息的参数是所按的键的ASCII码,ASCII码是可以区分字母的大小写的。而虚拟键码是不能区分大小写的。  
   
 3
、鼠标消息:WM_MOUSEMOVEWM_LBUTTONDOWN   WM_LBUTTONUP   WM_LBUTTONDBCLICKWM_RBUTTONDOWN   WM_RBUTTONUPWM_RBUTTONDBCLICK  
   
   
这组消息是与鼠标输入相关的,WM_MOUSEMOVE消息发生在鼠标移动的时候,剩余的六个消息则分别对应于鼠标左右键的按下、释放、双击事件,要指出的是WINDOWS系统并不是在鼠标每移动一个像素时都产生MOUSEMOVE消息,这一点要特别注意。  
 4
、另一组窗口消息:WM_MOVE   ,   WM_SIZE   ,   WM_PAINT  
    
当窗口移动的时候产生WM_MOVE   消息,窗口的大小改变的时候产生WM_SIZE消息,而当窗口工作区中的内容需要重画的时候就会产生WM_PAINT消息。

 

5.现在我们来看一个实例,此实例是根据windows程序设计改编,其中的程序逻辑进行了重组,现在我们来用代码来剖析消息的调用时机及GDI的基本用法。

目标:能够在一个窗口客户区内画曲线,并且可以用shu标随意更改,当窗口改变或最小化或移动时,均显示正常。具体实现过程如下:

1.我们需要在启动窗口后,窗口客户区内出现曲线。我们前面提到了,在窗口大小发生改变的时候产生WM_SIZE消息(我们可以认为创建窗口时属于窗口大小发生了改变),因此我们在窗口过程中加入如下消息:

static POINT apt[4] ;//定义四个坐标,以方便画曲线

case WM_SIZE:

{

cxClient = LOWORD (lParam);//取得窗口客户区的宽,即x坐标

           cyClient = HIWORD (lParam);// 取得窗口客户区的高,即y坐标

            

            apt[0].x = cxClient / 4 ;

            apt[0].y = cyClient / 2 ;

                

            apt[1].x = cxClient / 2 ;

            apt[1].y = cyClient / 4 ;

       

            apt[2].x = cxClient / 2 ;

            apt[2].y = 3 * cyClient / 4 ;

       

            apt[3].x = 3 * cxClient / 4 ;

            apt[3].y = cyClient / 2 ;

}

 

现在我们需要在屏幕上显示,看上面WM_PAINT消息的调用时机:

当窗口工作区中的内容需要重画的时候就会产生WM_PAINT消息,所以此时我们加入WM_PAINT消息,如下:

case   WM_PAINT:

                     

            hdc = BeginPaint (hwnd, &ps) ;

            DrawBezier (hdc, apt) ;

            EndPaint (hwnd, &ps) ;

其中DrawBezier (hdc, apt) ;表示根据点画曲线,我们定义为一个函数,方便调用,代码如下:

void DrawBezier (HDC hdc, POINT apt[])       

{

       

    PolyBezier (hdc, apt, 4) ;

}

 

如果我们运行这个程序,就会发现不管我们窗口如何改变大小,窗幕上会始终存在一根曲线。我们知道,在autocad软件中,我们可以改变曲线的形状,现在我们来加入此功能。

2.目标:能够用鼠标改变曲线形状,那么我们可以分别加入鼠标左键和右键功能。我们希望当点击鼠标左键或右键时,能够改变曲线形状,于是我们加入  WM_LBUTTONDOWN:WM_RBUTTONDOWN:消息,代码如下:

    case WM_LBUTTONDOWN:

    case WM_RBUTTONDOWN:

if (wParam & MK_LBUTTON || wParam & MK_RBUTTON)

            {

                   hdc = GetDC (hwnd);     

                   if (wParam & MK_LBUTTON)

                   {

                           //改变曲线第一个控制点的坐标

                           apt[1].x = LOWORD (lParam) ;

                           apt[1].y = HIWORD (lParam) ;

                   }

       

            

       

              if (wParam & MK_RBUTTON)

                  {

//改变曲线第二个控制点的坐标

                            apt[2].x = LOWORD (lParam) ;

                            apt[2].y = HIWORD (lParam) ;

                   }

                   //改变坐标后画一次

                   SelectObject (hdc, GetStockObject (BLACK_PEN)) ;

       

                   DrawBezier (hdc, apt) ;

       

                   ReleaseDC (hwnd, hdc) ;

解释:前面我们讲过,取得设备环境句柄的方式有两种:BeginPaint (hwnd, &ps) hdc = GetDC (hwnd);  ,其中BeginPaint只能在WM_PAINT:中使用,且使无效区域变为有效区域,即不会在消息队列中放入WM_PAINT消息;GetDC可以用于其它消息中,且不具备使无效区域变为有效区域的功能。

     wParam大家查MSDN就可以发现,只是表示一些控制键,if (wParam & MK_LBUTTON)这种方式就是表示按的是左键;

     lParam如果在WM_LBUTTONDOWN等消息中,表示鼠标的座标。

现在我们来运行程序,发现目标达到了,但是多了一个问题,当我们点左键后,会发现原来的曲线还存在,这是什么原因呢?是因为我们的画面并没有刷新。现在我们来解决这个问题

(3).利用InvalidateRect (hwnd, NULL, TRUE) ;进行主动刷新,这样就会手动产生一个WM_PAINT消息,我们仅需要在后面加一句

InvalidateRect (hwnd, NULL, TRUE)

好了,现在我们通过以上三步实现了,点左或右键改变形状的目的,但如果我想通过鼠标移动来调整形状呢,此时加入case WM_MOUSEMOVE:即可。其实现过程与WM_LBUTTONDOWN:一样。

问题又来了,但我们改变形状后,将窗口最小化,然后恢复正常时,会发现我们刚才的改动失效了,什么原因呢?

(4).这是因为当我们最小化,然后恢复正常时,此时会收到WM_SIZE:消息,而此消息里面只是放了一些初始值,所以当然就会还原为没有修改过的曲线形状了。解决办法是:

利用静态全局变量控制,第一次窗口启动时执行初始化。再次执行时,主动刷新即可。改动如下:

case WM_SIZE:

 

                   if(flag==0)

                   {

       

            cxClient = LOWORD (lParam);

       

            cyClient = HIWORD (lParam);

               

            apt[0].x = cxClient / 4 ;

            apt[0].y = cyClient / 2 ;

                

            apt[1].x = cxClient / 2 ;

            apt[1].y = cyClient / 4 ;

       

       

       

            apt[2].x = cxClient / 2 ;

       

            apt[2].y = 3 * cyClient / 4 ;

       

       

       

            apt[3].x = 3 * cxClient / 4 ;

       

            apt[3].y = cyClient / 2 ;

 

                            flag++;

                   }

                   else

                   {

                             InvalidateRect (hwnd, NULL, TRUE) ;

 

                   }

        return 0 ;

 

5.通过上面的步骤,其主体已完成,但我们在调节形状时,会发现很不方便,我们希望加入杠杆的调节功能,这儿我们可以用两条直线分别在端点处摸拟,直接修改画曲线的函数即可,相关代码如下:

void DrawBezier (HDC hdc, POINT apt[])

       

{

       

    PolyBezier (hdc, apt, 4) ;

   

        

    MoveToEx (hdc, apt[0].x, apt[0].y, NULL) ;

       

    LineTo   (hdc, apt[1].x, apt[1].y) ;

       

  

       

    MoveToEx (hdc, apt[2].x, apt[2].y, NULL) ;

       

    LineTo   (hdc, apt[3].x, apt[3].y) ;

         }

       

好了,整体代码如下:

 

Code

 

 

 

 

你可能感兴趣的:(sdk)