• windows 编程 —— 消息与参数(滚动条、键盘、鼠标)


    目录:

    • 滚动条
    • 键盘
    • 鼠标

    滚动条ScrollBar

    发送消息:WM_VSCROLL和WM_HSCROLL

    参数wParam:wParam消息参数被分为一个低字组和一个高字组。wParam的低字组是一个数值,它指出了鼠标对滚动条进行的操作。这个数值被看作一个「通知码」。通知码的值由以SB(代表「scroll bar(滚动条)」)开头的标识符定义。wParam的高字组存当前滚动条的位置信息。

    参数lParam: 对于来自作为窗口的一部分而建立的滚动条消息,您可以忽略lParam;它只用于作为子窗口而建立的滚动条(通常在对话框内)。

    也带有wParam和lParam消息参数。

    以下是在WINUSER.H中定义的通知码:

    #define SB_LINEUP       0
    #define SB_LINELEFT           0
    #define SB_LINEDOWN           1
    #define SB_LINERIGHT          1
    #define SB_PAGEUP         2
    #define SB_PAGELEFT           2
    #define SB_PAGEDOWN           3
    #define SB_PAGERIGHT          3
    #define SB_THUMBPOSITION   4
    #define SB_THUMBTRACK         5
    #define SB_TOP                6
    #define SB_LEFT           6
    #define SB_BOTTOM        7
    #define SB_RIGHT          7
    #define SB_ENDSCROLL          8

    使用示范代码

    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
    { //......
    hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"),
                       WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,
                       NULL, NULL, hInstance, NULL) ; //......
    }
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
    {
        switch (message)   {
       case WM_CREATE:
        // Initialization
        SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE) ;
        SetScrollPos   (hwnd, SB_VERT, iVscrollPos, TRUE) ;
                return 0 ;
        case WM_VSCROLL:
            
           switch (LOWORD (wParam)) {
            case SB_LINEUP:iVscrollPos++;break; 
    case SB_PAGEUP:break;
    case SB_THUMBPOSITION:break;
    //...... } //...... } }

    键盘

    发送消息:【按键消息】和【字符消息】

    【按键消息】:

     

    键按下

    键释放

    非系统键

    WM_KEYDOWN

    WM_KEYUP

    系统键

    WM_SYSKEYDOWN

    WM_SYSKEYUP

    (系统键:WM_SYSKEYDOWN和WM_SYSKEYUP消息经常由与Alt相组合的按键产生,这些按键启动程序菜单或者系统菜单上的选项,或者用于切换活动窗口等系统功能(Alt-Tab或者Alt-Esc),也可以用作系统菜单快捷键(Alt键与一个功能键相结合,例如Alt-F4用于关闭应用程序)。)

    【字符消息】:TranslateMessage把字符消息放入消息队列中。此字符消息将是GetMessage从消息队列中得到的按键消息之后的下一个消息。

     

    字符

    死字符

    非系统字符

    WM_CHAR

    WM_DEADCHAR

    系统字符

    WM_SYSCHAR

    WM_SYSDEADCHAR

    参数wParam:对所有四类按键消息,wParam是虚拟键代码,表示按下或释放的键,而lParam则包含属于按键的其它数据。

                       对于字符消息,wParam是字符的ASCII编码等,lParam 同上

    参数lParam:如下图:

                              

    重复计数

    重复计数是该消息所表示的按键次数,大多数情况下,重复计数设定为1。不过,如果按下一个键之后,您的窗口消息处理程序不够快,以致不能处理自动重复速率(您可以在「控制台」的「键盘」中进行设定)下的按键消息,Windows就把几个WM_KEYDOWN或者WM_SYSKEYDOWN消息组合到单个消息中,并相应地增加重复计数。WM_KEYUP或WM_SYSKEYUP消息的重复计数总是为1。

    因为重复计数大于1指示按键速率大于您程序的处理能力,所以您也可能想在处理键盘消息时忽略重复计数。几乎每个人都有文书处理或执行电子表格时画面卷过头的经验,因为多余的按键堆满了键盘缓冲区,所以当程序用一些时间来处理每一次按键时,如果忽略您程序中的重复计数,就能够解决此问题。不过,有时可能也会用到重复计数,您应该尝试使用两种方法执行程序,并从中找出一种较好的方法。

    OEM扫描码

    OEM扫描码是由硬件(键盘)产生的代码。这对中古时代的汇编程序写作者来说应该很熟悉,它是从PC相容机种的ROM BIOS服务中所获得的值(OEM指的是PC的原始设备制造商(Original Equipment Manufacturer)及其与「IBM标准」同步的内容)。在此我们不需要更多的信息。除非需要依赖实际键盘布局的样貌,不然Windows程序可以忽略掉几乎所有的OEM扫描码信息,参见第二十二章的程序KBMIDI

    扩充键旗标

    如果按键结果来自IBM增强键盘的附加键之一,那么扩充键旗标为1(IBM增强型键盘有101或102个键。功能键在键盘顶端,光标移动键从数字键盘中分离出来,但在数字键盘上还保留有光标移动键的功能)。对键盘右端的Alt和Ctrl键,以及不是数字键盘那部分的光标移动键(包括Insert和Delete键)、数字键盘上的斜线(/)和Enter键以及Num Lock键等,此旗标均被设定为1。Windows程序通常忽略扩充键旗标。

    内容代码

    右按键时,假如同时压下ALT键,那么内容代码为1。对WM_SYSKEYUP与WM_SYSKEYDOWN而言,此位总视为1;而对WM_SYSKEYUP与WM_KEYDOW消息而言,此位为0。除了两个之外:

    • 如果活动窗口最小化了,则它没有输入焦点。这时候所有的按键都会产生WM_SYSKEYUP和WM_SYSKEYDOWN消息。如果Alt键未被按下,则内容代码字段被设定为0。Windows使用WM_SYSKEYUP和WM_SYSKEYDOWN消息,从而使最小化了的活动窗口不处理这些按键。
       
    • 对于一些外国语文(非英文)键盘,有些字符是通过Shift、Ctrl或者Alt键与其它键相组合而产生的。这时内容代码为1,但是此消息并非系统按键消息。
       

    键的先前状态

    如果在此之前键是释放的,则键的先前状态为0,否则为1。对WM_KEYUP或者WM_SYSKEYUP消息,它总是设定为1;但是对WM_KEYDOWN或者WM_SYSKEYDOWN消息,此位可以为0,也可以为1。如果为1,则表示该键是自动重复功能所产生的第二个或者后续消息。

    转换状态

    如果键正被按下,则转换状态为0;如果键正被释放,则转换状态为1。对WM_KEYDOWN或者WM_SYSKEYDOWN消息,此字段为0;对WM_KEYUP或者WM_SYSKEYUP消息,此字段为1。

    位移状态

    在处理按键消息时,您可能需要知道是否按下了位移键(Shift、Ctrl和Alt)或开关键(Caps Lock、Num Lock和Scroll Lock)。通过呼叫GetKeyState函数,您就能获得此信息。例如:

    iState = GetKeyState (VK_SHIFT) ;
    //如果按下了Shift,则iState值为负(即设定了最高位置位)。如果Caps Lock键打开,则从
    iState = GetKeyState (VK_CAPITAL) ;
    //传回的值低位被设为1。此位与键盘上的小灯保持一致。
    
    /*
    您在使用GetKeyState时,会带有虚拟键码VK_SHIFT、VK_CONTROL和VK_MENU(在说明Alt键时呼叫)。使用GetKeyState时,您也可以用下面的标识符来确定按下的Shift、Ctrl或Alt键是左边的还是右边的:VK_LSHIFT、VK_RSHIFT、VK_LCONTROL、VK_RCONTROL、VK_LMENU、VK_RMENU。这些标识符只用于GetKeyState和GetAsyncKeyState
    
    使用虚拟键码VK_LBUTTON、VK_RBUTTON和VK_MBUTTON,您也可以获得鼠标键的状态。
    
    注意GetKeyState的使用,它并非实时检查键盘状态,而只是检查直到目前为止正在处理的消息的键盘状态。多数情况下,这正符合您的要求。如果您需要确定使用者是否按下了Shift-Tab,请在处理Tab键的WM_KEYDOWN消息时呼叫GetKeyState,带有参数VK_SHIFT。如果GetKeyState传回的值为负,那么您就知道在按下Tab键之前按下了Shift键。并且,如果在您开始处理Tab键之前,已经释放了Shift键也没有关系。您知道,在按下Tab键的时候Shift键是按下的。
    
    */

    使用示范代码

    //对于 IParam 的读取代码 (片段)
    //......
     TCHAR * szFormat[2] = {
    TEXT ("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"),
     TEXT ("%-13s  0x%04X%1s%c %6u %4d %3s %3s %4s %4s") } ;
    //......
                TextOut (hdc, 0, (cyClient / cyChar - 1 - i)*cyChar,szBuffer,
                wsprintf (szBuffer, szFormat [iType],
                szMessage [pmsg[i].message - WM_KEYFIRST],
                pmsg[i].wParam,
                (PTSTR) (iType ? TEXT (" ") : szKeyName),
                (TCHAR) (iType ? pmsg[i].wParam : ' '),
                LOWORD (pmsg[i].lParam),
                HIWORD (pmsg[i].lParam) & 0xFF,
                               0x01000000 & pmsg[i].lParam ? szYes  : szNo,
                               0x20000000 & pmsg[i].lParam ? szYes  : szNo,
                               0x40000000 & pmsg[i].lParam ? szDown : szUp,
                               0x80000000 & pmsg[i].lParam ? szUp:szDown));
    //......

    鼠标:

    发送消息:鼠标定义了21种消息,不过,其中有11个消息和显示区域无关(下面称之为「非显示区域」消息),Windows程序经常忽略这些消息。

    【显示区域相关】:WM_MOUSEMOVE + 

    按下

    释放

    按下(双键)

    WM_LBUTTONDOWN

    WM_LBUTTONUP

    WM_LBUTTONDBLCLK

    WM_MBUTTONDOWN

    WM_MBUTTONUP

    WM_MBUTTONDBLCLK

    WM_RBUTTONDOWN

    WM_RBUTTONUP

    WM_RBUTTONDBLCLK

    【显示区域无关】:WM_NCMOUSEMOVE +

    按下

    释放

    按下(双击)

    WM_NCLBUTTONDOWN

    WM_NCLBUTTONUP

    WM_NCLBUTTONDBLCLK

    WM_NCMBUTTONDOWN

    WM_NCMBUTTONUP

    WM_NCMBUTTONDBLCLK

    WM_NCRBUTTONDOWN

    WM_NCRBUTTONUP

    WM_NCRBUTTONDBLCLK

    + WM_NCHITTEST

    参数wParam:【显示区域】wParam的值指示鼠标按键以及Shift和Ctrl键的状态

           【非显示区域】wParam参数指明移动或者按鼠标按键的非显示区域。它设定为WINUSER.H中定义的以HT开头的标识符之一(HT表示「命中测试」)。

    参数lParam:   【显示区域】Client鼠标的位置:低字组为x坐标,高字组为y坐标
           【非显示区域】屏幕鼠标的位置:低字组为x坐标,高字组为y坐标

    使用小妙招(1):

    wParam的值指示鼠标按键以及Shift和Ctrl键的状态。您可以使用表头文件WINUSER.H中定义的位屏蔽来测试wParam。MK前缀代表「鼠标按键」。

    MK_LBUTTON

    按下左键

    MK_MBUTTON

    按下中键

    MK_RBUTTON

    按下右键

    MK_SHIFT

    按下Shift键

    MK_CONTROL

    按下Ctrl键

    wparam & MK_SHIFT 是TRUE(非0),您就知道当左键按下时也按下了Shift键。
    妙招实例:
     //实现 左键 拖动时 画点
    case   WM_MOUSEMOVE:
                if (wParam & MK_LBUTTON && iCount < 1000)
            
                {
                               pt[iCount  ].x = LOWORD (lParam) ;
                               pt[iCount++].y = HIWORD (lParam) ;
           
                               hdc = GetDC (hwnd) ;
                               SetPixel(hdc,LOWORD(lParam),HIWORD(lParam), 0) ;
                               ReleaseDC (hwnd, hdc) ;
                }
                return 0 ;
    使用小妙招(2):

    Windows函数GetKeyState(在“指导书”第六章中介绍过)可以使用虚拟键码VK_LBUTTON、VK_RBUTTON、VK_MBUTTON、VK_SHIFT和VK_CONTROL来传回鼠标按键与Shift键的状态。

    使用小妙招(3):
    检测双击事件需要在创建窗口时指定参数:  wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;

    使用小妙招(4):

    对于非显示区域的鼠标事件、以及下文将提到的“鼠标拦截”,得到的是整个屏幕的图素坐标,要得到其对应的可以直接使用的Client坐标,

    您可以用两个Windows函数将屏幕坐标转换为显示区域坐标或者反之:

    ScreenToClient (hwnd, &pt) ;
            
    ClientToScreen (hwnd, &pt) ;
    
    使用小妙招(5):
    21个鼠标消息中的20个,最后一个消息是WM_NCHITTEST,它代表「非显示区域命中测试」。此消息优先于所有其它的显示区域和非显示区域鼠标消息。lParam参数含有鼠标位置的x和y屏幕坐标,
    wParam参数没有用。

    Windows应用程序通常把这个消息传送给DefWindowProc,然后Windows用WM_NCHITTEST消息产生与鼠标位置相关的所有其它鼠标消息。对于非显示区域鼠标消息,在处理WM_NCHITTEST时,从DefWindowProc传回的值将成为鼠标消息中的wParam参数,这个值可以是任意非显示区域鼠标消息的wParam值再加上以下内容:

    HTCLIENT

    HTNOWHERE

    HTTRANSPARENT

    HTERROR

    显示区域

    不在窗口中

    窗口由另一个窗口覆盖

    使DefWindowProc产生警示用的哔声

    【猜测】【windows 来处理的话,自动处理了坐标转换等等;其返回的HT_... 就可以决定接下来产生的下一个消息:

    “相当”于:

    if(inClient)  SendClientMessage( hwnd,MOUSETYPE(MK_TEST),wParam(Shift...),lParam(PosTranslate));

    else  SendClientMessage( hwnd,MOUSETYPE2(MK_TEST),wParam(HTClient...),lParam(PosOrigon));】
    所以:

    case WM_NCHITTEST:
      return (LRESULT) HTNOWHERE ;

    就可以有效地禁用您窗口中的所有显示区域和非显示区域鼠标消息。

     使用小妙招(6):

    对于一些有需要的应用,可以使用 鼠标拦截

    SetCapture (hwnd) ;
    ReleaseCapture () ;
    /*
    在32位的Windows中,鼠标拦截要比在以前的Windows版本中有多一些限制。特别是,如果鼠标被拦截,而鼠标按键目前并未被按下,并且鼠标光标移到了另一个窗口上,
    那么将不是由拦截鼠标的那个窗口,而是由光标下面的窗口来接收鼠标消息。对于防止一个程序在拦截鼠标之后不释放它而引起整个系统的混乱,这是必要的。
    */
  • 相关阅读:
    【读书笔记《Android游戏编程之从零开始》】20.游戏开发基础(游戏数据存储)
    【Android 我的博客APP】1.抓取博客首页文章列表内容——网页数据抓取
    Android IPC机制(五)用Socket实现跨进程聊天程序
    Android IPC机制(四)用ContentProvider进行进程间通信
    Android IPC机制(三)使用AIDL实现跨进程方法调用
    Android IPC机制(二)用Messenger进行进程间通信
    Android IPC机制(一)开启多进程
    Android性能优化9-ANR完全解析
    性能优化8--内存泄露
    性能优化7--App瘦身
  • 原文地址:https://www.cnblogs.com/BensonLaur/p/5330269.html
Copyright © 2020-2023  润新知