【home88一必发】Windows程序设计_17_鼠标_1,使用鼠标

by admin on 2019年3月16日

CHECKE奥迪Q52程序包蕴3个键盘接口,内容与CHECKE中华V1完全相同。利用←、→、↑、↓八个方向键能够在2五个矩形之间活动鼠标指针。Home键把鼠标指针移动到左上角的矩形;End键使鼠标指针落到右下角的矩形。空格键和回车键都足以切换X形标记。

C语言Windows次第设计 -> 第八一天 -> 使用鼠标

 

鼠标的运用相同是透过获得Windows鼠标消息来得到用户如今的鼠标状态的。

① 、鼠标的牵线
    鼠标是总结机的输入设备之一, 在图形化的操作系统上,
鼠标的接纳使一些繁杂的操作变得不难, 随着科学技术的向上,
鼠标的品种也越多,
按接口类型可分为串行鼠标、PS/2鼠标、总线鼠标、USB鼠标(多为光电鼠标)八种。按其行事原理及其内部结构的两样足以分成机械式,光机式和光电式。
【home88一必发】Windows程序设计_17_鼠标_1,使用鼠标。    
    那里大家不探讨鼠标的硬件构造,
越来越多关于鼠标的硬件知识请自行查阅相关资料。
    
    1>. 鼠标所在的地方
        在Windows系统下, 用户移动鼠标时,
在显示器上一般会以二个斜式的箭头来表示鼠标当前的岗位,
这些箭头实际上是四个位图格式的小图标, 称为”鼠标指针”,
鼠标指针具有贰个单像素精度的”热点”(hot spot), 当鼠标准样品式为箭头时,
那一个”热点”正是鼠标箭头的极限, 还有一对体制是”十”字样式,
那样的指针”热点”位于”十”字的基本地点,
热点在显示设备上提示了3个精确的岗位。 当我们去捕获鼠标指针的岗位时,
实际上是指鼠标指针的那么些”热点”所在的像素单元的职位。
    
    2>. 鼠标的术语
        ①. 单击 : 按下鼠标按键, 然后放手;
        ②. 双击 : 接二连三快速的按下鼠标同贰个按键然后松手;
        ③. 拖动 : 保持按键按下景况, 并移动鼠标。
        
        未来大家广阔的三键鼠标, 八个按键分布称为左键、中键和右键,
当中左键的标识符简写为LBUTTON, 中键的标识符为MBUTTON,
右键的标识符为RBUTTON。 双键鼠标唯有左键和右键, 单键鼠标只有左键。
        
    3>. 鼠标的体裁
        Windows系统为鼠标提供了三种暗中认可的鼠标准样品式, 如:
箭头、沙漏、十字瞄准等,
在此前学习的经过中实际上大家曾经触发了接纳暗许的鼠标准样品式,
回想那行代码:

        wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ;

        那样就是使用二个私下认可的斜式箭头作为鼠标的指针样式,
斜式箭头样式的标识符为 IDC_ARROW, 这一个标识符定义在 WINUSER.H
头文件中, 其余还有以下标识符及其对应的样式:

home88一必发 1

贰 、使用鼠标的粗略示例
    1>. 示例一: 获取鼠标指针位置
        在这些示例中示范怎么样赢得鼠标的职务,
先说下相关的消息标识符以及函数。
        音信标识符: WM_MOUSEMOVE
当鼠标指针在客户区内移动或鼠标指针经过客户区窗口时会获得这么些音信。
        获取鼠标地方的函数: GetCursorPos 该函数的原型: BOOL
GetCursorPos(LPPOINT lpPoint) ;
        代码片段:

 1     switch(message)   2     {   3     case WM_PAINT:        //处理重绘消息   4         hdc = BeginPaint( hwnd, &ps ) ;   5         wsprintf( szBuffer,  "屏幕坐标:(%i, %i)", pt.x, pt.y );   6         TextOut( hdc, 10, 10, szBuffer, lstrlen(szBuffer) ) ;   7         ScreenToClient( hwnd, &pt ) ;        //将相对于屏幕的坐标转换为相对于窗口客户区的坐标   8         wsprintf( szBuffer,  "客户区坐标:(%i, %i)", pt.x, pt.y );   9         TextOut( hdc, 10, 30, szBuffer, lstrlen(szBuffer) ) ;  10         EndPaint( hwnd, &ps ) ;  11         return 0 ;  12   13     case WM_MOUSEMOVE:        //处理鼠标移动时发来的消息  14         GetCursorPos(&pt) ;  15         InvalidateRect( hwnd, NULL, TRUE ) ;  16         return 0 ;

    完整的言传身教代码:

home88一必发 2home88一必发 3View
Code – GetCursorPosition

 1 #include<windows.h>   2    3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;   4    5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )   6 {   7     static TCHAR szAppName[] = TEXT( "UseMouse_Demo" ) ;   8     HWND hwnd ;   9     MSG msg ;  10     WNDCLASS wndclass ;  11   12     wndclass.hInstance        = hInstance ;  13     wndclass.lpfnWndProc    = WndProc ;  14     wndclass.lpszClassName    = szAppName ;  15     wndclass.style            = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;  16     wndclass.hbrBackground    = (HBRUSH) GetStockObject( WHITE_BRUSH ) ;  17     wndclass.hCursor        = LoadCursor( NULL, IDC_ARROW ) ;  18     wndclass.hIcon            = LoadIcon( NULL, IDI_APPLICATION ) ;  19     wndclass.cbClsExtra        = 0 ;  20     wndclass.cbWndExtra        = 0 ;  21     wndclass.lpszMenuName    = 0 ;  22   23     if( !RegisterClass(&wndclass) )  24     {  25         MessageBox( NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK ) ;  26         return 0 ;  27     }  28   29     hwnd = CreateWindow( szAppName, TEXT("获取鼠标指针位置 - Demo"),  30         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,  31         CW_USEDEFAULT, CW_USEDEFAULT,  32         NULL, NULL, hInstance, NULL ) ;  33   34     ShowWindow( hwnd, iCmdShow ) ;  35     UpdateWindow( hwnd ) ;  36   37     while( GetMessage(&msg, NULL, 0, 0) )  38     {  39         TranslateMessage( &msg ) ;  40         DispatchMessage( &msg ) ;  41     }  42   43     return msg.wParam ;  44 }  45   46 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )  47 {  48     HDC hdc ;  49     PAINTSTRUCT ps ;  50     static POINT pt ;  51     TCHAR szBuffer[128] ;  52   53     switch(message)  54     {  55     case WM_PAINT:  56         hdc = BeginPaint( hwnd, &ps ) ;  57         wsprintf( szBuffer,  "屏幕坐标:(%i, %i)", pt.x, pt.y );  58         TextOut( hdc, 10, 10, szBuffer, lstrlen(szBuffer) ) ;  59         ScreenToClient( hwnd, &pt ) ;  60         wsprintf( szBuffer,  "客户区坐标:(%i, %i)", pt.x, pt.y );  61         TextOut( hdc, 10, 30, szBuffer, lstrlen(szBuffer) ) ;  62         EndPaint( hwnd, &ps ) ;  63         return 0 ;  64   65     case WM_MOUSEMOVE:  66         GetCursorPos(&pt) ;  67         InvalidateRect( hwnd, NULL, TRUE ) ;  68         return 0 ;  69   70     case WM_DESTROY:  71         PostQuitMessage(0) ;  72         return 0 ;  73     }  74   75     return DefWindowProc( hwnd, message, wParam, lParam ) ;  76 }

        说一下一体化的思路, 要即时跟踪获取鼠标在显示屏中的坐标,
首先要捕获鼠标的运动新闻 WM_MOUSEMOVE,
当Windows向我们发来这几个音信时就代码鼠标在进行移动,
随后大家对那些鼠标移动新闻实行拍卖, 调用 GetCursorPos(&pt) ;
这些函数获取鼠标未来的职位,
获取到鼠标地点后为了能够即时在窗口中展现出来, 再调用 InvalidateRect(
hwnd, NULL, TRUE ) ;
使整个客户区变成无效状态, 从而引发 WM_PAINT
必要重绘客户区内容的消息,
在处理重绘新闻时输出刚刚获得的鼠标指针坐标地方。
        
        关于 ScreenToClient( hwnd, &pt ) ; :
          
 这么些函数的成效是将荧屏坐标(相对于全体显示屏左上角的坐标)转换来相对于窗口客户区的坐标,
荧屏坐标与窗口客户区坐标的含义如图所示:

home88一必发 4

            GetCursorPos获得的鼠标地点是显示器坐标,
即便想通晓他在窗口客户区内的相对地方就须求调用ScreenToClient函数将其转化为客户区坐标。
          别的还有1个WIndows函数是将窗口客户区坐标转成显示器坐标的,
函数为: ClientToScreen( hwnd, &pt ) ;
          获取鼠标指针的职位还有其余的点子, 那里只是在那之中的一种。
    
    
    2>. 示例二: 处理鼠标左键单击事件
        鼠标左键在客户区被单击时发来的音讯: 【home88一必发】Windows程序设计_17_鼠标_1,使用鼠标。WM_LBUTTONDOWN

 1     switch(message)   2     {   3     case WM_PAINT:   4         hdc = BeginPaint( hwnd, &ps ) ;   5         EndPaint( hwnd, &ps ) ;   6         return 0 ;   7    8     case WM_LBUTTONDOWN:    //处理鼠标左键单击被按下时产生的消息   9         x = LOWORD( lParam ) ;    //获取鼠标位置x坐标信息  10         y = HIWORD( lParam ) ;    //获取鼠标位置y坐标信息  11         wsprintf( szBuffer,  "鼠标左键被单击, 击中位置: (%i, %i)", x, y );  12         MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;  13         return 0 ;  14   15     case WM_DESTROY:  16         PostQuitMessage(0) ;  17         return 0 ;  18     }

    完整的以身作则代码:

home88一必发 5home88一必发 6View
Code – WM_LBUTTONDOWN

 1 #include<windows.h>   2    3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;   4    5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )   6 {   7     static TCHAR szAppName[] = TEXT( "UseMouse_Demo" ) ;   8     HWND hwnd ;   9     MSG msg ;  10     WNDCLASS wndclass ;  11   12     wndclass.hInstance        = hInstance ;  13     wndclass.lpfnWndProc    = WndProc ;  14     wndclass.lpszClassName    = szAppName ;  15     wndclass.style            = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;  16     wndclass.hbrBackground    = (HBRUSH) GetStockObject( WHITE_BRUSH ) ;  17     wndclass.hCursor        = LoadCursor( NULL, IDC_ARROW ) ;  18     wndclass.hIcon            = LoadIcon( NULL, IDI_APPLICATION ) ;  19     wndclass.cbClsExtra        = 0 ;  20     wndclass.cbWndExtra        = 0 ;  21     wndclass.lpszMenuName    = 0 ;  22   23     if( !RegisterClass(&wndclass) )  24     {  25         MessageBox( NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK ) ;  26         return 0 ;  27     }  28   29     hwnd = CreateWindow( szAppName, TEXT("处理鼠标单击事件 - Demo"),  30         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,  31         CW_USEDEFAULT, CW_USEDEFAULT,  32         NULL, NULL, hInstance, NULL ) ;  33   34     ShowWindow( hwnd, iCmdShow ) ;  35     UpdateWindow( hwnd ) ;  36   37     while( GetMessage(&msg, NULL, 0, 0) )  38     {  39         TranslateMessage( &msg ) ;  40         DispatchMessage( &msg ) ;  41     }  42   43     return msg.wParam ;  44 }  45   46 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )  47 {  48     HDC hdc ;  49     PAINTSTRUCT ps ;  50     static POINT pt ;  51     TCHAR szBuffer[128] ;  52     static int x, y ;  53   54     switch(message)  55     {  56     case WM_PAINT:  57         hdc = BeginPaint( hwnd, &ps ) ;  58         EndPaint( hwnd, &ps ) ;  59         return 0 ;  60   61     case WM_LBUTTONDOWN:  62         x = LOWORD( lParam ) ;  63         y = HIWORD( lParam ) ;  64         wsprintf( szBuffer,  "鼠标左键被单击, 击中位置: (%i, %i)", x, y );  65         MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;  66         return 0 ;  67   68     case WM_DESTROY:  69         PostQuitMessage(0) ;  70         return 0 ;  71     }  72   73     return DefWindowProc( hwnd, message, wParam, lParam ) ;  74 }

    这么些示例演示的是当鼠标在客户区按下时弹出3个对话框,
对话框的始末是鼠标被按下时鼠标指针的职分音讯, 能够看出, 那里大家并未使用
GetCursorPos 函数来赢得鼠标指针的职位, 而是通过

        x = LOWORD( lParam ) ;    //获取鼠标位置x坐标信息          y = HIWORD( lParam ) ;    //获取鼠标位置y坐标信息

    来收获的, 参数lParam饱含了鼠标指针的岗位消息,
个中没有字节表示x坐标, 高位字节表示y坐标,
利用LOWORDHIWORD宏能够得到那一个坐标值,
那里收获的坐标指的是顶牛于窗口客户区的坐标。

 

叁 、客户区鼠标音信
    与键盘信息分歧, 在键盘音讯中,
Windows只把键盘音讯发送到当前有所输入主题的窗口,
而鼠标消息无论窗口是还是不是得到关节, 只要鼠标经过客户区,
恐怕在客户区内被单击窗口进程都会接收鼠标音信,
被点击(包含双击/单击/拖动)的窗口将改为活动窗口。与客户区消息相对应的称为非客户区新闻,
非客户区音信是指鼠标指针在窗口内并在在客户区外的活动或单击/双击等,
非客户区包罗窗口的标题栏、菜单栏、滚动条、窗口的边框,
这个将在末端实行商讨, 那里先说客户区鼠标消息。
    
    1>. 鼠标单击
        鼠标在客户区单击时各类鼠标按键所发出的新闻如下:

鼠标按键 按下时产生的消息 释放时产生的消息
左键 WM_LBUTTONDOWN WM_LBUTTONUP
中键 WM_MBUTTONDOWN WM_MBUTTONUP
右键 WM_RBUTTONDOWN WM_RBUTTONUP

        
        

 

 

    示例第22中学一度演示了2个拍卖鼠标左键单击的以身作则,
对于中键和右键处理的章程是如出一辙的,
只要等待Windows发来音讯然后处理那几个音讯就行了。
        
    2>. wParam参数中的内容
        参数wParam中的值表示了鼠标按钮、Shift键和Ctrl键的事态。
将wParam与”鼠标键”标识符举行按位与(&)运算能够获取鼠标按键与鼠标键的意况,
在此从前缀MK_为始发的标识符称为”鼠标键”, 有如下鼠标键:

            #define MK_LBUTTON          0x0001            //按下左键              #define MK_RBUTTON          0x0002            //按下右键
            #define MK_MBUTTON          0x0010            //按下中键              #define MK_SHIFT            0x0004            //按下Shift键              #define MK_CONTROL          0x0008            //按下Ctrl键  

        例如, 当接收到 WM_LBUTTONDOWN 消息时, 若

            wParam & MK_SHIFT 

        的值为TRUE(非零), 则意味按下左键的还要也按下了Shift键。
        例如:

        case WM_LBUTTONDOWN:              if( wParam & MK_CONTROL )              {                  MessageBox( hwnd, TEXT("Ctrl键与鼠标左键同时被按下!"), TEXT("鼠标动作"), MB_OK ) ;                  return 0 ;              }              return 0 ;

       
唯有当鼠标左键与键盘的Ctrl键同时被按下时我们弹出个对话框表明”Ctrl键与鼠标左键同时被按下!”,
不然什么也不做。
        
        3>. 鼠标双击
            双击对四遍击中的地点以及时光间隔都有早晚须求,
唯有当一次飞跃的单击在大体地方上靠的很近并且时间间隔相当的短的情事下才算双击。
            
            假诺想让窗口进程接受鼠标双击音信,
供给在登记窗口类(RegisterClass)时,
早先化wndclass中的style成员的质量中再添加CS_DBLCLKS标识符:

                wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;

            即便在窗口类的style成员中没有包蕴 CS_DBLCLKS 标识符,
那么尽管当用户双击时不会发生双击信息, 而是发生一串如下的新闻:

                WM_LBUTTONDOWN                  WM_LBUTTONUP                  WM_LBUTTONDOWN                  WM_LBUTTONUP

            由于用户在接连一次按下鼠标左键时需求自然时间,
尽管那么些时辰比较短暂, 不过在这一个历程中等射程序依然有恐怕接收任何消息的,
比如用户在便捷的四回单击中手的微微抖动就会在在那之中插入二个WM_MOUSEMOVE的消息,
那里暂且忽略在那之中插入的新闻, 如果音信正是连接的那么些。
            
            当窗口类的style成员只中富含CS_DBLCLKS标识符后,
用户再度双击就会发出那样的一串音讯:

                WM_LBUTTONDOWN                  WM_LBUTTONUP                  WM_LBUTTONDBLCLK                  WM_LBUTTONUP

            能够观望, 在出席 CS_DBLCLKS 标识符后, 第多少个消息
WM_LBUTTONDOWN 只是被回顾的替换来了 WM_LBUTTONDBLCLK 消息。
home88一必发,            
            鼠标种种按键双击时第多个消息所对应替换的新闻如下:

            #define WM_LBUTTONDBLCLK                0x0203            //左键              #define WM_MBUTTONDBLCLK                0x0209            //中键              #define WM_RBUTTONDBLCLK                0x0206            //右键

 

四 、非客户区鼠标音讯
    非客户区音信差不离与客户区新闻完全对应,
只是在标识符中多了3个”NC“字符(noclient),
当鼠标指针在窗口的非客户区移动时(比如标题栏), 窗口进程就会收到到
WM_NCMOUSEMOVE 音信, 在客户区外鼠标按下发出的音信如下:

鼠标按键 按下 释放 第二次按下(双击)
左键 WM_NCLBUTTONDWON WM_NCLBUTTONUP WM_NCLBUTTONDBLCLK
中键 WM_NCMBUTTONDOWN WM_NCMBUTTONUP WM_NCMBUTTONDBLCLK
右键 WM_NCRBUTTONDOWN WM_NCRBUTTONUP WM_NCRBUTTONDBLCLK

 

 

 

  此外与客户区音信差别的是, 那里的 wParam
参数中的值与客户区中的含义有所不一样, 那里的 wParam
表示非客户区鼠标移动或单击的职位, 他的值被设定成一些以 HT
开头的标识符中, 表示 “命中测试“(Hit Test),
关于击中测试与以HT开始的标识符将在底下讲到。
    
    参数 lParam 中的值如故是鼠标指针的职位消息,
但此时的音讯正好与客户区中的 lParam 的坐标新闻相反, 客户区中的 lParam
的值是相对于窗口客户区的坐标, 而那里的(非客户区) lParam
中所包罗的坐标音信是荧屏坐标, 在地点已经涉及过,
使用ScreenToClientClientToScreen可以兑现荧屏坐标与客户区坐标之间的转向。
    
    处理非客户区左键单击示例:

    switch(message)      {      case WM_PAINT:          hdc = BeginPaint( hwnd, &ps ) ;          EndPaint( hwnd, &ps ) ;          return 0 ;        case WM_NCLBUTTONDOWN:        //处理非客户区鼠标左键单击事件          MessageBox( hwnd, TEXT("非客户区鼠标左键被单击"), TEXT("鼠标动作"), MB_OK ) ;          return 0 ;        case WM_DESTROY:          PostQuitMessage(0) ;          return 0 ;      }

 

伍 、关于”击中测试”新闻 WM_NCHITTEST
    WM_NCHITTEST意味着”非客户区击中测试”,
所谓的命中测试便是测试鼠标当前所在的岗位,
这么些音讯的事先级高于其它兼具的客户区和非客户区新闻, 参数 lParam
中包含相对于显示器坐标的x值与y值, wParam 参数另有用途。
    
    一般的话, WM_NCHITTEST 消息是交给 DefWindowProc
暗中同意的新闻处理函数举行拍卖的, 对于客户区中, Windows会利用 WM_NCHITTEST
音讯来发出负有和其他鼠标位置相关的鼠标音讯。对于非客户区音信以来,
DefWindowProc 处理 WM_NCHITTEST 新闻后赶回一个 wParam 值,
那个值可以是随机一个非客户区鼠标新闻的 wParam 参数的值, 这么些 wParam
值用来判定鼠标的随地的职分。
    
    举例来说, 若是 DefWindowProc 函数在拍卖 WM_NCHITTEST 消息后重回3个
HTCLIENT , One plusLIENT 表示鼠标在客户区,
那时Windows会将显示屏坐标转换到客户区坐标,并发生三个相关的客户区的鼠标新闻;
    当再次回到值为 HTCAPTION 表示鼠标此时在3个标题栏中,
所以Windows会将那儿鼠标的坐标地点转成荧屏坐标并发送有关的非客户区音信。
    
    这一个重回的标识符定义在WINUSER.H头文件中, 相关的概念如下:

#define HTERROR             (-2)                    //在屏幕的后面或在窗体之间的线上(使函数DefWindowProc产生一个警示音)  #define HTTRANSPARENT       (-1)                    //在一个被其它窗口覆盖的窗口中  #define HTNOWHERE           0                        //在屏幕背景或窗口之间的分界线  #define HTCLIENT            1                        //在客户区中  #define HTCAPTION           2                        //在标题栏中  #define HTSYSMENU           3                        //在一个窗口菜单栏或子窗口的关闭按钮上  #define HTGROWBOX           4                        //在尺寸框中  #define HTSIZE              HTGROWBOX                //同HTGROWBOX  #define HTMENU              5                        //在菜单区域  #define HTHSCROLL           6                        //在水平滚动条上  #define HTVSCROLL           7                        //在垂直滚动条上  #define HTMINBUTTON         8                        //在最小化按钮上  #define HTMAXBUTTON         9                        //在最大化按钮上  #define HTLEFT              10                        //在窗口的左边框上  #define HTRIGHT             11                        //在窗口的右边框上  #define HTTOP               12                        //在窗口水平边框的上方  #define HTTOPLEFT           13                        //在窗口边框的左上角  #define HTTOPRIGHT          14                        //在窗口边框的右上角  #define HTBOTTOM            15                        //在窗口的水平边框的底部  #define HTBOTTOMLEFT        16                        //在窗口边框的左下角  #define HTBOTTOMRIGHT       17                        //在窗口边框的右下角  #define HTBORDER            18                        //在不具有可变大小边框的窗口的边框上  #define HTREDUCE            HTMINBUTTON                //同HTMINBUTTON  #define HTZOOM              HTMAXBUTTON                //同HTMAXBUTTON  #define HTSIZEFIRST         HTLEFT                    //同HTLEFT  #define HTSIZELAST          HTBOTTOMRIGHT             //同HTBOTTOMRIGHT  #define HTOBJECT            19                        //忽略该标识符, 已废弃  #define HTCLOSE             20                        //在关闭按钮上  #define HTHELP              21                        //在帮助按钮上

    那样在取得非客户区音信时大家就能够根据 wParam
中的值判断鼠标在窗口的职责了, 像那样:

    case WM_NCLBUTTONDOWN:        //处理非客户区的鼠标左键单击事件          x = LOWORD( lParam ) ;    //通过lParam获取鼠标位置          y = HIWORD( lParam ) ;          switch(wParam)            //通过wParam判断鼠标在窗口的位置          {          case HTCAPTION:        //在标题上              wsprintf( szBuffer,  "鼠标左键在标题栏中被单击, 击中位置: (%i, %i)", x, y ) ;              MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;              break ;          case HTMINBUTTON:    //在最小化按钮上              wsprintf( szBuffer,  "鼠标左键在最小化按钮上被单击, 击中位置: (%i, %i)", x, y ) ;              MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;              break ;          case HTMAXBUTTON:    //在最大化按钮上              wsprintf( szBuffer,  "鼠标左键在最大化按钮上被单击, 击中位置: (%i, %i)", x, y ) ;              MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;              break ;          }          return 0 ;

    首先捕获 鼠标左键在非客户区的单击事件, 然后再通过 wParam
判断鼠标在窗口的地点, 这里得到鼠标地点是透过 LOWO索罗德D 和 HIWO牧马人D 宏完毕的,
还有四个职能雷同的宏也能够用来收获lParam中的鼠标音讯, 他们是
GET_X_LPARAM 宏和 GET_Y_LPARAM, 然而这七个宏是定义在
WINDOWSX.H 头文件中的, 若是要运用那五个宏须要将 WINDOWSX.H
包罗进来。
    举例:

    xPos = GET_X_LPARAM(lParam) ;      yPos = GET_Y_LPARAM(lParam) ;

 

 


wid, 2012.11.30

 

上一篇: C语言Windows程序设计 -> 第捌天 -> 响应键盘事件

 


C语言Windows程序设计 -> 第柒一天 -> 使用鼠标

 

鼠标的接纳同样是因而得到Windows鼠标音信来获得用户眼下的鼠标状态的。

壹 、鼠标的介绍
    鼠标是电脑的输入设备之一, 在图形化的操作系统上,
鼠标的施用使有个别错综复杂的操作变得简单, 随着科学技术的前进,
鼠标的品种也尤为多,
按接口类型可分为串行鼠标、PS/2鼠标、总线鼠标、USB鼠标(多为光电鼠标)各个。按其工作原理及其内部结构的两样足以分为机械式,光机式和光电式。
    
    那里咱们不探究鼠标的硬件构造,
更加多关于鼠标的硬件知识请自行查阅有关材料。
    
    1>. 鼠标所在的岗位
        在Windows系统下, 用户移动鼠标时,
在荧屏上相似会以三个斜式的箭头来表示鼠标当前的义务,
那么些箭头实际上是三个位图格式的小图标, 称为”鼠标指针”,
鼠标指针具有二个单像素精度的”热点”(hot spot), 当鼠标准样品式为箭头时,
那么些”热点”便是鼠标箭头的巅峰, 还有一对样式是”十”字样式,
那样的指针”热点”位于”十”字的基本岗位,
热点在体现设备上提醒了1个纯粹的岗位。 当大家去捕获鼠标指针的职分时,
实际上是指鼠标指针的这些”热点”所在的像素单元的职位。
    
    2>. 鼠标的术语
        ①. 单击 : 按下鼠标按键, 然后放手;
        ②. 双击 : 一连急忙的按下鼠标同二个按键然后放开;
        ③. 拖动 : 保持按键按下情状, 并移动鼠标。
        
        今后大家广阔的三键鼠标, 四个按键分布称为左键、中键和右键,
个中左键的标识符简写为LBUTTON, 中键的标识符为MBUTTON,
右键的标识符为RBUTTON。 双键鼠标唯有左键和右键, 单键鼠标唯有左键。
        
    3>. 鼠标的体制
        Windows系统为鼠标提供了三种暗中同意的鼠标准样品式, 如:
箭头、沙漏、十字瞄准等,
在原先学习的进度中实际上大家曾经触发了应用默许的鼠标准样品式,
回想这行代码:

        wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ;

       
那样正是行使二个默许的斜式箭头作为鼠标的指针样式, 斜式箭头样式的标识符为
IDC_ARROW, 那个标识符定义在
WINUSER.H 头文件中,
其余还有以下标识符及其相应的样式:

home88一必发 7

② 、使用鼠标的简单示例
    1>. 示例一: 获取鼠标指针地点
        在这些示例中示范怎样取得鼠标的职位,
先说下有关的音信标识符以及函数。
        消息标识符: WM_MOUSEMOVE
当鼠标指针在客户区内移动或鼠标指针经过客户区窗口时会得到这一个音讯。
        获取鼠标地点的函数: GetCursorPos 该函数的原型: BOOL
GetCursorPos(LPPOINT lpPoint) ;
        代码片段:

home88一必发 8

 1     switch(message)
 2     {
 3     case WM_PAINT:        //处理重绘消息
 4         hdc = BeginPaint( hwnd, &ps ) ;
 5         wsprintf( szBuffer,  "屏幕坐标:(%i, %i)", pt.x, pt.y );
 6         TextOut( hdc, 10, 10, szBuffer, lstrlen(szBuffer) ) ;
 7         ScreenToClient( hwnd, &pt ) ;        //将相对于屏幕的坐标转换为相对于窗口客户区的坐标
 8         wsprintf( szBuffer,  "客户区坐标:(%i, %i)", pt.x, pt.y );
 9         TextOut( hdc, 10, 30, szBuffer, lstrlen(szBuffer) ) ;
10         EndPaint( hwnd, &ps ) ;
11         return 0 ;
12 
13     case WM_MOUSEMOVE:        //处理鼠标移动时发来的消息
14         GetCursorPos(&pt) ;
15         InvalidateRect( hwnd, NULL, TRUE ) ;
16         return 0 ;

home88一必发 9

   
完整的演示代码:

home88一必发 10View Code – GetCursorPosition

home88一必发 11

 1 #include<windows.h>
 2 
 3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;
 4 
 5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
 6 {
 7     static TCHAR szAppName[] = TEXT( "UseMouse_Demo" ) ;
 8     HWND hwnd ;
 9     MSG msg ;
10     WNDCLASS wndclass ;
11 
12     wndclass.hInstance        = hInstance ;
13     wndclass.lpfnWndProc    = WndProc ;
14     wndclass.lpszClassName    = szAppName ;
15     wndclass.style            = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;
16     wndclass.hbrBackground    = (HBRUSH) GetStockObject( WHITE_BRUSH ) ;
17     wndclass.hCursor        = LoadCursor( NULL, IDC_ARROW ) ;
18     wndclass.hIcon            = LoadIcon( NULL, IDI_APPLICATION ) ;
19     wndclass.cbClsExtra        = 0 ;
20     wndclass.cbWndExtra        = 0 ;
21     wndclass.lpszMenuName    = 0 ;
22 
23     if( !RegisterClass(&wndclass) )
24     {
25         MessageBox( NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK ) ;
26         return 0 ;
27     }
28 
29     hwnd = CreateWindow( szAppName, TEXT("获取鼠标指针位置 - Demo"),
30         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
31         CW_USEDEFAULT, CW_USEDEFAULT,
32         NULL, NULL, hInstance, NULL ) ;
33 
34     ShowWindow( hwnd, iCmdShow ) ;
35     UpdateWindow( hwnd ) ;
36 
37     while( GetMessage(&msg, NULL, 0, 0) )
38     {
39         TranslateMessage( &msg ) ;
40         DispatchMessage( &msg ) ;
41     }
42 
43     return msg.wParam ;
44 }
45 
46 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
47 {
48     HDC hdc ;
49     PAINTSTRUCT ps ;
50     static POINT pt ;
51     TCHAR szBuffer[128] ;
52 
53     switch(message)
54     {
55     case WM_PAINT:
56         hdc = BeginPaint( hwnd, &ps ) ;
57         wsprintf( szBuffer,  "屏幕坐标:(%i, %i)", pt.x, pt.y );
58         TextOut( hdc, 10, 10, szBuffer, lstrlen(szBuffer) ) ;
59         ScreenToClient( hwnd, &pt ) ;
60         wsprintf( szBuffer,  "客户区坐标:(%i, %i)", pt.x, pt.y );
61         TextOut( hdc, 10, 30, szBuffer, lstrlen(szBuffer) ) ;
62         EndPaint( hwnd, &ps ) ;
63         return 0 ;
64 
65     case WM_MOUSEMOVE:
66         GetCursorPos(&pt) ;
67         InvalidateRect( hwnd, NULL, TRUE ) ;
68         return 0 ;
69 
70     case WM_DESTROY:
71         PostQuitMessage(0) ;
72         return 0 ;
73     }
74 
75     return DefWindowProc( hwnd, message, wParam, lParam ) ;
76 }

home88一必发 12

       
说一下完好无缺的思路, 要即时跟踪获取鼠标在显示屏中的坐标,
首先要捕获鼠标的运动音信 WM_MOUSEMOVE,
当Windows向大家发来以此音信时就代码鼠标在进行移动,
随后大家对那个鼠标移动消息举行处理, 调用 GetCursorPos(&pt) ;
这一个函数获取鼠标未来的地点,
获取到鼠标地点后为了能够即时在窗口中显示出来, 再调用 InvalidateRect( hwnd, NULL, TRUE ) ;
使任何客户区变成无效状态, 从而引发 WM_PAINT 须求重绘客户区内容的音信,
在拍卖重绘新闻时输出刚刚得到的鼠标指针坐标地点。
        
        关于 ScreenToClient( hwnd, &pt )
;
:
          
 那个函数的效率是将显示器坐标(相对于漫天荧屏左上角的坐标)转换来绝对于窗口客户区的坐标,
显示屏坐标与窗口客户区坐标的意思如图所示:

home88一必发 13

            GetCursorPos获得的鼠标地方是显示器坐标,
如若想掌握她在窗口客户区内的相对地点就必要调用ScreenToClient函数将其转会为客户区坐标。
          别的还有一个WIndows函数是将窗口客户区坐标转成显示屏坐标的,
函数为: ClientToScreen( hwnd, &pt ) ;

          获取鼠标指针的职位还有其余的法子, 那里只是当中的一种。
    
    
    2>. 示例二: 处理鼠标左键单击事件
        鼠标左键在客户区被单击时发来的新闻: WM_LBUTTONDOWN

home88一必发 14

 1     switch(message)
 2     {
 3     case WM_PAINT:
 4         hdc = BeginPaint( hwnd, &ps ) ;
 5         EndPaint( hwnd, &ps ) ;
 6         return 0 ;
 7 
 8     case WM_LBUTTONDOWN:    //处理鼠标左键单击被按下时产生的消息
 9         x = LOWORD( lParam ) ;    //获取鼠标位置x坐标信息
10         y = HIWORD( lParam ) ;    //获取鼠标位置y坐标信息
11         wsprintf( szBuffer,  "鼠标左键被单击, 击中位置: (%i, %i)", x, y );
12         MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;
13         return 0 ;
14 
15     case WM_DESTROY:
16         PostQuitMessage(0) ;
17         return 0 ;
18     }

home88一必发 15

   
完整的以身作则代码:

home88一必发 16View Code – WM_LBUTTONDOWN

home88一必发 17

 1 #include<windows.h>
 2 
 3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;
 4 
 5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
 6 {
 7     static TCHAR szAppName[] = TEXT( "UseMouse_Demo" ) ;
 8     HWND hwnd ;
 9     MSG msg ;
10     WNDCLASS wndclass ;
11 
12     wndclass.hInstance        = hInstance ;
13     wndclass.lpfnWndProc    = WndProc ;
14     wndclass.lpszClassName    = szAppName ;
15     wndclass.style            = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;
16     wndclass.hbrBackground    = (HBRUSH) GetStockObject( WHITE_BRUSH ) ;
17     wndclass.hCursor        = LoadCursor( NULL, IDC_ARROW ) ;
18     wndclass.hIcon            = LoadIcon( NULL, IDI_APPLICATION ) ;
19     wndclass.cbClsExtra        = 0 ;
20     wndclass.cbWndExtra        = 0 ;
21     wndclass.lpszMenuName    = 0 ;
22 
23     if( !RegisterClass(&wndclass) )
24     {
25         MessageBox( NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK ) ;
26         return 0 ;
27     }
28 
29     hwnd = CreateWindow( szAppName, TEXT("处理鼠标单击事件 - Demo"),
30         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
31         CW_USEDEFAULT, CW_USEDEFAULT,
32         NULL, NULL, hInstance, NULL ) ;
33 
34     ShowWindow( hwnd, iCmdShow ) ;
35     UpdateWindow( hwnd ) ;
36 
37     while( GetMessage(&msg, NULL, 0, 0) )
38     {
39         TranslateMessage( &msg ) ;
40         DispatchMessage( &msg ) ;
41     }
42 
43     return msg.wParam ;
44 }
45 
46 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
47 {
48     HDC hdc ;
49     PAINTSTRUCT ps ;
50     static POINT pt ;
51     TCHAR szBuffer[128] ;
52     static int x, y ;
53 
54     switch(message)
55     {
56     case WM_PAINT:
57         hdc = BeginPaint( hwnd, &ps ) ;
58         EndPaint( hwnd, &ps ) ;
59         return 0 ;
60 
61     case WM_LBUTTONDOWN:
62         x = LOWORD( lParam ) ;
63         y = HIWORD( lParam ) ;
64         wsprintf( szBuffer,  "鼠标左键被单击, 击中位置: (%i, %i)", x, y );
65         MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;
66         return 0 ;
67 
68     case WM_DESTROY:
69         PostQuitMessage(0) ;
70         return 0 ;
71     }
72 
73     return DefWindowProc( hwnd, message, wParam, lParam ) ;
74 }

home88一必发 18

   
那些示例演示的是当鼠标在客户区按下时弹出3个会话框,
对话框的剧情是鼠标被按下时鼠标指针的义务新闻, 能够见到, 那里我们从不运用
GetCursorPos 函数来拿到鼠标指针的地方, 而是通过

        x = LOWORD( lParam ) ;    //获取鼠标位置x坐标信息
        y = HIWORD( lParam ) ;    //获取鼠标位置y坐标信息

   
来博取的, 参数lParam含有了鼠标指针的岗位音信,
当中没有字节表示x坐标, 高位字节表示y坐标, 利用LOWORDHIWORD宏可以收获这几个坐标值,
那里收获的坐标指的是争论于窗口客户区的坐标。

 

叁 、客户区鼠标信息
    与键盘音讯不相同, 在键盘音讯中,
Windows只把键盘音信发送到当前持有输入大旨的窗口,
而鼠标音信无论窗口是不是获得关节, 只要鼠标经过客户区,
也许在客户区内被单击窗口进度都会吸收鼠标新闻,
被点击(包括双击/单击/拖动)的窗口将改为活动窗口。与客户区新闻相对应的名为非客户区音讯,
非客户区消息是指鼠标指针在窗口内并在在客户区外的运动或单击/双击等,
非客户区包罗窗口的标题栏、菜单栏、滚动条、窗口的边框,
那么些将在背后举办研究, 那里先说客户区鼠标音信。
    
    1>. 鼠标单击
        鼠标在客户区单击时种种鼠标按键所发生的音信如下:

鼠标按键 按下时产生的消息 释放时产生的消息
左键 WM_LBUTTONDOWN WM_LBUTTONUP
中键 WM_MBUTTONDOWN WM_MBUTTONUP
右键 WM_RBUTTONDOWN WM_RBUTTONUP

        
        

 

 

    示例第22中学早就演示了三个处理鼠标左键单击的以身作则,
对于中键和右键处理的点子是同等的,
只要等待Windows发来新闻然后处理那些新闻就行了。
        
    2>. wParam参数中的内容
        参数wParam中的值表示了鼠标按钮、Shift键和Ctrl键的意况。
将wParam与”鼠标键”标识符实行按位与(&)运算能够得到鼠标按键与鼠标键的处境,
以前缀MK_为始发的标识符称为”鼠标键”,
有如下鼠标键:

            #define MK_LBUTTON          0x0001            //按下左键
            #define MK_RBUTTON          0x0002            //按下右键
            #define MK_MBUTTON          0x0010            //按下中键
            #define MK_SHIFT            0x0004            //按下Shift键
            #define MK_CONTROL          0x0008            //按下Ctrl键

       
例如, 当接收到 WM_LBUTTONDOWN 消息时, 若

            wParam & MK_SHIFT 

       
的值为TRUE(非零), 则意味按下左键的同时也按下了Shift键。
        例如:

home88一必发 19

        case WM_LBUTTONDOWN:
            if( wParam & MK_CONTROL )
            {
                MessageBox( hwnd, TEXT("Ctrl键与鼠标左键同时被按下!"), TEXT("鼠标动作"), MB_OK ) ;
                return 0 ;
            }
            return 0 ;

home88一必发 20

       
唯有当鼠标左键与键盘的Ctrl键同时被按下时大家弹出个对话框表明”Ctrl键与鼠标左键同时被按下!”,
不然什么也不做。
        
        3>. 鼠标双击
            双击对两回击中的位置以及时光间隔都有必然必要,
唯有当五次飞速的单击在情理地点上靠的很近并且时间间隔非常的短的情事下才算双击。
            
            借使想让窗口进程接受鼠标双击音信,
须求在登记窗口类(RegisterClass)时, 初步化wndclass中的style成员的性质中再增进CS_DBLCLKS标识符:

                wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;

      
     如若在窗口类的style成员中从未包蕴 CS_DBLCLKS 标识符,
那么固然当用户双击时不会发出双击新闻, 而是发生一串如下的音信:

                WM_LBUTTONDOWN
                WM_LBUTTONUP
                WM_LBUTTONDOWN
                WM_LBUTTONUP

      
     由于用户在接连两遍按下鼠标左键时索要肯定时间, 固然那个日子比较短暂,
然而在那个进程中等射程序依旧有大概收取任何新闻的,
比如用户在高效的四次单击中手的微微抖动就会在里面插入一个WM_MOUSEMOVE的新闻,
那里暂且忽略个中插入的消息, 假诺音信正是连连的那些。
            
            当窗口类的style成员只中隐含CS_DBLCLKS标识符后,
用户再度双击就会发生这么的一串新闻:

                WM_LBUTTONDOWN
                WM_LBUTTONUP
                WM_LBUTTONDBLCLK
                WM_LBUTTONUP

      
     可以见见, 在进入 CS_DBLCLKS 标识符后, 第⑤个音信
WM_LBUTTONDOWN
只是被总结的替换来了 WM_LBUTTONDBLCLK 消息。
            
            鼠标各个按键双击时第多个音信所对应替换的新闻如下:

            #define WM_LBUTTONDBLCLK                0x0203            //左键
            #define WM_MBUTTONDBLCLK                0x0209            //中键
            #define WM_RBUTTONDBLCLK                0x0206            //右键

 

四 、非客户区鼠标音信
    非客户区新闻大概与客户区音讯完全对应, 只是在标识符中多了一个”NC“字符(noclient),
当鼠标指针在窗口的非客户区移动时(比如标题栏), 窗口进程就会收到到 WM_NCMOUSEMOVE 新闻,
在客户区外鼠标按下发生的音信如下:

鼠标按键 按下 释放 第二次按下(双击)
左键 WM_NCLBUTTONDWON WM_NCLBUTTONUP WM_NCLBUTTONDBLCLK
中键 WM_NCMBUTTONDOWN WM_NCMBUTTONUP WM_NCMBUTTONDBLCLK
右键 WM_NCRBUTTONDOWN WM_NCRBUTTONUP WM_NCRBUTTONDBLCLK

 

 

 

  其它与客户区音信分裂的是,
那里的 wParam
参数中的值与客户区中的含义有所分裂, 那里的 wParam
表示非客户区鼠标移动或单击的岗位, 他的值被设定成一些以 HT 初始的标识符中, 表示
命中测试“(Hit Test), 关于击中测试与以HT开始的标识符将在上边讲到。
    
    参数 lParam 中的值照旧是鼠标指针的职位音信,
但此时的新闻正好与客户区中的 lParam 的坐标信息相反, 客户区中的 lParam
的值是周旋于窗口客户区的坐标, 而这里的(非客户区) lParam
中所包括的坐标音信是显示器坐标, 在上边已经关系过, 使用ScreenToClientClientToScreen能够兑现荧屏坐标与客户区坐标之间的转会。
    
    处理非客户区左键单击示例:

home88一必发 21

    switch(message)
    {
    case WM_PAINT:
        hdc = BeginPaint( hwnd, &ps ) ;
        EndPaint( hwnd, &ps ) ;
        return 0 ;

    case WM_NCLBUTTONDOWN:        //处理非客户区鼠标左键单击事件
        MessageBox( hwnd, TEXT("非客户区鼠标左键被单击"), TEXT("鼠标动作"), MB_OK ) ;
        return 0 ;

    case WM_DESTROY:
        PostQuitMessage(0) ;
        return 0 ;
    }

home88一必发 22

 

⑤ 、关于”击中测试”音信WM_NCHITTEST
    WM_NCHITTEST表示”非客户区击中测试”,
所谓的命中测试便是测试鼠标当前所在的职分,
那几个新闻的预先级高于别的全数的客户区和非客户区新闻, 参数 lParam
中带有相对于显示器坐标的x值与y值, wParam 参数另有用途。
    
    一般的话, WM_NCHITTEST 音信是付诸 DefWindowProc
私下认可的新闻处理函数进行处理的, 对于客户区中, Windows会利用 WM_NCHITTEST
音讯来爆发负有和别的鼠标地点相关的鼠标音信。对于非客户区消息以来,
DefWindowProc 处理 WM_NCHITTEST 新闻后回到3个 wParam 值,
那么些值能够是不管三七二十一三个非客户区鼠标音信的 wParam 参数的值, 这几个 wParam
值用来判定鼠标的处处的职位。
    
    举例来说, 借使 DefWindowProc 函数在处理 WM_NCHITTEST 音信后归来1个
HTCLIENT , 中兴LIENT
代表鼠标在客户区,
这时Windows会将荧屏坐标转换到客户区坐标,并产生二个有关的客户区的鼠标音讯;
    当再次来到值为 HTCAPTION
表示鼠标此时在一个标题栏中,
所以Windows会将此时鼠标的坐标地方转成显示屏坐标并发送有关的非客户区音讯。
    
    这个再次回到的标识符定义在WINUSER.H头文件中, 相关的定义如下:

home88一必发 23

#define HTERROR             (-2)                    //在屏幕的后面或在窗体之间的线上(使函数DefWindowProc产生一个警示音)
#define HTTRANSPARENT       (-1)                    //在一个被其它窗口覆盖的窗口中
#define HTNOWHERE           0                        //在屏幕背景或窗口之间的分界线
#define HTCLIENT            1                        //在客户区中
#define HTCAPTION           2                        //在标题栏中
#define HTSYSMENU           3                        //在一个窗口菜单栏或子窗口的关闭按钮上
#define HTGROWBOX           4                        //在尺寸框中
#define HTSIZE              HTGROWBOX                //同HTGROWBOX
#define HTMENU              5                        //在菜单区域
#define HTHSCROLL           6                        //在水平滚动条上
#define HTVSCROLL           7                        //在垂直滚动条上
#define HTMINBUTTON         8                        //在最小化按钮上
#define HTMAXBUTTON         9                        //在最大化按钮上
#define HTLEFT              10                        //在窗口的左边框上
#define HTRIGHT             11                        //在窗口的右边框上
#define HTTOP               12                        //在窗口水平边框的上方
#define HTTOPLEFT           13                        //在窗口边框的左上角
#define HTTOPRIGHT          14                        //在窗口边框的右上角
#define HTBOTTOM            15                        //在窗口的水平边框的底部
#define HTBOTTOMLEFT        16                        //在窗口边框的左下角
#define HTBOTTOMRIGHT       17                        //在窗口边框的右下角
#define HTBORDER            18                        //在不具有可变大小边框的窗口的边框上
#define HTREDUCE            HTMINBUTTON                //同HTMINBUTTON
#define HTZOOM              HTMAXBUTTON                //同HTMAXBUTTON
#define HTSIZEFIRST         HTLEFT                    //同HTLEFT
#define HTSIZELAST          HTBOTTOMRIGHT             //同HTBOTTOMRIGHT
#define HTOBJECT            19                        //忽略该标识符, 已废弃
#define HTCLOSE             20                        //在关闭按钮上
#define HTHELP              21                        //在帮助按钮上

home88一必发 24

   
那样在获得非客户区新闻时大家就足以依照 wParam
中的值判断鼠标在窗口的职位了, 像那样:

home88一必发 25

    case WM_NCLBUTTONDOWN:        //处理非客户区的鼠标左键单击事件
        x = LOWORD( lParam ) ;    //通过lParam获取鼠标位置
        y = HIWORD( lParam ) ;
        switch(wParam)            //通过wParam判断鼠标在窗口的位置
        {
        case HTCAPTION:        //在标题上
            wsprintf( szBuffer,  "鼠标左键在标题栏中被单击, 击中位置: (%i, %i)", x, y ) ;
            MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;
            break ;
        case HTMINBUTTON:    //在最小化按钮上
            wsprintf( szBuffer,  "鼠标左键在最小化按钮上被单击, 击中位置: (%i, %i)", x, y ) ;
            MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;
            break ;
        case HTMAXBUTTON:    //在最大化按钮上
            wsprintf( szBuffer,  "鼠标左键在最大化按钮上被单击, 击中位置: (%i, %i)", x, y ) ;
            MessageBox( hwnd, szBuffer, TEXT("鼠标动作"), MB_OK ) ;
            break ;
        }
        return 0 ;

home88一必发 26

   
首先捕获 鼠标左键在非客户区的单击事件, 然后再经过 wParam
判断鼠标在窗口的岗位, 那里收获鼠标地方是透过 LOWOPRADOD 和 HIWO哈弗D 宏完毕的,
还有多个效益雷同的宏也能够用来获得lParam中的鼠标音信, 他们是 GET_X_LPARAM 宏和 GET_Y_LPARAM, 不过那五个宏是定义在
WINDOWSX.H 头文件中的,
假若要动用那四个宏必要将 WINDOWSX.H 包括进来。
    举例:

    xPos = GET_X_LPARAM(lParam) ;
    yPos = GET_Y_LPARAM(lParam) ;

 

 


 

转自:

  老长期不曾更新Windows程序设计的始最后,前日看了一段Windows程序设计文本操作的情节,同时想起鼠标操作还没有革新过

home88一必发 27home88一必发 28

文字,这里先就写一些关于鼠标文字吗。

  1 /*---------------------------------------------
  2 CHECKER2.C -- Mouse Hit-Test Demo Program No.2
  3               (c) Charles Petzold, 1998
  4 ---------------------------------------------*/
  5 
  6 #include <Windows.h>
  7 
  8 #define DIVISIONS 5
  9 
 10 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 11 
 12 int WINAPI WinMain( __in HINSTANCE hInstance
 13                     , __in_opt HINSTANCE hPrevInstance
 14                     , __in LPSTR lpCmdLine
 15                     , __in int nShowCmd )
 16 {
 17     static TCHAR szAppName[] = TEXT("Checker2");
 18     HWND hwnd;
 19     MSG msg;
 20     WNDCLASS wndclass;
 21 
 22     wndclass.style = CS_HREDRAW | CS_VREDRAW;
 23     wndclass.lpfnWndProc = WndProc;
 24     wndclass.cbClsExtra = 0;
 25     wndclass.cbWndExtra = 0;
 26     wndclass.hInstance = hInstance;
 27     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 28     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 29     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 30     wndclass.lpszMenuName = NULL;
 31     wndclass.lpszClassName = szAppName;
 32 
 33     if (!RegisterClass(&wndclass))
 34     {
 35         MessageBox(NULL, TEXT("Program requires Windows NT!")
 36             , szAppName, MB_ICONERROR);
 37         return 0;
 38     }
 39 
 40     hwnd = CreateWindow(szAppName, TEXT("Checker2 Mouse Hit-Test Demo")
 41         , WS_OVERLAPPEDWINDOW
 42         , CW_USEDEFAULT, CW_USEDEFAULT
 43         , CW_USEDEFAULT, CW_USEDEFAULT
 44         , NULL, NULL, hInstance, NULL);
 45 
 46     ShowWindow(hwnd, nShowCmd);
 47     UpdateWindow(hwnd);
 48 
 49     while (GetMessage(&msg, NULL, 0, 0))
 50     {
 51         TranslateMessage(&msg);
 52         DispatchMessage(&msg);
 53     }
 54 
 55     return msg.wParam;
 56 }
 57 
 58 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 59 {
 60     static BOOL fState[DIVISIONS][DIVISIONS];
 61     static int cxBlock, cyBlock;
 62     HDC hdc;
 63     int x, y;
 64     PAINTSTRUCT ps;
 65     POINT point;
 66     RECT rect;
 67 
 68     switch (message)
 69     {
 70     case WM_SIZE:
 71         cxBlock = LOWORD(lParam) / DIVISIONS;
 72         cyBlock = HIWORD(lParam) / DIVISIONS;
 73         return 0;
 74 
 75     case WM_SETFOCUS:
 76         ShowCursor(TRUE);
 77         return 0;
 78 
 79     case WM_KILLFOCUS:
 80         ShowCursor(FALSE);
 81         return 0;
 82 
 83     case WM_KEYDOWN:
 84         GetCursorPos(&point);
 85         ScreenToClient(hwnd, &point);
 86 
 87         x = max(0, min(DIVISIONS - 1, point.x / cxBlock));
 88         y = max(0, min(DIVISIONS - 1, point.y / cyBlock));
 89 
 90         switch (wParam)
 91         {
 92         case VK_UP:
 93             --y;
 94             break;
 95 
 96         case VK_DOWN:
 97             ++y;
 98             break;
 99 
100         case VK_LEFT:
101             --x;
102             break;
103 
104         case VK_RIGHT:
105             ++x;
106             break;
107 
108         case VK_HOME:
109             x = y = 0;
110             break;
111 
112         case VK_END:
113             x = y = DIVISIONS - 1;
114             break;
115 
116         case VK_RETURN:
117         case VK_SPACE:
118             SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(x * cxBlock, y * cyBlock));
119             break;
120         }
121 
122         x = (x + DIVISIONS) % DIVISIONS;
123         y = (y + DIVISIONS) % DIVISIONS;
124 
125         point.x = x * cxBlock + cxBlock / 2;
126         point.y = y * cyBlock + cyBlock / 2;
127 
128         ClientToScreen(hwnd, &point);
129         SetCursorPos(point.x, point.y);
130         return 0;
131 
132     case WM_LBUTTONDOWN:
133         x = LOWORD(lParam) / cxBlock;
134         y = HIWORD(lParam) / cyBlock;
135 
136         if (x < DIVISIONS && y < DIVISIONS)
137         {
138             fState[x][y] ^= 1;
139 
140             rect.left = x * cxBlock;
141             rect.top = y * cyBlock;
142             rect.right = (x + 1) * cxBlock;
143             rect.bottom = (y + 1) * cyBlock;
144 
145             InvalidateRect(hwnd, &rect, FALSE);
146         }
147         else
148             MessageBeep(0);
149         return 0;
150 
151     case WM_PAINT:
152         hdc = BeginPaint(hwnd, &ps);
153 
154         for (x = 0; x < DIVISIONS; ++x)
155             for (y = 0; y < DIVISIONS; ++y)
156             {
157                 Rectangle(hdc, x * cxBlock, y * cyBlock
158                     , (x + 1) * cxBlock, (y + 1) * cyBlock);
159                 
160                 if (fState[x][y])
161                 {
162                     MoveToEx(hdc, x * cxBlock, y * cyBlock, NULL);
163                     LineTo(hdc, (x + 1) * cxBlock, (y + 1) * cyBlock);
164                     MoveToEx(hdc, x * cxBlock, (y + 1) * cyBlock, NULL);
165                     LineTo(hdc, (x + 1) * cxBlock, y * cyBlock);
166                 }
167             }
168 
169         EndPaint(hwnd, &ps);
170         return 0;
171 
172     case WM_DESTROY:
173         PostQuitMessage(0);
174         return 0;
175     }
176 
177     return DefWindowProc(hwnd, message, wParam, lParam);
178 }

一、鼠标

CHECKER2.C

(1)

在CHECKER2程序中,处理WM_KEYDOWN时选拔GetCursorPos判断指针的职责,并使用ScreenToClient将显示器坐标转换来客户区坐标,然后将坐标值除以矩形块的宽和高,获得x和y。那几个x和y的值表示了矩形在5*5数组中的地点。当按下有些键时,鼠标指针恐怕在客户区也大概不在客户区内,由此x和y必须带有在min和max的宏处理中,保障它们的限量处于0和4时期。

  Windows协助单键、双键和三键鼠标,仍是能够运用操纵杆可能光笔模拟鼠标。

对此方向键,CHECKELacrosse2程序相应的增多或减少x和y的值。若按下回车键或空格键,CHECKE劲客2程序调用SendMessage给自身发送叁个WM_LBUTTONDOWN消息。最后,WM_KEYDOWN处理逻辑总结获得针对性矩形中央的客户区坐标,并调用ClientToScreen将其转换来显示屏坐标,最终调用SetCursorPos设置指针的职分。

  一 、判断系统是不是留存鼠标

  在运用鼠标在此以前必须认清系统中是或不是留存鼠标,可经过函数GetSystemMetrics来判断鼠标是还是不是存在。

      bMouse=GetSystemMetrics(SM_MOUSEPRESENT);

  若安装了鼠标,则bMouse将回来TRUE,否则就回来0.

  要点:

    Windows9第88中学不管是或不是安装鼠标,这一个函数都将回来TRUE。

  二 、判断鼠标的键数

  通过GetSystemMetrics函数还足以确定安装的鼠标的键鼠,只要将传递给函数是参数改为:SM_CMOUSEBUTTONS
即可判断

系统中鼠标的键;假如没有设置鼠标,那么函数将再次来到0;在Windows9第88中学,无论有没有安装鼠标,这几个函数都将回来2.

  叁 、判断鼠标是或不是切换过左右手

  通过向GetSystemMetrics函数字传送递SM_SWAPBUTTON是不是开始展览了那种切换;

(2) windows预定义

  当Windows用户移动鼠标时,在显示屏上校有贰个“鼠标光标”的小位图,随着用户的位移而移动。鼠标光标上有叁个对准显示器上标准

任务的单象素“热点”。

  Windows协助预约义的鼠标光标:IDC_AHavalROW(箭头光标)、IDC_CROSS(十字光标)、IDC_WAIT(光标);箭头光标的热点在

箭头的顶端,十字光标的走俏在大旨。

  Windows扶助用户自定义鼠标光标;在概念窗口类的时候,大家能够钦点预订义的鼠标光标为窗口暗中同意鼠标。

  通过下边的话语内定窗口私下认可光标:

          wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);

  鼠标操作术语:

    单击:按下并放手3个鼠标键

    双击:火速按下并松手鼠标键两回

    拖曳:按住鼠标键并活动鼠标

  鼠标键名:

    左键:LBUTTON

    中键:MBUTTON

    右键:RBUTTON

  双键鼠标唯有左键与右键,单键鼠标只有3个左键。

(3)客户区鼠标音讯

  鼠标在用户窗口移动、按键窗口都会吸收接纳音讯;那一点与键盘音讯不等同:只有拥有输入核心的窗口才能吸收接纳键盘消息。

  Windows一共定义了21种鼠标信息,个中10种是客户去音信,而有1第11中学是非客户去音讯,用户程序常常忽略非客户区新闻。当鼠标从窗口移动

时,窗口会收到到WM_MOUSEMOVE音信;当用户在窗口客户去按下鼠标键的时候会经受到如下鼠标新闻:

home88一必发 29

  只有三键鼠标才会吸收到中键鼠标音信,唯有双键、三键鼠标才能接到到右键音讯;仅当定义的窗口类能接过DBLCLK双击音讯随后,窗口

才能采纳到那个音讯。

  对于窗口接收到的鼠标音讯,lParam参数的值均隐含鼠标的地方:低位字为x坐标,高位字为y坐标,lParam参数为三十五人,字定义为十五位;

当要处理鼠标音讯时能够经过:

        x=LOWORD(lParam);

        y=HIWORD(lParam);

  获取当前鼠标新闻的紧俏坐标,x、y的值均是争辩于窗口客户区左上角顶点而言;那与平日接纳的坐标有点不均等。

  Windows定义wParam参数提醒鼠标键及Shift和Ctrl键的景观,在先后中,能够选用WINUSELX570.H定义的位旗标来测试wParam参数。

home88一必发 30

  MK代表鼠标音信,能够动用上面包车型客车讲话测试按键的情事:

        if(wParam&MK_SHIFT)

          statement;

    假设接受到WM_LBUTTONDOWN音讯的时候,上边的statement语句能进行,则象征按下左键的时候还要按下了shift键。

  Windows不可能在鼠标移动进度中,为热点经过的每种像素都发送WM_MOUSEMOVE新闻,窗口接收到音讯的速度取决于鼠标移动的快慢

以及窗口过程处理移动音信的速度;即Windows不可能用未处理的WM_MOUSEMOVE音信来填充音信队列,(为了证实那些性情,能够选拔spy

次第来监督鼠标音信和窗口处理音讯的景色)

  窗口处理鼠标音讯的进度如下:

    非活动窗口——>按下鼠标——》窗口变成活动窗口——》发送鼠标音信到窗口进度,

    活动窗口——》按下鼠标——》发送鼠标音信到窗口进程

  上述进度是一般的经过,然而这一个历程有时候会成为任何样子,当有模态窗口存在时,这一个进度恐怕变得不平等,须要

注意。

  窗口接收到的音讯也不必然是按下左键/按下右键为接到到第三个鼠标音信;例如大家在第①个窗口按下鼠标左键,而

后将鼠标移动到首个窗口释放;那么第二个窗口接收到鼠标音讯将会率先WM_MOUSEMOVE,然后是WM_LBUTTONUP消息,

故而在处理的时候须要专注。

  例外情形:

home88一必发 31

 

(4)处理shift和Ctrl键与鼠标组合音信

  前面说过,能够透过wParam参数和位旗标的与运算来查看是不是按下了鼠标和shift/ctrl那样的构成消息。

  在鼠标信息中能够透过下边包车型地铁语句来分明是还是不是按下了shift和Ctrl键

    if(wParam & MK_SHIFT)

    {

        if(wParam & MK_CONTROL)

          {鼠标音信的同时按下了shift和ctrl键}

        else

          {鼠标键的还要按下shift键}

    }

    else

    {

        if(wParam & MK_CONTROL)

          {鼠标音讯的还要按下了ctrl键}

        else

          {仅有鼠标音信}

    }

  在用户程序中得以经过行使鼠标和键盘的构成消息来效仿鼠标音讯,那样在单键鼠标的景观下就足以兑现右键鼠标音信的处理。具体

大家就绝不实例代码表明了。

  Windows使用函数GetKeyState通过虚拟键码VK_LBUTTON、VK_RBUTTON、VK_MBUTTON、VK_SHIFT、VK_CONTROL来返回

鼠标键与shift键的意况。假如GetKeySate重临负值,则表明按下了鼠标键可能shift键。因为GetKeyState再次来到当前正值处理的鼠标键或许shift

键的景观,所以一切景观消息都与相应的新闻时协同的。

(5)双击鼠标键

  双击鼠标键是指在长时间内单击三遍。

  双击条件:  
壹 、三遍单击产生时鼠标光标热点的距离必须在系统明确的方面之内

        ② 、三次单击发生的光阴世隔在内定的光阴限制内;能够在系统控制面板中改变鼠标双击时间距离。

  若是要使得窗口能接受双击鼠标音讯,那么窗口的品格必须有CS_DBLCLKS位旗标。如下所示:

    wndclass.style=CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

  假若没有安装窗口作风,那么双击时窗口将接受上边一八种的新闻:

      home88一必发 32

  假诺设置了窗口作风将收到到:

  home88一必发 33

  如上所示,那八个新闻队列,仅是第一个音讯发生了变化。

  就算双击中的第三个单击操作达成有些单击作用,则处理双击音信很简单,那么第三回单击消息则用来成功第二回单击以外的政工;

这里如:

  Windows财富管理器的鼠标的单击选定,而双击打开;
当然能够在系统中安插针对性选定而单击打开,不过那不符合多数人的操

作习惯。

(6)非客户区鼠标新闻

  假如鼠标在窗口之内,但是在客户区之外,那么窗口将会吸收系统一发布送的非客户区鼠标新闻;窗口的非客户区包涵标题栏、菜单栏和

窗口滚动条,一般情状下,用户程序不供给处理非窗口鼠标音讯,而将那些消息由DefWindowProc函数处理。

  Windows用“NC”
表示非客户区新闻,如果鼠标在窗口的非客户区中移动,那么窗口进度接受到WM_NCMOUSEMOVE消息。非客

户区音信如下所示:

home88一必发 34

  因为非客户区有多少个区域,因此大家在拍卖非客户区音讯的时候,还需求分辨是在标题栏、菜单栏依旧滚动条的鼠标信息。

  为了贯彻分辨区域:能够重视wParam、lParam参数来甄别。客户区的鼠标音信和非客户区鼠标音信的wParam、lParam参数是区别的,wParam

参数指明发送鼠标新闻的非客户区地点。当有非客户区鼠标音讯时,wParam设定为一HT伊始的宏标识符(HT表示命中测试)

  当有非客户区鼠标新闻时,lParam参数重临鼠标新闻产生时热点的坐标,然则此时的坐标为荧屏坐标,而不是客户区坐标。显示器坐标以显示屏的左上角顶

点为坐标原点(0,0);当鼠标往右移动时x扩大,鼠标往下移动时y扩展。

  能够运用Windows函数在显示屏坐标和客户区坐标实行转换:

      ScreenToClient(hwnd,&pt);

      ClientToScreen(hwnd,&pt);

home88一必发 35

  

(8)命中测试音讯

  在Windows中设置了命中测试音讯WM_NCHITTEST,此音讯优先于具有其余的客户区和非客户区鼠标音讯,lParam参数含有鼠标地方的

x和y显示器坐标,wParam参数没有用。

  Windows应用程序平日把那个音讯传递给DefWindowProc,然后Windows用WM_NCHITTEST音信发出基于鼠标地点的任何鼠标音信;

对此非客户区鼠标新闻,当处理WM_NCHITTEST音信时,从DefWindowProc重回的值域将成为鼠标音信中的wParam参数,这一个值可以是任

意非客户区鼠标音讯的wParam值再添加以下内容:

  HTCLIENT:  客户区

  HTNOWHERE:不在窗口中

  HTTRANSPARENT:窗口由另三个窗口覆盖

  HTE冠道RO帕杰罗:使DefWindowProc发生蜂鸣声

  如果DefWindowProc在处理WM_NCHITTEST音信后回到HUAWEILIENT,那么Windows将把显示器坐标转换为客户区坐标,并爆发客户区鼠标新闻。

因为WM_NCHITTEST音讯在拥有的鼠标音讯前处理,因而我们能够运用那一点来剥夺全体的鼠标音信:

  在窗口进度函数中大家能够如此做:

    case  WM_NCHITTEST:

      return (LRESULT)WM_NCHITTEST;

   那样就能够禁止使用全数的窗口鼠标音信。

 

  利用命中测试音信,Windows程序设计了三个消息发出新闻的机制,大家得以选用窗口的系统菜单的鼠标双击来分析那一个历程:

  

    鼠标双击窗口系统菜单——》WM_NCHITTEST消息——》DefWindowProc处理WM_NCHITTEST消息,后返回——》DefWindowProc将

WM_NCLBUTTONDBLCLK音讯放到音信队列,并把wParam设置为HTSYSMENU——》通常用户程序不会处理WM_NCLBUTTONDBLCLK

新闻,而把音讯交给DefwindowProc函数处理——》DefwindowProc接收到wParam=HTSYSMENU的WM_NCLBUTTONDBLCLK

消息后;将WM_SYSCOMMAND音讯放入音讯队列中,并安装wParam=SC_CLOSE参数——》窗口进度把这些WM_SYSCOMMAND消息

传给DefWindowProc函数处理——》DefwindowProc通过给窗口发送WM_CLOSE消息。

  要是用户程序不处理WM_CLOSE新闻,那么DefWindowProc将拍卖那些音信;DefwindowProc函数接收到WM_CLOSE消息后,将调用

DestroyWindow函数来拍卖WM_CLOSE,除了其余处理,DestroyWindow还给窗口进度发送WM_DESTROY新闻,窗口进程一般用下列

代码来拍卖WM_DESTROY消息:

    case WM_DESTROY:

        PostQiutMessage(0);

        retrun 0;

  PostQiutMessage使Windows把WM_QUIT音讯放入音讯队列中,这一个音信永远不会路过窗口进度处理,因为WM_DESTROY消息将

是GetMessage函数重临0,终止新闻循环,从而退出程序。

  

(9)捕获鼠标

  有时候,大概在鼠标离开窗口后还想博得鼠标新闻,那时候大家就需求捕获鼠标。

  例如:  

    在画画程序中,必要画矩形,大家按下鼠表左键,然后拖曳鼠标到符合大小,接下去释放鼠标,就足以绘出矩形;可是只要在拖曳的长河

中鼠标离开了窗口的客户区,那么原来的窗口就无法经受鼠标释放的消息,那时没有艺术明确矩形的分寸,就不知道哪些绘制矩形了,

  为了处理地点的题材,能够通过鼠标捕获功用来促成。

  Windows提供了函数捕获鼠标,如下所示:

    SetCapture(hwnd);

  在调用那几个函数后,Windows会将随后的装有鼠标音信发送给窗口句柄为hwnd的窗口过程,并且鼠标新闻都将是客户区新闻,尽管鼠标在非客户区;

lParam参数将指令鼠标在坐标中的地点。但是LOWO本田CR-VD(lParam)和HIOKugaD(lParam)再次回到的值或然为正也为负,那与鼠标发送消息的岗位有关。

  当要释放鼠标时,调用

    ReleaseCapture(hwnd);

  就能够使消息苏醒到捕获前的意况。

  我们领略在窗口1按下鼠标键,并且窗口1鼠标被抓获,然后移动到窗口2,接下去释放鼠标键,那么窗口1将无法接过到鼠标释放新闻,而窗口2将吸收

鼠标音讯,那时将不是捕获鼠标的十一分窗口接收鼠标音讯,而是由光标上面包车型客车窗口来选择鼠标新闻;

  为了防范捕获发生的老大状态,只有当鼠标键在窗口的客户区中被按下时才捕获鼠标,当键被放出时才假释鼠标捕获。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图