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

近日要实现远程控制,需要捕获本地鼠标信息,传输到远程计算机。


鼠标事件,无非是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);
}

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

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值