鼠标模拟软件






4.91/5 (14投票s)
一个简单的基于软件的鼠标模拟器
您可以在这里找到本文的俄语版本
如何实现

引言
本项目演示了使用编程钩子技术通过键盘来模拟鼠标。鼠标模拟软件在“真实”鼠标损坏(或不存在)并且您需要执行一些需要鼠标指针设备的操作时非常有用。
背景
有一天,在旧电脑上工作时,我遇到了一个问题……USB端口坏了,并且没有PS/2鼠标。我决定编写一个简单的程序来代替鼠标设备。
Using the Code
“虚拟鼠标”软件的思路如下。 首先,需要选择并分配键以对应鼠标动作。 选择的分配如下
- 向左移动指针(NUM4)
- 向右移动指针(NUM6)
- 向上移动指针(NUM8)
- 向下移动指针(NUM5)
- 鼠标左键单击(NUM7)
- 鼠标右键单击(NUM9)
这些分配的源代码如下所示
// keys to control cursor
#define KeyMoveLeft                VK_NUMPAD4
#define KeyMoveRight               VK_NUMPAD6
#define KeyMoveUP                  VK_NUMPAD8
#define KeyMoveDown                VK_NUMPAD5
 
// keys to emulate mouse buttons
#define KeyClickLeft               VK_NUMPAD7
#define KeyClickRight              VK_NUMPAD9
另一件必要的事情是分配一个按钮(它可以是按键的组合),该按钮将控制“虚拟鼠标”的状态。 如有必要,可以将其打开和关闭。 为此,代码中存在以下定义
// key to switch on/off MouseEmulate software
#define KeyMouseEmulateEnDis       VK_F7
接下来的显而易见的动作是
- 创建用于按键处理的函数
- 从“main”函数启动键盘钩子
什么是键盘钩子? 钩子是一种机制,应用程序可以通过该机制拦截事件,例如消息,鼠标动作和击键。 拦截特定类型事件的函数称为钩子过程。 钩子过程可以作用于其接收的每个事件,然后修改或丢弃该事件[1]。 要启动键盘钩子,必须编写在按下键时将处理的回调函数。 回调函数是
LRESULT CALLBACK KeyboardHook (int nCode, WPARAM wParam, LPARAM lParam)
{
     if (nCode == HC_ACTION)
         if (wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN || 
             wParam == WM_SYSKEYUP || wParam == WM_KEYUP)
            CheckKey (nCode, wParam, lParam);
     return CallNextHookEx(hHook, nCode, wParam, lParam);
}
在此,CheckKey(…)函数正是“键处理器”。 此函数的内容(骨架)如下所示
void CheckKey( int nCode, WPARAM wParam, LPARAM lParam )
{
     …
     // switch on/off the MouseEmulate program
     if (wParam == WM_SYSKEYUP || wParam == WM_KEYUP)
     {                
         if( hookStruct->vkCode == KeyMouseEmulateEnDis )
         {
            bEmulEn = bEmulEn ? false: true;
         }
     }
 
     if(bEmulEn)
     {
         switch ( hookStruct->vkCode )
         {
              case KeyClickLeft:
              {
                // emulating left mouse button click
              }
              break;
              case KeyClickRight:
              {
                // emulating right mouse button click
              }
              break;
              case KeyMoveLeft:
              {
                // move cursor left
              }
              break;
              case KeyMoveUP:
              {
                 // move cursor up
              }
              break;
              case KeyMoveRight:
              {                
                // move cursor right
              }
              break;
              case KeyMoveDown:
              {
                 // move cursor down
              }
              break;
         }
    }
}
可以从“main”函数启动钩子
int APIENTRY WinMain( HINSTANCE hInstance, 
    HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
     hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHook, hInstance , 0);
     while (GetMessage(NULL,NULL,0,0)) ; // NOP while not WM_QUIT
     return UnhookWindowsHookEx(hHook);
}
有关函数SetWindowsHookEx(…)的更多详细信息,请参见[2]。
最好使用确定移动光标的步长值的技术,具体取决于是否长按(或短按)负责移动光标的按钮。 以下函数负责该想法
void StepCalculate()
{
     unsigned short speedMax = 40;
     unsigned short dt = tNow-tBefore;
     float dtMax = 30.0f;
 
     step = (int)(speedMax * dtMax/(tNow-tBefore));
}
在此,speedMax变量是通过长按键动作移动光标时的最大步长(40像素)。 dt值表示两个相同的按键(长按键被视为多次按键)之间的时间差。
技巧
要使此软件每次Windows启动时都运行,只需将“.exe”文件放入“启动”目录中即可。
在文本编辑器中使用此软件时,请按下CTRL按钮以避免输入意外的char值。
关注点
我第一次编写此软件时,将鼠标左键单击模拟为“向左移动光标”-“向右移动光标”键的快速单击组合。 鼠标右键单击也类似地模拟为“向右移动光标”-“向左移动光标”键的单击组合。 这非常好,但是仍然存在一些错误。 很高兴将来实现(或恢复)这样的想法。
参考文献
- http://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx
- http://msdn.microsoft.com/en-us/library/ms644990(v=vs.85).aspx
历史
- 2011年5月10日:初始版本


