`

[原]在全局鼠标钩子中模拟鼠标右键单击

阅读更多
[标题]:在全局鼠标钩子中模拟鼠标右键单击
[时间]:2009-3-28
[摘要]:本文分为两部分,第一部分:使用SwapMouseButton()切换鼠标左右键功能。第二部分:在全局鼠标钩子中模拟鼠标右键单击。
[关键字]:鼠标失灵,鼠标右键,切换鼠标左右键,屏幕宽度,屏幕高度,SendInput,全局鼠标钩子,鼠标事件
[平台]:Windows XP SP3 ,VC6
[作者]:Winty (wintys@gmail.com)

[正文]:
    我的鼠标左键失灵了。暂时只能使用鼠标右键。就只能把鼠标右键改成左键用(切换方法:控制面板->鼠标->切换主要和次要的按键)。当然,切换了之后,右键功能就没有了。可是平时用惯了鼠标右键,现在没有可不行。利用程序切换时,代码片段如下:

//bSwap:BOOL型
//为TRUE为切换左右键功能,为FALSE为恢复原左右键功能
::SwapMouseButton(bSwap);

    可以使用RegisterHotKey()注册一个热键,在需要时切换。但是还得知道该传入TRUE还是FALSE到SwapMouseButton(),那么可以使用GetSystemMetrics(SM_SWAPBUTTON)查询鼠标当前左右键功能是否反转了:

//返回TRUE表示鼠标左右键功能反转了,FALSE表示正常
::GetSystemMetrics(SM_SWAPBUTTON);

    最后可以让程序每次自动启动,需要右键的时候按一下热键就可以了,使用完了再切换回来。问题是,这样每次使用时,在左右键之间来回切换很麻烦。
   
    下面在全局鼠标钩子中模拟鼠标右键单击。当按Ctrl+鼠标左键时,相当于点击右键。这里所说的鼠标左键与右键都是用SwapMouseButton(TRUE)切换过的,也就是说,我的真实的鼠标左键已坏,实际上只有鼠标右键可以使用,现在我把鼠标右键当左键用,但又添加右键本身的功能(就是加按Ctrl时相当于原来的右键)。未特殊说明,则下同。

    1、使用SetWindowsHookEx安装钩子。
HHOOK CUtil::InstallMouseHook(HINSTANCE hInstance)
{
    HHOOK hhkMouseHook;
    hhkMouseHook = SetWindowsHookEx(
        WH_MOUSE_LL,                        // hook type
        CUtil::LowLevelMouseProc,        // hook procedure
        hInstance,                                    // handle to application instance
        0                                                // thread identifier:为0表示全局钩子
    );

    if(hhkMouseHook==NULL)
        MessageBox(NULL,"安装MouseHook失败!","InstallMouseHook()",MB_OK);

    return hhkMouseHook;
}

   
    在应用程序的InitInstance()中调用:
//m_hInstance为应用程序实例句柄
HHOOK hhkLowLevelKybd = InstallMouseHook(m_hInstance):

    2、编写全局鼠标钩子回调函数LowLevelMouseProc:
//当按下"Ctrl+鼠标左键" 或 "数字键盘减号键 + 鼠标左键"时,模拟鼠标右键
LRESULT CALLBACK
CUtil::LowLevelMouseProc(int nCode,
                                        WPARAM wParam,
                                        LPARAM lParam)
{
    if(nCode == HC_ACTION){
        //左Ctrl键按下
        BOOL bLeftCtrlDown =
            (GetAsyncKeyState(VK_LCONTROL) & 0x8000) != 0;
        //数字键盘减号"-"键按下
        BOOL bNumpadSubtractDown = 
            (GetAsyncKeyState(VK_SUBTRACT) & 0x8000) != 0;

        //当按下"Ctrl+鼠标左键" 或 "数字键盘减号键+鼠标左键"时,模拟鼠标右键
        if( (wParam == WM_LBUTTONDOWN) &&
            (bLeftCtrlDown || bNumpadSubtractDown) )   
        {
            const INPUT_SIZE = 2;
            INPUT input[INPUT_SIZE];
            ZeroMemory( &input, sizeof(INPUT)*INPUT_SIZE);//初始化INPUT结构体
           
            //鼠标右键按下
            input[0].type = INPUT_MOUSE;
            input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
               
            //鼠标右键弹起
            input[1].type = INPUT_MOUSE;
            input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;

            //发送鼠标右键单击
            SendInput(
                INPUT_SIZE,                // count of input events
                input,                    // array of input events
                sizeof(INPUT)            // size of structure
            );
           
            //发送模拟的鼠标右键单击后,不再响应鼠标左键单击消息
            return 1;   
        }

    }
   
    //如果不是"Ctrl+鼠标左键"按下,则向后传递鼠标消息。
    return CallNextHookEx(NULL,nCode,wParam,lParam);
}

    LowLevelMouseProc()有几点要注意的地方:
        a)、LowLevelMouseProc()应声明为static,因为在一个类中,只有static函数才能作为回调函数。
        b)、wParam == WM_LBUTTONDOWN一句原来写的是WM_LBUTTONUP,结果点击后右键菜单出来了,但无法完成复制。所以应为WM_LBUTTONDOWN就不会有问题了。
        c)、只用使用VK_LCONTROL而不是VK_CONTROL是因为Ctrl+鼠标左键单击在选择多个文件时已被使用。所以把VK_RCONTROL留给选择多个文件等其它用途时使用,而用数字键盘中的减号代替VK_RCONTROL。也就是说,当按下"Ctrl+鼠标左键" 或 "数字键盘减号键+鼠标左键"时,模拟鼠标右键。
        d)、input[i].mi.dwFlags中不用MOUSEEVENTF_ABSOLUTE,代表input[i].mi.dx或input[i].mi.dx中的坐标是相对于上一次鼠标事件时的坐标,而dx和dy都已被初始化为0,即在原地显示鼠标右键菜单;
        e)、也可以在input[i].mi.dwFlags中使用MOUSEEVENTF_ABSOLUTE,此时是绝对坐标,input[i].mi.dx和input[i].mi.dx的计算如下(而不是直接使用当前鼠标位置,具体原因请查看MSDN):

PMSLLHOOKSTRUCT pllh = (PMSLLHOOKSTRUCT)lParam;//鼠标坐标等信息

int cx=GetSystemMetrics(SM_CXSCREEN);//得到屏幕宽度
int cy=GetSystemMetrics(SM_CYSCREEN);//得到屏幕高度

LONG dx = pllh->pt.x * 65535 / cx;
LONG dy = pllh->pt.y * 65535 / cy;

input[0].mi.dx = dx;
input[0].mi.dy = dy;

input[1].mi.dx = dx;
input[1].mi.dy = dy;

        f)、在发送完模拟的鼠标右键事件后,需要return 1;来阻止消息的继续传递,而不是return CallNextHookEx(NULL,nCode,wParam,lParam);,不然会出现不正确的右键行为。
        g)、需要发送鼠标右键按下和弹起两个事件,不然会出现不正确的右键行为。
        h)、WM_LBUTTONDOWN映射的是逻辑鼠标按键,wParam == WM_LBUTTONDOWN表示鼠标左键按下。而MOUSEEVENTF_LEFTDOWN映射的是物理鼠标按键,input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN对应已切换功能的真实鼠标左键,所以实际表示发送的是鼠标右键消息。MOUSEEVENTF_LEFTUP意义相仿。

    3、在应用程序的ExitInstance()中卸载全局鼠标钩子:
UnhookWindowsHookEx(hhkMouseHook);

    这样就完成了所需要的功能。当按下"Ctrl+鼠标左键" 或 "数字键盘减号键 + 鼠标左键"时,相当于按了右键。
分享到:
评论

相关推荐

    IE9双击标签页关闭当前选项卡的工具.docx

    然而,当IE9运行在保护模式下时,全局鼠标钩子无法正常工作。为了解决这个问题,有两种可能的解决方案: - **关闭保护模式**:但这会降低浏览器的安全性,不是一个理想的选择。 - **创建独立的Hook程序**:编写一个...

    鼠标和键盘操作.zip

    对于鼠标操作,同样利用`SetWindowsHookEx`,但这次使用的是`WH_MOUSE_LL`类型的钩子,这样就能捕获鼠标的移动、左键单击、右键单击等事件。当鼠标事件发生时,`Mouse_Event`函数被调用,它会记录鼠标的坐标和按钮...

    Capture Message

    "拦截鼠标点击消息"是通过安装WH_MOUSE钩子实现的,它允许开发者在鼠标事件发生时收到通知,例如鼠标左键单击、右键单击、移动等。通过这种方式,你可以记录或修改这些事件,甚至模拟用户行为。 至于"拦截各种...

    WinMouseInput:WinAPI 的鼠标输入

    通过理解和运用以上知识点,开发者可以在WinAPI环境中实现复杂的鼠标交互功能,无论是简单的响应鼠标点击,还是模拟鼠标动作,或是全局监控鼠标活动,都能得心应手。在实际项目中,结合Windows SDK文档和示例代码,...

    delphi 开发经验技巧宝典源码

    0007 在Delphi中加载QReport报表组件 7 1.3 创建DLL文件 8 0008 生成一个DLL文件 8 0009 调用DLL文件 8 1.4 窗体相关操作 9 0010 将组件置前/置后 9 0011 如何锁定窗体中的组件 9 0012 如何改变窗体...

    delphi 开发经验技巧宝典源码06

    0007 在Delphi中加载QReport报表组件 7 1.3 创建DLL文件 8 0008 生成一个DLL文件 8 0009 调用DLL文件 8 1.4 窗体相关操作 9 0010 将组件置前/置后 9 0011 如何锁定窗体中的组件 9 0012 如何改变窗体...

    易语言 茶凉专用模块

    子程序 窗口禁止, 逻辑型, 公开, 在窗口中允许或禁止所有鼠标及键盘输入(成功返回真,失败返回假) .参数 窗口句柄, 整数型, , 欲禁止鼠标键盘输入的窗口或控件的句柄 .参数 是否禁止, 逻辑型, 可空, 默认为真:禁止 假...

    C#编程经验技巧宝典

    79 <br>0115 如何判断是否为数字 79 <br>0116 如何在字符串中查找指定字符 79 <br>0117 如何在字符串中用一子串替换另一子串 80 <br>0118 将新字符串添加到已有字符串中 80 <br>0119 如何在...

    1345个易语言模块

    全局事件模块.ec 全 程API模块.ec 全面操作SQL Server 2000.ec 公农历转换.ec 公农历转换1.2.ec 公农历转换 1.ec 公农历转换35.ec 公农历转换_2.ec 公历转农历模块 1.0.ec 公历转换为农历.ec 六六自用 模块.ec 六十...

    1350多个精品易语言模块

    全局事件模块.ec 全 程API模块.ec 全面操作SQL Server 2000.ec 公农历转换.ec 公农历转换1.2.ec 公农历转换 1.ec 公农历转换35.ec 公农历转换_2.ec 公历转农历模块 1.0.ec 公历转换为农历.ec 六六自用 模块.ec 六十...

Global site tag (gtag.js) - Google Analytics