VC6捕获鼠标事件(移动、单击等)的一些总结(MFC消息、DriectInput、钩子)

来源:互联网 发布:考研英语单词书 知乎 编辑:程序博客网 时间:2024/05/14 05:04
鼠标事件,无非是WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOVE(就说这基本的三个命令吧),开始以为很容易获取这些事件,但在实现过程中,并不是想象中的那么简单:


① 在基于MFC中的对话框应用程序中,可以在 PreTranslateMessage 中获取(【主对话框】的或者是【CWinApp】的,应该说放在【CWinApp】中的PreTranslateMessage更好一些),如同下面:

BOOL CTestApp::PreTranslateMessage(MSG* pMsg) {if (pMsg->message == WM_LBUTTONDOWN)//左键按下{if(m_bSend){CPoint pt;GetCursorPos(&pt);memset(szMsg,'\0',sizeof(szMsg));sprintf(szMsg,"WM_LBD;%d;%d;%d;0;\0",pt.x, pt.y, keyFlags);LanSend(szMsg, strlen(szMsg));}}else if (pMsg->message == WM_LBUTTONUP)//左键抬起{if(m_bSend){CPoint pt;GetCursorPos(&pt);memset(szMsg,'\0',sizeof(szMsg));sprintf(szMsg,"WM_LBU;%d;%d;%d;0;\0",pt.x, pt.y, keyFlags);LanSend(szMsg, strlen(szMsg));}}else if (pMsg->message == WM_MOUSEMOVE)//光标移动{if(m_bSend){POINT pt;::GetCursorPos(&pt);memset(szMsg,'\0',sizeof(szMsg));//sprintf(szMsg,"WM_MM;%d;%d;%d;0;\0", GET_X_LPARAM(pMsg->lParam), GET_Y_LPARAM(pMsg->lParam), 0);sprintf(szMsg,"WM_MM;%d;%d;%d;0;\0", pt.x, pt.y, 0);LanSend(szMsg, strlen(szMsg));}}else if (pMsg->message == WM_KEYDOWN)//键盘按下{if(m_bSend){memset(szMsg,'\0',sizeof(szMsg));sprintf(szMsg,"WM_KD;%d;%d;%d;%d;\0",pMsg->wParam, 0, 0, 0);LanSend(szMsg, strlen(szMsg));}}else if (pMsg->message == WM_KEYUP)//键盘抬起{if(m_bSend){memset(szMsg,'\0',sizeof(szMsg));sprintf(szMsg,"WM_KU;%d;%d;%d;%d;\0",pMsg->wParam, 0, 0, 0);LanSend(szMsg, strlen(szMsg));}}return CDialog::PreTranslateMessage(pMsg);}


上面的确实现了三个基本命令,但存在这样一个问题:即鼠标的当前位置离开这个应用程序时(对话框界面),这些鼠标数据就无法捕获。
为什么会有这种应用呢?比如在应用程序中点击某个按钮,打开了一个CMD命令行控制台窗口(验证与对方能否Ping通),弹出的CMD窗口上就无法捕获这些鼠标数据;
上面说的为什么在“在【CWinApp】中的PreTranslateMessage更好一些”?是说如果要编写的应用程序假若有多个Dialog,岂不是要给每一个窗口写一个PreTranslateMessage!


② 怎么在“整个系统”中获取鼠标?想到了DriectX中的DriectInput,和鼠标钩子。先说DriectInput,下面是在定时器或者线程中获取鼠标数据的一段:(初始化部分就不粘贴了)


HRESULT ReadImmediateData( HWND hDlg ){    HRESULT       hr;    TCHAR         strNewText[128] = TEXT("");   // Output string    DIMOUSESTATE2 dims2;      // DirectInput mouse state structure    if( NULL == g_pMouse )         return S_OK;        // Get the input's device state, and put the state in dims    ZeroMemory( &dims2, sizeof(dims2) );    hr = g_pMouse->GetDeviceState( sizeof(DIMOUSESTATE2), &dims2 );    if( FAILED(hr) )     {        // DirectInput may be telling us that the input stream has been        // interrupted.  We aren't tracking any state between polls, so        // we don't have any special reset that needs to be done.        // We just re-acquire and try again.                // If input is lost then acquire and keep trying         hr = g_pMouse->Acquire();        while( hr == DIERR_INPUTLOST )             hr = g_pMouse->Acquire();        // Update the dialog text         if( hr == DIERR_OTHERAPPHASPRIO ||             hr == DIERR_NOTACQUIRED )             SetDlgItemText( hDlg, IDC_DATA, TEXT("Unacquired") );        // hr may be DIERR_OTHERAPPHASPRIO or other errors.  This        // may occur when the app is minimized or in the process of         // switching, so just try again later         return S_OK;     }        // The dims structure now has the state of the mouse, so     // display mouse coordinates (x, y, z) and buttons.    StringCchPrintf( strNewText, 128, TEXT("(X=% 3.3d, Y=% 3.3d, Z=% 3.3d) B0=%c B1=%c B2=%c B3=%c B4=%c B5=%c B6=%c B7=%c"),dims2.lX, dims2.lY, dims2.lZ,                        (dims2.rgbButtons[0] & 0x80) ? '1' : '0',                        (dims2.rgbButtons[1] & 0x80) ? '1' : '0',                        (dims2.rgbButtons[2] & 0x80) ? '1' : '0',                        (dims2.rgbButtons[3] & 0x80) ? '1' : '0',                        (dims2.rgbButtons[4] & 0x80) ? '1' : '0',                        (dims2.rgbButtons[5] & 0x80) ? '1' : '0',                        (dims2.rgbButtons[6] & 0x80) ? '1' : '0',                        (dims2.rgbButtons[7] & 0x80) ? '1' : '0');    // Get the old text in the text box    TCHAR strOldText[128];    GetDlgItemText( hDlg, IDC_DATA, strOldText, 127 );        // If nothing changed then don't repaint - avoid flicker    if( 0 != lstrcmp( strOldText, strNewText ) )         SetDlgItemText( hDlg, IDC_DATA, strNewText );//// 仍然使用::GetCursorPos 发送鼠标的绝对位置//char szMsg[100] = {0};POINT pt;::GetCursorPos(&pt);sprintf(szMsg,"WM_MM;%d;%d;%d;0;\0", pt.x, pt.y, 0);LanSend(szMsg, strlen(szMsg));//// 发送鼠标的【按下】和【抬起】事件//因为driectInput里无法识别鼠标抬起事件,这里只能模拟抬起//if (dims2.rgbButtons[0] & 0x80){GetCursorPos(&pt);sprintf(szMsg,"WM_LBD;%d;%d;%d;0;\0",pt.x, pt.y, 0);LanSend(szMsg, strlen(szMsg));Sleep(2);sprintf(szMsg,"WM_LBU;%d;%d;%d;0;\0",pt.x, pt.y, 0);LanSend(szMsg, strlen(szMsg));}        return S_OK;}

用DirectInput有几个局限,一是如上程序看到,鼠标移动中给出的是“相对位置”,而不是“绝对位置”(两者如何在DirectInput中转换,我没有试出来),不得不还是使用Win32中的::GetCursorPos;二是只能判断鼠标“点击按下”,无法识别鼠标按键“抬起”,如上程序,自己模拟了在点击后延时2ms后发生“抬起”事件,但这样并不是用户在操作中的真正行为;
③ 继续说鼠标低级钩子:用的是WH_MOUSE_LL,对应的挂接函数为LowLevelMouseProc,好处是不用单独写一个DLL库,直接在应用程序中使用即可;


//// 全局变量和全局函数定义//HHOOK hhookMs = NULL;LRESULT CALLBACK LowLevelMouseProc (INT nCode, WPARAM wParam, LPARAM lParam);BOOL UninstallKbHook();BOOL InstallKbHook();//// 安装鼠标Hook//void CTestMFCDlg::OnButton1() {InstallKbHook();}//// 卸掉键盘Hook//void CTestMFCDlg::OnButton2() {UninstallKbHook();}LRESULT CALLBACK LowLevelMouseProc (INT nCode, WPARAM wParam, LPARAM lParam){    MSLLHOOKSTRUCT *pkbhs = (MSLLHOOKSTRUCT *)lParam;char strMsg[100] = {0};    switch (nCode)    {case HC_ACTION:        {//鼠标移动if (wParam == WM_MOUSEMOVE) {sprintf(strMsg, "WM_MOUSEMOVE: x= %d, y= %d\n", pkbhs->pt.x, pkbhs->pt.y);OutputDebugString(strMsg);}//鼠标左击if(wParam == WM_LBUTTONDOWN){sprintf(strMsg, "WM_LBUTTONDOWN: x= %d, y= %d\n", pkbhs->pt.x, pkbhs->pt.y);OutputDebugString(strMsg);}// //滚轮事件// if (wParam == WM_MOUSEWHEEL)// {// sprintf(strMsg, "WM_MOUSEWHEEL: %d\n", HIWORD(pkbhs->mouseData));// OutputDebugString(strMsg);// }        }default:break;    }    return CallNextHookEx (NULL, nCode, wParam, lParam);}BOOL InstallKbHook( ){    if (hhookMs )        UninstallKbHook();    hhookMs = SetWindowsHookEx(WH_MOUSE_LL,         (HOOKPROC)LowLevelMouseProc, AfxGetApp()->m_hInstance, NULL);    return(hhookMs != NULL);}BOOL UninstallKbHook(){    BOOL fOk = FALSE;    if (hhookMs ) {        fOk = UnhookWindowsHookEx(hhookMs );        hhookMs = NULL;    }    return(fOk);}


鼠标低级钩子是一个全局的,只要安装钩子成功,在整个系统中都是有效的,基本解决了这个问题。
综上,决定使用鼠标低级钩子。








0 0
原创粉丝点击