苏紫方璇 发表于 2018-4-3 17:57

简易鼠标连击修正工具原理与实现

1、起因
近日鼠标的微动开关出问题了,偶尔单击鼠标变成双击,测试了下10次单击有2-3次会变为双击,网上买的鼠标还要两三天的时间才能到,于是就做了个简单的小工具先应付着。本程序原理较为简单,但由于本人水平有限,代码写的较烂,请各位多多指点。
2、原理
利用鼠标钩子获取鼠标按下和弹起的操作,计算两次点击的时间差,如果太快了,那肯定就是微动开关的问题造成的,然后屏蔽掉第二次点击操作就可以了。
3、实现
3.1、主要API介绍
1、设置钩子SetWindowsHookEx
HHOOK SetWindowsHookEx(          int idHook,
    HOOKPROC lpfn,
    HINSTANCE hMod,
    DWORD dwThreadId
);
参数:idHook:消息类型 lpfn:消息处理函数hMod:模块句柄dwThreadId:关联的线程ID
2、取消钩子UnhookWindowsHookEx
BOOL UnhookWindowsHookEx(          HHOOK hhk
);
参数:hhk:SetWindowsHookEx的返回值
3、查询计数器频率QueryPerformanceFrequency
BOOL QueryPerformanceFrequency(          LARGE_INTEGER *lpFrequency
);
参数:lpFrequency:计数器频率
4、查询计数器QueryPerformanceCounter
BOOL QueryPerformanceCounter(          LARGE_INTEGER *lpPerformanceCount
);
参数:lpPerformanceCount:计数器计数
3.2、基本流程
3.2.1主程序流程
程序启动,获取命令行参数,得到设置的延迟时间,启动钩子进行消息处理。
3.2.2消息处理流程
区分是否为第一次按下,第一次按下:记录按下时间A。非第一次按下:将上次按下时间设为A。
记录当前时间B,若B-A大于设置的间隔,则放行。若小于则取消本次按下操作,并设置一个标记,让对应的弹起操作也取消。
弹起操作与之类似。
3.3、代码实现
3.3.1、启动钩子
//启动钩子
BOOL StartHook()
{
      //获取计数器频率
      QueryPerformanceFrequency(&TimeFreq);
      //设置鼠标底层钩子
      hHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)HookProc, NULL, 0);
      if (hHook) return TRUE;
      return FALSE;
}
3.3.2、取消钩子
//卸载钩子
BOOL UnLoadHook()
{
      return(UnhookWindowsHookEx(hHook));
}
3.3.3、消息处理
//消息处理
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
      if (nCode == HC_ACTION)
      {
                int time = 0;
                switch (wParam)
                {
                //左键按下
                case WM_LBUTTONDOWN:
                        //初始化点击时间
                        if (LClkTimeStart.QuadPart == 0)
                              QueryPerformanceCounter(&LClkTimeStart);
                        else
                              LClkTimeStart.QuadPart = LClkTimeNext.QuadPart;
                        //获取本次的按下时间
                        QueryPerformanceCounter(&LClkTimeNext);
                        //计算两次左键按下间隔时间,单位ms
                        time = (((LClkTimeNext.QuadPart - LClkTimeStart.QuadPart) * 1000) / TimeFreq.QuadPart);
                        //若已经设置弹起无效,则恢复。
                        if (LBtnUPCancel)
                        {
                              LBtnUPCancel = FALSE;
                        }
                        //在屏蔽范围内,屏蔽此次按下消息,并设置下次弹起消息无效
                        if (time < BnDelay && time>0)
                        {
                              LBtnUPCancel = TRUE;
                              //输出调试信息
                              if (Debug)
                              {
                                        MyOutputDebugString("LDtime=%d————LDNCancel\n", time);
                              }
                              return TRUE;
                        }
                        else
                        {
                              if (Debug)
                              {
                                        MyOutputDebugString("LDtime=%d\n", time);
                              }
                        }
                        break;
                //左键弹起
                case WM_LBUTTONUP:
                        //初始化点击时间
                        if (LClkTimeStart.QuadPart == 0)
                              QueryPerformanceCounter(&LClkTimeStart);
                        else
                              LClkTimeStart.QuadPart = LClkTimeNext.QuadPart;
                        //获取本次弹起时间
                        QueryPerformanceCounter(&LClkTimeNext);
                        //计算两次间隔
                        time = (((LClkTimeNext.QuadPart - LClkTimeStart.QuadPart) * 1000) / TimeFreq.QuadPart);
                        //若已经设置弹起取消,则取消标记并取消本次弹起
                        if (LBtnUPCancel)
                        {
                              LBtnUPCancel = FALSE;
                              if (Debug)
                              {
                                        MyOutputDebugString("LUtime=%d————LUPCancel\n", time);
                              }
                              return TRUE;
                        }
                        else
                        {
                              if (Debug)
                              {
                                        MyOutputDebugString("LUtime=%d\n", time);
                              }
                        }
                        break;
                default:
                        return(CallNextHookEx(hHook, nCode, wParam, lParam));
                        break;
                }
      }
      return(CallNextHookEx(hHook, nCode, wParam, lParam));
}
3.3.4、主程序
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
      MSG msg;
      BOOL bRet;
      char Cmd = { 0 };
      //设置互斥体,防止多开
      if (CreateMutex(NULL, TRUE, "_FixMouse_") != NULL)
      {
                if (ERROR_ALREADY_EXISTS == GetLastError())
                {
                        return 0;
                }
      }
      //提取命令行
      if (NULL== lpCmdLine)
      {
                MessageBox(NULL, "正确命令行为:40,0   延迟取值范围为1-499", "不正确的取值", MB_OK);
                return 0;
      }
      strcpy_s(Cmd, 20, lpCmdLine);
      char *s,*next;
      s = strtok_s(Cmd, ",",&next);
      if (NULL!=s)
      {
                BnDelay = strtol(s, NULL, 10);
                if (!(BnDelay > 0 && BnDelay < 500))
                {
                        MessageBox(NULL, "正确命令行为:40,0   延迟取值范围为1-499", "不正确的取值", MB_OK);
                        return 0;
                }
      }
      s = strtok_s(next,",",&next);
      if (NULL != s)
      {
                Debug = strtol(s, NULL, 10);
      }
      //启动钩子
      Flag = StartHook();
      //进入消息循环
      while ((bRet = GetMessage(&msg, NULL, 0, 0)) != -1)
      {
                if (bRet == 0)
                {
                        break;
                }

                TranslateMessage(&msg);   
                DispatchMessage(&msg);   
      }
      return msg.wParam;    //WinMain函数结束, 整个程序退出
}

4、添加其他部分
按照上面的代码,便可以做一个简易的鼠标连击修正工具,但是却无法操控,结束程序只能靠任务管理器结束进程。于是我做了一个托盘图标来控制,在这里代码不再赘述,有兴趣可以在附件中查看。



ps:本源码用VS2015编译通过,WIN10 X64 1709版本运行正常

苏紫方璇 发表于 2018-4-4 09:23

天若幽心 发表于 2018-4-3 20:22
win10 64位,尝试添加注册表,开机时软件没有启动。是否是因为开机时一些程序加载不完整导致的?

我也不清楚什么问题,是不是杀软拦截了,按说应该可以开机启动的

苏紫方璇 发表于 2019-5-5 09:42

加油 发表于 2019-5-5 06:37
请教下 ,鼠标钩子做热键、拦截操作(误按)等玩各种游戏会被反外挂关照吗?

各家游戏保护不同,并不排除会被反外挂检测的可能。

憨厚小猪 发表于 2018-4-3 18:06

lnshijia 发表于 2018-4-3 18:07

大神,膜拜

610100 发表于 2018-4-3 18:11

硬件出问题,软件来搞定

小白看不懂,真心来膜拜

莫愁前路无知己 发表于 2018-4-3 18:20

可以的,支持大神

as36601987 发表于 2018-4-3 18:33

为啥子按住不连点

thyonezhy 发表于 2018-4-3 18:42

支持发布原创

gunxsword 发表于 2018-4-3 18:50

呵呵,这个历害了,还是换个鼠标比较好吧!

苏紫方璇 发表于 2018-4-3 19:01

gunxsword 发表于 2018-4-3 18:50
呵呵,这个历害了,还是换个鼠标比较好吧!

是的,但是这不是买的鼠标还没到么

苏紫方璇 发表于 2018-4-3 19:08

610100 发表于 2018-4-3 18:11
硬件出问题,软件来搞定

小白看不懂,真心来膜拜

然而软件也就是暂时解决一下,bug还有很多,还是换硬件比较好
页: [1] 2 3 4 5
查看完整版本: 简易鼠标连击修正工具原理与实现