将客户端坐标转换为Pixel坐标以在MFC中模拟鼠标单击

时间:2015-09-23 16:25:27

标签: visual-c++ mfc mouseevent sendinput

我正试图在遗留代码中模拟CView窗口上的鼠标点击,我必须说我不完全理解。我们的想法是在CView中搜索特定项目,获取其坐标,然后使用SendInput模拟鼠标右键单击。我想了解我所遵循的基本步骤是否正确,然后再继续深入研究遗留代码,遗留代码在协调系统中发生了一系列转换:(以下是我遵循的步骤:

  1. 获取CView中显示的项目的位置坐标。此时,坐标位于内部坐标系中(我们称之为CDPoint)。

    CDPoint gPosn = viewObj->m_point_a ;

  2. 使用代码中的现有转换将坐标转换为客户端坐标系统,即CDPoint转换为CPoint。

    CPoint newPosn = GetTransform().Scale(gPosn);

  3. //注意:到达这是正确使用的转换的基础是下面的代码,在鼠标单击处理程序代码中发生了精确的反向转换,以将CPoint转换为CDPoint:< / p>

     `CDesignView::OnLButtonDown(UINT nFlags, CPoint p) {
          CDPoint np = GetTransform().DeScale(p);
     }`
    

    这种想法是正确的,在OnLButtonDown()处理程序中收到的CPoint将始终在客户端坐标中,因此反向转换应该将CDPoint(内部坐标)转换为客户端坐标(CPoint)吗?

    1. 将客户坐标转换为屏幕坐标:

      ClientToScreen(&newPosn);

    2. 转换为像素坐标后,将这些值传递给SendInput函数:

      INPUT buffer[1]; MouseSetup(buffer); MouseMoveAbsolute(buffer, newPos.x, newPos.y); MouseClick(buffer);

    3. Mousexxx()函数定义如下,类似于这篇文章中的示例代码: How to simulate a mouse movement

    4. #define SCREEN_WIDTH (::GetSystemMetrics( SM_CXSCREEN )-1) 
      #define SCREEN_HEIGHT (::GetSystemMetrics( SM_CYSCREEN )-1) 
      
      static void inline makeAbsXY(double &x, double &y) { 
          x = (x * 0xFFFF) / SCREEN_WIDTH ; 
          y = (y * 0xFFFF) / SCREEN_HEIGHT ; 
      }
      
      static void inline MouseSetup(INPUT *buffer)
      {
          buffer->type = INPUT_MOUSE;
          buffer->mi.dx = (0 * (0xFFFF / SCREEN_WIDTH));
          buffer->mi.dy = (0 * (0xFFFF / SCREEN_HEIGHT));
          buffer->mi.mouseData = 0;
          buffer->mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
          buffer->mi.time = 0;
          buffer->mi.dwExtraInfo = 0;
      }
      
      static void inline MouseMoveAbsolute(INPUT *buffer, double x, double y)
      {
          makeAbsXY(x,y) ; 
          buffer->mi.dx = x ;
          buffer->mi.dy = y ;
          buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE);
          SendInput(1, buffer, sizeof(INPUT));
      }
      
      static void inline MouseClick(INPUT *buffer)
      {
          buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN);
          SendInput(1, buffer, sizeof(INPUT));
          Sleep(10);
          buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTUP);
          SendInput(1, buffer, sizeof(INPUT));
      }
      

      任何人都可以提供有关这些步骤中可能出错的指示,因为模拟的mosue点击似乎总是向左移动一些因素随着x更大的增加而不断增加。我已经确认gPosn指向(0,0)它总是模拟客户端屏幕右上角的鼠标单击。

      感谢您的时间。

1 个答案:

答案 0 :(得分:0)

如果您在客户端坐标中有xy,则必须将它们转换为屏幕坐标:

POINT point;
point.x = x;
point.y = y;
::ClientToScreen(m_hWnd, point);

其中m_hWnd是拥有对象的窗口。 xy相对于此窗口客户区左上角。

假设point.xpoint.y在屏幕坐标中,SendInput的其余转换是正确的。您还可以为INPUT创建SendInput数组,这将不间断地发送鼠标消息。

INPUT input[3];
for (int i = 0; i < 3; i++)
{
    memset(&input[i], 0, sizeof(INPUT));
    input[i].type = INPUT_MOUSE;
}

input[0].mi.dx = (point.x * 0xFFFF) / (GetSystemMetrics(SM_CXSCREEN) - 1);
input[0].mi.dy = (point.y * 0xFFFF) / (GetSystemMetrics(SM_CYSCREEN) - 1);
input[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
input[1].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
input[2].mi.dwFlags = MOUSEEVENTF_RIGHTUP;

SendInput(3, input, sizeof(INPUT));